文章目录
- 前言
- 一、字节数组流介绍
- 1.字节数组流作用
- 2.ByteArrayInputStream介绍
- 3.ByteArrayOutputStream介绍
- 二、构造方法
- 1.ByteArrayInputStream构造
- 2.ByteArrayOutputStream构造
- 三、read()方法
- 四、write()方法
- 1.write()方法
- 2.扩容机制
- 5.字节数组流与缓冲流比较
- 1.构造方法区别
- 2.BufferedInputStream与ByteArrayInputStream
- 3.BufferedOutputStream与ByteArrayOutputStream
- 总结
前言
前几篇文章我们都是讲的如何读写文件数据,但其实有时候我们不需要读取文件,可能几个简单的字符串就能满足我们的读写需求,这里我们就引入了字节数组流
一、字节数组流介绍
1.字节数组流作用
对于要创建临时性文件的程序以及网络数据的传输、数据压缩后的传输等可以提高运行的的效率,可以不用访问磁盘。
流的来源或目的地并不一定是文件,也可以是内存中的一块空间,例如一个字节数组。java.io.ByteArrayInputStream、java.io.ByteArrayOutputStream就是将字节数组当作流输入来源、输出目的地的类。
2.ByteArrayInputStream介绍
ByteArrayInputStream 是字节数组输入流。它继承于InputStream。
public class ByteArrayInputStream extends InputStream
内部存储一个字节数组,或者说,他有一个缓冲区,缓冲区是一个字节数组
/**
* An array of bytes that was provided
* by the creator of the stream. Elements {@code buf[0]}
* through {@code buf[count-1]} are the
* only bytes that can ever be read from the
* stream; element {@code buf[pos]} is
* the next byte to be read.
*/
protected byte buf[];
这个内部实现和BufferedInputStream一样
3.ByteArrayOutputStream介绍
ByteArrayOutputStream 是字节数组输出流。它继承于OutputStream。
public class ByteArrayOutputStream extends OutputStream
内部同样存储一个 byte 数组,数组会随着数据的不断写入而自动增长。 可使用 toByteArray() 和 toString() 获取数据。
/**
* The buffer where data is stored.
*/
protected byte buf[];
二、构造方法
1.ByteArrayInputStream构造
/**
* Creates a {@code ByteArrayInputStream}
* so that it uses {@code buf} as its
* buffer array.
* The buffer array is not copied.
* The initial value of {@code pos}
* is {@code 0} and the initial value
* of {@code count} is the length of
* {@code buf}.
*
* @param buf the input buffer.
*/
public ByteArrayInputStream(byte buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
/**
* Creates {@code ByteArrayInputStream}
* that uses {@code buf} as its
* buffer array. The initial value of {@code pos}
* is {@code offset} and the initial value
* of {@code count} is the minimum of {@code offset+length}
* and {@code buf.length}.
* The buffer array is not copied. The buffer's mark is
* set to the specified offset.
*
* @param buf the input buffer.
* @param offset the offset in the buffer of the first byte to read.
* @param length the maximum number of bytes to read from the buffer.
*/
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;
}
和BufferedInputStream不同的是,ByteArrayInputStream不需要套接一个流实现读取,他要读取的数据来自一个数组,所以构造方法里要接入一个字符数组
2.ByteArrayOutputStream构造
/**
* Creates a new {@code ByteArrayOutputStream}. The buffer capacity is
* initially 32 bytes, though its size increases if necessary.
*/
public ByteArrayOutputStream() {
this(32);
}
/**
* Creates a new {@code ByteArrayOutputStream}, with a buffer capacity of
* the specified size, in bytes.
*
* @param size the initial size.
* @throws IllegalArgumentException if size is negative.
*/
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
构造ByteArrayOutputStream的过程其实就是构造一个字节数组
之后就是对这个数组写入(赋值)
三、read()方法
read是要把buf的数据读出去,可以把这里的buf看作一个外部文件,我们要从这个外部文件里读进来数据
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an {@code int} in the range
* {@code 0} to {@code 255}. If no byte is available
* because the end of the stream has been reached, the value
* {@code -1} is returned.
* <p>
* This {@code read} method
* cannot block.
*
* @return the next byte of data, or {@code -1} if the end of the
* stream has been reached.
*/
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
/**
- Reads up to {@code len} bytes of data into an array of bytes from this
- input stream. If {@code pos} equals {@code count}, then {@code -1} is
- returned to indicate end of file. Otherwise, the number {@code k} of
- bytes read is equal to the smaller of {@code len} and {@code count-pos}.
- If {@code k} is positive, then bytes {@code buf[pos]} through
- {@code buf[pos+k-1]} are copied into {@code b[off]} through
- {@code b[off+k-1]} in the manner performed by {@code System.arraycopy}.
- The value {@code k} is added into {@code pos} and {@code k} is returned.
- <p>
- This {@code read} method cannot block.
- 12. @param b the buffer into which the data is read.
- @param off the start offset in the destination array {@code b}
- @param len the maximum number of bytes read.
- @return the total number of bytes read into the buffer, or
- {@code -1} if there is no more data because the end of
- the stream has been reached.
- @throws NullPointerException If {@code b} is {@code null}.
- @throws IndexOutOfBoundsException If {@code off} is negative,
- {@code len} is negative, or {@code len} is greater than
- {@code b.length - off}
*/
public synchronized int read(byte b[], int off, int len) {
Objects.checkFromIndexSize(off, len, b.length);
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;
}
read两种调用方法
- read():如果当前读取到的位置小于buf里的总有效字节长度,返回buf[pos] & 0xff;否则返回-1
- read(byte b[], int off, int len):把数据读进b[]里,从b[]的off开始填入len个字节。如果没有剩余的可读数据,返回-1;如果剩余数据量avail不满足需求的len,返回avail
四、write()方法
1.write()方法
write是要把数据写进buf里,如果把这里的buf看作一个外部文件,我们就是要把数据写进这个文件里
/**
* Writes the specified byte to this {@code ByteArrayOutputStream}.
*
* @param b the byte to be written.
*/
public synchronized void write(int b) {
ensureCapacity(count + 1);
buf[count] = (byte) b;
count += 1;
}
/**
* Writes {@code len} bytes from the specified byte array
* starting at offset {@code off} to this {@code ByteArrayOutputStream}.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
* @throws NullPointerException if {@code b} is {@code null}.
* @throws IndexOutOfBoundsException if {@code off} is negative,
* {@code len} is negative, or {@code len} is greater than
* {@code b.length - off}
*/
public synchronized void write(byte b[], int off, int len) {
Objects.checkFromIndexSize(off, len, b.length);
ensureCapacity(count + len);
System.arraycopy(b, off, buf, count, len);
count += len;
}
不难看出,write支持两种输入,写入机制与BufferedOutputStream大体类似,这里就不阐述了。不同的地方是,当BufferedOutputStream里的缓冲区满了的时候,它会调用flushBuffer()输出并清空;而ByteArrayOutputStream的缓冲区满了的时候,他会进行一种扩容的操作,直到全部写入为止,下面讲这种扩容机制。
2.扩容机制
每次write的时候,会调用ensureCapacity(int minCapacity),检验当前缓冲区内存是否充足
/**
* Increases the capacity if necessary to ensure that it can hold
* at least the number of elements specified by the minimum
* capacity argument.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if {@code minCapacity < 0}. This is
* interpreted as a request for the unsatisfiably large capacity
* {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
*/
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity - buf.length > 0)
grow(minCapacity);
}
当最小需求容量大于buf长度时,调用grow函数
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
可以看出grow先考虑扩容一倍,如果newCapacity仍然小于最小需求量,newCapacity = minCapacity
,如果newCapacity 大于 MAX_ARRAY_SIZE(Integer.MAX_VALUE-8),调用hugeCapacity检查需求量是否越栈,如果越栈,抛出异常,如果没有越栈,返回Integer.MAX_VALUE,即newCapacity = Integer.MAX_VALUE
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
5.字节数组流与缓冲流比较
字节数组流与缓冲流的源码较为相似,这里分析一下区别
1.构造方法区别
- 缓冲流需要套接原始流
- 字节数组流不需要套接一个原始流,构造的时候只需要一个字节数组
2.BufferedInputStream与ByteArrayInputStream
- BufferedInputStream读取时,因为套接了一个输入流,所以缓冲区读完还可以重新从磁盘fill
- ByteArrayInputStream不依赖磁盘,他的数据只存在于缓冲区buf里,所以缓冲区读完直接返回-1
3.BufferedOutputStream与ByteArrayOutputStream
- BufferedOutputStream会创建一个默认的容器量,capacity = 8192 = 8KB,每次在写之前都会检查buf中是否还有空间可以写入,如果没有就flushBuffer(),缓冲区数据写入outputStream中,清空缓冲区,再继续写入内容,期间不存在扩容操作
- ByteArrayOutputStream也会创建一个默认的容器量, capacity = 32 = 32b, 每次在写之前都会检查一遍buf剩余空间是否够用, 如果不够用, 就进行扩容操作,一直等到内容写完, 这些数据都会一直处于内存中
当你资源不足够用时,选择BufferedOutputStream是最佳的选择, 当你选择快速完成一个作业时,可以选择ByteArrayOutputStream之类的输出流
总结
字节数组流就总结到这了,下一个会写数据流,将各种基本数据类型与字节流相连通