1.字节数组流概述
回顾我们所学的IO流,IO流按照处理对象不同来分类,可以分为节点流和包装流。目前我们所学的FileOutputStream、FileInputStream、FileWriter和FileReader都属于节点流,而缓冲流、转换流、打印流、数据流和对象流等都属于包装流。节点流都可以配合包装流来操作,例如直接使用字节流来复制文件效率低,那么我们可以使用缓冲流来提高效率。例如使用字节流来存取任意数据类型数据操作繁琐,那么我们可以使用对象流来简化操作等等。
接下来,我们要学习的字节数组流,它也属于节点流。字节数组流分为输入流和输出流,分别是:ByteArrayInputStream和ByteArrayOutputStream。使用字节数组流的时候,为了提高效率和简化操作,我们也可以让字节数组流配合包装流来一起使用。
常见的节点流中,例如:FileInputStreamr和FileReade都是把“文件”当做数据源,而ByteArrayInputStream则是把内存中的“字节数组”当做数据源。字节数组流,就是和内存中的数组相关的一个流,可以将字节数组写到输出流中,也可以将字节数组从输入流中读出来,不涉及磁盘。内存数组输出流可以看成一个可自动扩容的byte数组,可以往里写字节。
通过字节数组流,我们可以实现所有数据类型(基本数据类型、引用数据类型)和字节数组之间的转换,然后把转换成字节数组后可以保存到文件或者传输到网络。
2.ByteArrayOutputStream类
ByteArrayOutputStream字节数组输出流在内存中创建一个byte数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中。缓冲区初始化时默认32个字节,会随着数据的不断写入而自动增长,但是缓冲区最大容量是2G,只要数据不超过2G,都可以往里写。
数据写出完毕后,可使用toByteArray()方法或toString()方法来获取数据,从而实现了将任意数据类型数据转化为字节数组。
例如,给一个字节数组,然后往这个数组中放入各种数据,比如整形、布尔型、浮点型、字符串和对象等,这种需求就可以使用ByteArrayOutputStream来实现。
【示例】将任意数据类型数据转化为字节数组案例
public class ArrayStreamTest {
public static void main(String[] args) {
try {
// 字节数组输出流(节点流),可将任意数据类型转换为字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 缓冲流(包装类),用于提高效率
BufferedOutputStream bos = new BufferedOutputStream(baos);
// 对象流(包装流),实现写出任意数据类型
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 使用对象流来写数据
oos.writeInt(123);
oos.writeDouble(123.45);
oos.writeChar('A');
oos.writeBoolean(false);
oos.writeUTF("whsxt");
oos.writeObject(new Date());
// 刷新流,在获取数据之前一定要先刷新流,因为使用了包装流
oos.flush();
// 获取数据
byte[] bs = baos.toByteArray();
System.out.println(Arrays.toString(bs));
} catch (IOException e) {
e.printStackTrace();
}
}
}
通过查看底层源码,我们发现ByteArrayOutputStream类的close()方法并没有实现,所以调用close()方法关闭此流后仍可被使用。
3.ByteArrayInputStream类
字节数组输入流就是把一个字节数组 byte[] 包装了一下,使其具有流的属性,可顺序读下去,还可标记跳回来继续读,主要的作用就是用来读取字节数组中的数据。
同理,关闭ByteArrayInputStream无效,调用close()方法在关闭此流后仍可被调用。
【示例】读取上个案例获取的字符数组
public class ArrayStreamTest {
public static void main(String[] args) {
try {
// 获取字节数组,返回上个案例中通过字节数组输出流写出的字节数组
byte[] bs = outputStreamMethod();
// 字节数组输入流(节点流),用于读取字节数组中的数据
ByteArrayInputStream bios = new ByteArrayInputStream(bs);
// 缓冲流(包装类),用于提高效率
BufferedInputStream bis = new BufferedInputStream(bios);
// 对象流(包装流),实现读取指定类型的数据
ObjectInputStream ois = new ObjectInputStream(bis);
// 读取数据
System.out.println(ois.readInt());
System.out.println(ois.readDouble());
System.out.println(ois.readChar());
System.out.println(ois.readBoolean());
System.out.println(ois.readUTF());
System.out.println(ois.readObject());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
补充,ByteArrayInputStream和ByteArrayOutputStream是字节数组流,那么与之对应的字符数组流则是StringReader和StringWriter。
与字节数组流相比,字符数组流反而用得更少,因为StringBuilder和StringBuffer也能方便的用来存储动态长度的字符,而且大家更熟悉这些类。