java基础之IO流(一)之字节流
IO流体系太大,涉及到的各种流对象,我觉得很有必要总结一下。
那什么是IO流,IO代表Input、Output,而流就是原始数据源与目标媒介的数据传输的一种抽象。典型数据源与目标媒介包括磁盘、网络、内存等等。
IO流的分类:
按流向分为:输入流和输出流(本地内存为参考)
按处理数据单位:字符流和字节流
按照是否与特定的地方相连(磁盘、网络、内存):节点流和处理流
节点流:可以从或向一个特定的地方(节点)读写数据。
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
下图是常用到的IO流,需要说明的是箭头指向不代表直接的继承关系。此图中出现的流还会在下文中一一介绍。
(一)字节流
根据上图可知,字节流对应的顶层抽象类分别为 InputStream和OutputStream,字节流时万能流,可以处理任意类型的数据。
InputStream提供了基本的读取数据的方法:
int read():一次读取一个字节,返回值为读到的字节,如果返回值为-1,则表示读到末端。
int read(byte[] b):一次读取一个字节数组,返回值为读到的有效长度。
int read(byte[] b, int offset, int len):一次读取一个字节数组的一部分(少用),返回值为读入缓冲区的字节总数,如果读到末尾,返回-1
其子类就是在三个读取功能的基础上进行了各自的实现与功能扩展。
OutputStream提供了基本的写数据的方法:
void write(int b):一次写一个字节,形参为int变量,如果b超过了byte的范围,采取高位截断的方式进行处理。
void write(byte[] bys):一次写一个字节数组。
void write(byte[] bys, int offset, int len):一次写一个字节数组的一部分。
其子类就是在这三个写功能的基础上进行各自的实现和功能扩展。
1. FileInputStream和FileOutputStream
从类名中就可以看出这一对输入输出流针对的数据源和目标媒介为文件系统。
FIleInputStream:
构造方法有两个:
FileInputStream(String fileName)和FileInputStream(File file)。
FileInputStream仅仅实现了InputStream提供的基本读的方法,并没有特殊的功能。
FileOutputStream:
包含四个构造方法:
FileOutputStream(String fileName) 和FileOutputStream(File file)
FileOutputStream(String fileName,boolean append)和FileOutputStream(File file, boolean append):表示执行写方法时是否在目标媒介末尾追加。
FileOutputStream一样,并未对父类功能进行扩展。
2. BufferedInputStream和BufferedOutputStream
自带缓冲区的字节流,从他的构造方法也可以看出,它是一个处理流,内部封装了一个InputStream或OutputStream对象。
BuffeedInputStream构造方法:
BufferedInputStream(InputStream in, int size):指定其内部封装的字节流和缓冲区的大小。
BufferedInputStream(InputStream in, int size):指定内部封装的字节流,缓冲区的大小采用默认值。
private static int DEFAULT_BUFFER_SIZE = 8192;//默认缓冲区8*1024
protected volatile byte buf[];//实际存放数据的地方
BufferedOutputStream构造参数与BufferedInputStream相同,指定内部的封装的输出流和缓冲区大小。
BufferedInputStream和BufferedOutputStream都封装了节点流,内部维护了一个缓冲区,具体数据的读写都是通过传入的节点流进行的,在读写功能上也并未扩展。
3.DataInputStream和DataOutputStream
思考这样一个问题,操作基本数据类型的数据,若不使用数据流会怎样?答案是高位阶段,造成精度丢失。而数据流(DataInputStream和DataOutputStream)的使命就是完成基本数据类型的读写。
它也是一个处理流,内部封装了节点流,从其构造方法中可以看出:
DataInputStream(InputStream in), 提供了基本数据类型的读操作:readInt(); readChar();readByte();readBoolean();readDouble()等
DataOutputStream(OutputStream out):包含相应基本数据类型的写操作。
其读写各种数据的内部实现原理也是通过一个一个字节的读取,然后通过二进制位的移位运算进行的。
需要特别留意的一个方法:
writeUTF() :使用的是修改版的UTF-8编码:该方法会首先写入两个字节用来表示要写入的字符串的长度,后面跟的是要写字符串的字节表示,每个字符1~3个字节不等。因此总长度为2+字符串长度到2+字符串长度的3倍不等。readUTF()用于读取writeUTF写入的字符串。
4. ByteArrayInputStream和ByteArrayOutputStream
这两个流是用来临时处理数据的。
了解ByteArrayOutputStream关键一点在于了解它的数据源和目标媒介。数据源很容易理解,就是通过write(int)、write(byte[])、write(byte[] ,int ,int)方法写入的数据,目标媒介就是内存中的一个字节数组,可自动扩容,内存字节数组的大小可通过构造方法设置,也可使用默认大小。
protected byte buf[];//实际存数据的字节数组
protected int count;//字节数组的有效长度
public ByteArrayOutputStream() {
this(32);//默认长度为32
}
ByteArrayInputputStream的数据源是通过构造方法传入的字节数组,内部也有一个字节数组用于存储从中读取的数据。
protected byte buf[];
public ByteArrayInputStream(byte buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
5.PrintStream
PrintStream是用来装饰其他输出流,为其他输出流添加功能,方便他们打印出各种数据值表示形式。与其他输出流不同,PrintStream永远不会抛出IOException,它产生的错误会被自身的函数所捕获并设置错误标记,用户可以通过checkError()返回错误标记,查看是否产生IOException。PrintStream提供了自动flush和字符集设置功能,写入的数据会立刻调用flush()函数。PrintStream
打印的所有字符都使用平台的默认字符编码转换为字节,也可通过构造方法设置字符集。