Java IO源码分析(二)——ByteArrayInputStream 和 ByteArrayOutputStream
简介
ByteArrayInputStream 是字节数组输入流,它继承于InputStream。
它的内部数据存储结构就是字节数组。
ByteArrayOutputStream是字节数组输出流,它继承于OutputStream。
它的内部数据存储结构也是字节数组。
源码分析
InputStream
在分析ByteArrayInputStream之前,应该先看InputStream,父类InputStream是ByteArrayInputStream的父类,主要实现了读取和跳跃的方法。
public abstract class InputStream implements Closeable {
// 最大可跳过的字节数
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
// 向后读取一个字节
public abstract int read() throws IOException;
// 将字节流中的数据装到字节数组的0位开始的位置
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
// 将字节流中的数据装到字节数组的指定位置当中
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
// 跳过输入流中的n个字节
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
// 是否还有
public int available() throws IOException {
return 0;
}
// 关闭流
public void close() throws IOException {}
// 标记
public synchronized void mark(int readlimit) {}
// 重置
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
// 是否支持标记方法
public boolean markSupported() {
return false;
}
}
ByteArrayInputStream
public
class ByteArrayInputStream extends InputStream {
// 字节数组,存储数据
protected byte buf[];
// 记录当前可读的第一个位置
protected int pos;
// 标记的位置
protected int mark = 0;
// 数据最大的可读长度
protected int count;
// 初始化字节流数组
public ByteArrayInputStream(byte buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
// 初始化字节流,填入字节数组的指定位置
public ByteArrayInputStream(byte buf[], int offset, int length) {
this.buf = buf;
this.pos = offset;
// 设置最大可读长度,数组的长度比设置的长度还短,说明传入的长度有问题,就设置为数组的长度
this.count = Math.min(offset + length, buf.length);
this.mark = offset;
}
// 读取单个字节
public synchronized int read() {
// 这里& 0xff的操作是为了只取低八位
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
// 读取数据到数组的指定位置
public synchronized int read(byte b[], int off, int len) {
// 边界判断
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
// 读完了
if (pos >= count) {
return -1;
}
// 剩余长度
int avail = count - pos;
// 如果要求读的长度大于剩余长度,就设置读的长度为剩余长度
if (len > avail) {
len = avail;
}
if (len <= 0) {
return 0;
}
// 底层采用的
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
// 跳过指定长度的字节
public synchronized long skip(long n) {
long k = count - pos;
if (n < k) {
// 如果是负数那么就不动
// 取n和k最小的一个
k = n < 0 ? 0 : n;
}
// 移动k位
pos += k;
return k;
}
// 是否还有数据可以读
public synchronized int available() {
return count - pos;
}
// 是否支持标记功能
public boolean markSupported() {
return true;
}
// 标记当前位置,这个传入参数是个摆设
public void mark(int readAheadLimit) {
mark = pos;
}
// 重置,也就是将当前指针指向之前mark的位置
public synchronized void reset() {
pos = mark;
}
// 关闭字节流
public void close() throws IOException {
}
}
OutputStream
OutputStream是ByteArrayOutputStream的父类,先看看它的源码。
很短,实现了Closeable, Flushable。
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
public void flush() throws IOException {
}
public void close() throws IOException {
}
}
ByteArrayOutputStream
public class ByteArrayOutputStream extends OutputStream {
// 存储数据的字节数组
protected byte buf[];
// 数组长度
protected int count;
// 默认构造,默认大小是32
public ByteArrayOutputStream() {
this(32);
}
// 初始化长度的构造
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
// 初始化一个数组对象
buf = new byte[size];
}
// 查看是否需要扩容
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity - buf.length > 0)
grow(minCapacity);
}
// 数组的最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = buf.length;
// 默认先扩容两倍
int newCapacity = oldCapacity << 1;
// 如果还是不够就将容量扩到需求的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 边界判断
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将旧数组复制到新数组
buf = Arrays.copyOf(buf, newCapacity);
}
// 边界判断
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
// 写入单个字节
public synchronized void write(int b) {
// 先判断是否需要扩容
ensureCapacity(count + 1);
// 写入字节
buf[count] = (byte) b;
// 增加可用长度
count += 1;
}
// 写入字节数组
public synchronized void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) - b.length > 0)) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(count + len);
// 将数组整个复制过去
System.arraycopy(b, off, buf, count, len);
count += len;
}
// 将当前Stream输出到指定的Streamz中
public synchronized void writeTo(OutputStream out) throws IOException {
// 直接将可用长度的数组写入
out.write(buf, 0, count);
}
// 重置
public synchronized void reset() {
count = 0;
}
// 转化为字符数组
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
// 获取可用长度
public synchronized int size() {
return count;
}
// 转化为字符串
public synchronized String toString() {
return new String(buf, 0, count);
}
// 根据指定编码转化为字符串
public synchronized String toString(String charsetName)
throws UnsupportedEncodingException
{
return new String(buf, 0, count, charsetName);
}
@Deprecated
// 获取高位的字节
public synchronized String toString(int hibyte) {
return new String(buf, hibyte, 0, count);
}
public void close() throws IOException {
}
}
总结
输入输出流的本质就是一个中间缓存器,暂时将数据放在中间的缓存区,然后根据指定要求输出或如输入。
ByteArrayInputStream 特点
- 数组实现中间缓存;
- 修改和读取操作都是线程安全了,因为加了synchronized;
- 具有标记回读的功能,就是可以先读后面的数据,然后经过重置,再去读前面标记位置的数据。
ByteArrayOutputStream特点
- 数组实现中间缓存;
- 修改读取也是具有线程安全的;
- 具有扩容功能,应为想要写入的数据是增长的,在写入之前,就会进行依次扩容判断;
- 默认的初始大小是32,如果一个个数据写入的扩容,每次是扩一倍的大小;
- 可以写入到其他的输出流上。
Java IO源码分析(二)——ByteArrayInputStream 和 ByteArrayOutputStream的更多相关文章
- Java IO源码分析(三)——PipedOutputStream和PipedInputStream
简介 PipedOutputStream和PipedInputStream主要用于线程之间的通信 .二者必须配合使用,也就是一段写入,另一端接收.本质上也是一个中间缓存区,讲数据缓存在PipedInp ...
- 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>
目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...
- java io 源码研究记录(一)
Java IO 源码研究: 一.输入流 1 基类 InputStream 简介: 这是Java中所有输入流的基类,它是一个抽象类,下面我们简单来了解一下它的基本方法和抽象方法. 基本方法: publ ...
- 框架-springmvc源码分析(二)
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
- java集合源码分析(三):ArrayList
概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...
- java集合源码分析(六):HashMap
概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...
- Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题
4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...
- Java Reference 源码分析
@(Java)[Reference] Java Reference 源码分析 Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使 ...
- Java 集合源码分析(一)HashMap
目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...
随机推荐
- c++实现split
#include<iostream> #include<vector> #include<algorithm> #include<string> usi ...
- Java基础 之一 基本知识
Java基础 之一 基本知识 1.数据类型 Java有8种基本数据类型 int.short .long.byte.float.double.char.boolean 先说明以下单位之间的关系 1位 = ...
- linux绑定盘符
[root@centos6 ~]# udevadm info -q path -n /dev/sdb [root@centos6 ~]# udevadm info -q path -n /dev/sd ...
- Python_异常处理、调试
1.try except 机制 # 错误处理 # 一般程序都要用到错误捕获,当没有加且有错误的时候Python解释器会执行错误捕获,且是一层层向上捕获[所以问题点会在最下面] try: print(' ...
- [LeetCode题解]142. 环形链表 II | 快慢指针
解题思路 本题是在141. 环形链表基础上的拓展,如果存在环,要找出环的入口. 如何判断是否存在环,我们知道通过快慢指针,如果相遇就表示有环.那么如何找到入口呢? 如下图所示的链表: 当 fast 与 ...
- My SQL的基本操作(总结)
My SQL的基本操作(总结) 因为本人目前是学生,前一段时间因为一些原因没有按时更新博客,今天我来总结一下My SQL的基本操作. 一.下载与安装 windows版本MySQL下载地址: http: ...
- java的常用定时任务的几种方式
Java基本的定时任务,一般有这几种方式:一.Timer 1 public class Timer{ 2 static int index=0; 3 public static void main(S ...
- 花了三天整理,Spring Cloud微服务如何设计异常处理机制?还看不懂算我输
前言 首先说一下为什么发这篇文章,是这样的.之前和粉丝聊天的时候有聊到在采用Spring Cloud进行微服务架构设计时,微服务之间调用时异常处理机制应该如何设计的问题.我们知道在进行微服务架构设计时 ...
- 通过Camtasia来添加各种各样的光标效果
在十几二十年前的时候,我们想要学习新的知识需要到学校和培训班才行,但是现在只要有一台电脑.一部手机或者平板,我们在家里也能找到我们喜欢的课程来学习了,微课也因此而生. 同样的,有了想要学习知识的学生, ...
- jquery删除文件
1 <div class="panel panel-default"> 2 <div class="panel-body"> 3 < ...