介绍
1. 输出流:ByteArrayOutputStream
上一篇简单介绍了输出流的超类OutputStream,也大概的讲述了输出流的作用,本篇就介绍一下,输出流的一种实现,字节数组输出流,该输出流是为了处理字节的基础流,本质上就是写入数据到类中的缓冲字节数组中;
2. ByteArrayOutputStream 源代码介绍
(1)属性内容:属性内容相较输入流的会有存放数据的缓冲区,也就是字节数组;
count:写操作的时候的计数,也可以算作数据的大小
// 存储数据的缓冲区(字节数组)。
protected byte buf[];
//缓冲区中的有效字节数(写入的内容大小,也可以看做是位置)
protected int count;
(2)构造函数:默认的构造函数会初始化缓冲区大小为32个字节,即缓冲数组的大小为32;
//默认构造函数
public ByteArrayOutputStream() {
this(32);
}
//指定的缓冲区大小
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
(3)主要方法:输出流的主要方法当然就是写,ByteArrayOutputStream中有三种写方法,第一是写入一个int 数据以字节的形式存到缓冲数组中,第二个就是写入指定的字节数组,可以指定从该数组的哪个位置开始写,和写多少;最后一个是写到指定的输出流中,相当于继续流,
// 将指定的字节写入此 byte 数组输出流。
public synchronized void write(int b) {
ensureCapacity(count + 1);
buf[count] = (byte) b;
count += 1;
}
//将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此 byte 数组输出流。
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;
}
//将此 byte 数组输出流的全部内容写入到指定的输出流参数中
public synchronized void writeTo(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
扩容方法:
在上面的写方法中我们可以看到其中还有一个中间处理的方法,也就是扩容的方法,在数据写入的过程中,每次会判断当前的缓冲区容量是否够写入,如果不够就两倍扩容;
//判断是否需要扩容
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity - buf.length > 0)
grow(minCapacity);
}
//进行扩容操作
private void grow(int minCapacity) {
int oldCapacity = buf.length;
int newCapacity = oldCapacity << 1;
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity < 0) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
buf = Arrays.copyOf(buf, newCapacity);
}
(4)其他方法介绍
reset():重置方法仅仅是将count值设置为0,这样写数据的时候就可以重新从0开始写,此时缓冲区的数据其实还存在;
public synchronized void reset() {
count = 0;
}
size():返回当前写入了多少数据,其实就是count的值;
public synchronized int size() {
return count;
}
toByteArray():以字节数组的形式返回当前缓冲数组中的数据,当然位置是从0,到count,所以reset操作之后即使缓冲数组中的数据很多,但只显示到count;
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
还有toString()的方法:字符串显示,类似toByteArray();
3. 附上自己重写的代码
只实现核心的方法功能,没有继承其他类;
//字节数组输出流
public class MyByteArrayOutputStream {
//缓冲的字节数组
protected byte[] buffer;
//输出流的内容大小,写入的大小
protected int counts;
//默认构造初始化时定义32字节大小的缓冲数组空间
public MyByteArrayOutputStream() {
this(32);
}
//创建指定大小的缓冲数组空间
public MyByteArrayOutputStream(int size) {
if(size<0){
throw new IllegalArgumentException("size 值必须大于0"+size);
}
buffer = new byte[size];
}
//输出流写入一个int b :即将数据写到缓冲数组中
public synchronized void write(int b){
//每次写数据前判断当前缓冲数组空间是否够写入,不够则进行扩容
ensureCapacity(counts+1);
buffer[counts] = (byte)b;
//统计写入的大小
counts+=1;
}
//从指定的字节数组向输出流中写数据,可以指定数组中的啥位置开始写入,和写多少
public synchronized void write(byte[] b,int offset,int length){
if(b==null){
throw new NullPointerException();
}else if((offset < 0) ||(b.length<offset)|| length < 0 || (b.length<length+offset)){
throw new IndexOutOfBoundsException("写入的大小不正常,越界了");
}
//每次写数据前判断当前缓冲数组空间是否够写入,不够则进行扩容
ensureCapacity(counts+length);
//将指定的数据写入到缓冲数组中
System.arraycopy(b, offset, buffer, counts, length);
//统计写入的大小
counts+=length;
}
//将缓冲数组输出流中的数据写入指定的输出流中
public synchronized void writeTo(OutputStream out) throws IOException{
//类似回调方法,写入指定输出流中
out.write(buffer,0,counts);
}
//返回字节数组:即将当前缓冲数组中的数据以字节数组的形式返回
public synchronized byte[] toByteArray(){
return Arrays.copyOf(buffer, counts);
}
//返回字符串:即将当前缓冲数组中的数据以字符串的形式返回
public synchronized String toString(){
return new String(buffer,0,counts);
}
//返回字符串:即将当前缓冲数组中的数据以字符串的形式返回,指定了字符集
public synchronized String toString(String charsetName) throws UnsupportedEncodingException{
return new String(buffer,0,counts,charsetName);
}
//返回当前输出流的大小,即缓冲数组中写入的数据
public synchronized int size(){
return counts;
}
//判断当前是否需要扩容:即比较当前的容量是否大于新增的数量,不够则进行扩容
private void ensureCapacity(int capacity){
if(capacity > buffer.length){
growCapacity(capacity);
}
}
//扩容方法:两倍当前容量进行扩容,如果两倍容量还小于需要的容量,则使用需要的容量,当然最大不能超过数组的最大容量
private void growCapacity(int capacity){
int oldCapacity = buffer.length;
int newCapacity = oldCapacity << 1;
if(newCapacity < capacity){
newCapacity = capacity;
}
if(newCapacity < 0){
if(capacity<0){
throw new IndexOutOfBoundsException("增加的容量不正常");
}
newCapacity=Integer.MAX_VALUE;
}
buffer = Arrays.copyOf(buffer, newCapacity);
}
//重置:将写入的位置设置为0,这样就可以从0位置写数据,重置后缓冲数组中可能存在之前的数据
public synchronized void reset(){
counts = 0;
}
}
4.最后召唤神兽
/**
*
* __----~~~~~~~~~~~------___
* . . ~~//====...... __--~ ~~
* -. \_|// |||\\ ~~~~~~::::... /~
* ___-==_ _-~o~ \/ ||| \\ _/~~-
* __---~~~.==~||\=_ -_--~/_-~|- |\\ \\ _/~
* _-~~ .=~ | \\-_ '-~7 /- / || \ /
* .~ .~ | \\ -_ / /- / || \ /
* / ____ / | \\ ~-_/ /|- _/ .|| \ /
* |~~ ~~|--~~~~--_ \ ~==-/ | \~--===~~ .\
* ' ~-| /| |-~\~~ __--~~
* |-~~-_/ | | ~\_ _-~ /\
* / \ \__ \/~ \__
* _--~ _/ | .-~~____--~-/ ~~==.
* ((->/~ '.|||' -_| ~~-/ , . _||
* -_ ~\ ~~---l__i__i__i--~~_/
* _-~-__ ~) \--______________--~~
* //.-~~~-~_--~- |-------~~~~~~~~
* //.-~~~--\
* 神兽保佑
* 代码无BUG!