引言
对程序语言设计者来说,设计一个令人满意的I/O(输入输出)系统,是件极艰巨的任务,摘自《Thinking in Java 》
概述
JAVA程序通过流来完成输入/输出。流是生产或消费信息的抽象,流通过Java的输入/输出系统与物理设备连接。JAVA的I/O通过java.io包下的类和接口来支持,主要是5个类和一个接口。
- File:用于文件或目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件路径等
- InputStream:抽象类,所有输入流的父类,定义了所有输入流具有的共同特征。
- OutputStream:抽象类,基于字节的输出操作,所有输出流的父类,定义了输出流具有的共同特征。
- Reader:抽象类,基于字符的输入类。
- Writer:抽象类,基于字符的输出类。
- Serializable:将Java对象转换成平台无关的二进制数据
- RandomAccessFile:包装了一个随机访问的文件,实现了DataInput/DataOutput接口。
File类
一个File类的对象,表示了磁盘上的文件或目录,提供了与平台无关的方法来对磁盘上的文件或者目录进行操作,直接处理文件或目录,但是不能访问文件本身。
在java.io包中提供了60多了类流:
从功能上分为:
- 输入流:从外部流向内存
- 输出流:从内存流向外部
从流的角色上分为:
- 节点流:可以从/向一个特定的IO设备(磁盘,网络)读/写数据的流,直接连接物理设备或socket
- 处理流:对一个已经存在的流(节点流)进行连接或者封装。
从流的结构上分为:
- 字节流 :InputStream OutputStream
- 字符流 : Reader Writer
在最底层,所有的输入输出都是以字节为单位的,基于字符的流只是为处理字符提供方便有效的方法。它们的操作方式几乎完全一样,只是操作的数据单元不同,字节流操作的数据单元是字符,字符流操作的数据单元是字符。
类流全家福
从上面的图片中,可以看出无论是字节流和字符流都提供了对文件,数组,字符串,管道,对象,音频等内容的输入/输出流支持。
对输入/输出流基类的简单介绍
InputSteam 三个基本的读方法:
abstract int read(): 读取一个字节数据,并返回读到数据的末尾,如果返回-1 , 表示都到了输入流的末尾。
int read(byte[] b ): 将数据读入一个字节数组,同时返回实际读取的字节数,如果返回-1 ,表示读到了输入流的末尾。
int read(byte[] b , int off , int len):将数据读入一个字节数组,同时返回实际读取的字节数,如果返回-1 ,表示读到了输入流的末尾,off指定在数组b中存放数据的起始位置,len指定读取的最大字节数。
可能会有一个疑问,为什么上面的第一个方法是抽象,而其余两个read方法是具体的? 通过源码找到了想要的答案。第二个方法依靠了第三个方法,而第三个方法依靠了第一个方法实现,换句话说只有第一个read方法是与具体的I/O设备相关的,需要InputStream的子类来实现。源码如下:
public abstract int read() throws IOException;
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; //没有字节返回-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; //返回读取的字节数组长度
}
其它方法
long skip(long n ):输入流中跳过n个字节,并返回实际跳过的字节数
void close():关闭流
。。。
OutputStream 主要方法
void write(byte[]] b ):将参数b中的字节写到输出流
void write(byte[] b , int off ,int len):将参数b的从偏移量off开始len个字节写到输出流
abstract viud write(int b ):先将int转换成byte类型,把低字节写入到输出流中
void flush():将缓冲区中的字节全部输出,并清空缓存区
void close():关闭输出流并释放与流相关的系统资源
Reader里包含的3个读取方法:
int read():
int read(char[] c):
int read(char[] c ,int off , int len) :
Write包含的方法
void write(char[] c)
void write(String str)
void write(String str, int off, int len)
abstract flush()
void close()
根据上面图片做一个简单的总结:
- FileInputStream和FileOutputStream 节点流,用于对文件读取或往文件中写入字节流。如果在构造FileOutputStream时,文件已经存在,则覆盖整个文件。
- BufferedInputStream和BufferedOutputStream过滤流,需要使用已经存在的节点流来构造,提供了读写的效率。
- DataInputStream和DataOutputStream 过滤流,需要使用已经存在的节点流来构造,提供了读写Java中基本数据类型的功能。
- PipedInputStream和PipedOutputStream 管道流,用于线程间的通信,一个线程的PipedInputStream对象从另一个线程的PipedOutputStream 对象读取输入,只要管道流有用,必须提供同时构造管道输入流和管道输出流。
- InputStreamReader和OutputStreamWriter类,用于处理字符流的基本类,用来在字节流和字符流之间搭一座桥。将字节流和字符流进行转换。
- FileReader和FileWriter 可以创建一个可以读/写文件的类。
- BufferedReader:通过缓冲输入提高性能。
- SequenceInputStream:把多个InputStream合并为一个InputStream
I/O流的链接示意图:
常用类
FileInputStream , FileOutputStream , ByteArrayInputStream, ByteArrayOutputStream ,BufferedReader,BufferedWriter, ,InputStreamReader ,InputStreamWriter,FileReader ,FileWriter
如何选择I/O流
确定是输入还是输出
输入:输入流 InputStream Reader
输出:输出流 OutputStream Writer
明确操作的数据对象是否是纯文本
是:字符流 Reader,Writer
否:字节流 InputStream,OutputStream
明确具体的设备。
文件:
读:FileInputStream,, FileReader,
写:FileOutputStream,FileWriter
数组:
byte[ ]:ByteArrayInputStream, ByteArrayOutputStream
char[ ]:CharArrayReader, CharArrayWriter
String:
StringBufferInputStream(已过时,因为其只能用于String的每个字符都是8位的字符串), StringReader, StringWriter
Socket流
键盘:用System.in(是一个InputStream对象)读取,用System.out(是一个OutoutStream对象)打印
是否需要转换流
是,就使用转换流,从Stream转化为Reader、Writer:InputStreamReader,OutputStreamWriter
是否需要缓冲提高效率
是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWrite
少年听雨歌楼上,红烛昏罗帐。
壮年听雨客舟中,江阔云低,断雁叫西风。
感谢支持!
---起个名忒难