【学习】Java IO基础(一)
一、IO流的概念
Java的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出源抽象表述为"流"。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。
二、IO流的分类
1. 输入流和输出流
根据数据流向的不同分为:输入流和输出流。
- 输入流:只能从中读取数据,而不能向其写入数据;
- 输出流:只能向其写入数据,而不能从中读取数据。
输入流类名中包含关键字InputStream或Reader,输出流类名中包含关键字OutputStream或Writer。
2. 字节流和字符流
根据操作的数据单元类型分为:字节流和字符流
字节流操作的数据单元是8位的字节(byte),字符流操作的是16位的字符。
-
字节流:操作的数据单元是8位的字节(byte);
-
字符流:操作的数据单元是16位的字符,处理纯文本数据,优先考虑使用字符流,除此之外都使用字节流。
字节流类名中包含关键字InputStream或OutputStream,字符流类名中包含关键字Reader或Writer。
3. 节点流与处理流
按流的角色来划分,分为节点流与处理流
- 节点流:是指程序可以向一个特定的节点读写数据,直接连接数据源。
- 最常见的是文件,类名中包含关键字File。还可以是数组、管道、字符串,关键字分别为ByteArray/CharArray,Piped,String。
- 处理流:其不直接连接数据源,大多情况是对已存在的节点流进行包装,是一种典型的装饰器设计模式。
- 使用处理流主要是为了更方便的执行输入输出工作,如PrintStream,输出功能很强大,推荐输出时都使用处理流包装。
4. 一些特别的流类型
- 转换流:转换流只有字节流转换为字符流,因为字符流使用起来更方便,我们只会向更方便使用的方向转化。如:InputStreamReader与OutputStreamWriter;
- 缓冲流:有关键字Buffered,也是一种处理流,为其包装的流增加了缓存功能,提高了输入输出的效率,增加缓冲功能后需要使用flush()才能将缓冲区中内容写入到实际的物理节点。但是,在现在版本的Java中,只需记得关闭输出流(调用close()方法),就会自动执行输出流的flush()方法,可以保证将缓冲区中内容写入;
- 对象流:有关键字Object,主要用于将目标对象保存到磁盘中或允许在网络中直接传输对象时使用(对象序列化);
- 推回输入流:有关键字PushBack,当程序调用推回输入流的unread()方法时,系统回把指定数组内容的内容推回到一个推回缓冲区中,在调用read()方法读入内容时,就先从推回缓冲区中读取,直到读完推回缓冲区中内容后才会从原输入流中读取。
三、IO流的四大基类
InputStream、Reader、OutputStream、Writer,这四大抽象基类,本身并不能创建实例来执行输入/输出,但它们将成为所有输入/输出流的模版,所以它们的方法是所有输入/输出流都可以使用的方法。类似于集合中的Collection接口。
1. InputStream
InputStream 是所有的输入字节流的父类,是一个抽象类,主要包含三个方法:
//从输入流读取数据的下一个字节,并以整数的形式返回,读到文件末尾返回-1。
int read();
//从输入流中读取一定数量的字节,并将其存储在缓冲区数组buffer中,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。
int read(byte[] buffer);
//读取len个字节并存储到一个字节数组buffer,从off位置开始存,最多len,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。
int read(byte[] buffer, int off, int len);
2. Reader
Reader 是所有的输入字符流的父类,是一个抽象类,主要包含三个方法:
//从输入流读取数据的下一个字符,并以整数的形式返回,读到文件末尾返回-1。
int read();
//读取cbuf.length长度的字符并存储到数组cbuf,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。等同于read(cbuf, 0, cbuf.length)
int read(char[] cbuf);
//读取len个字符,并存储到数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。
int read(char[] cbuf, int off, int len);
对比 InputStream 和 Reader 所提供的方法,不难发现两个基类的功能基本一样的,只不过读取的数据单元不同。
在执行完流操作后,要调用 close() 方法关闭输入流,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。
除此之外,InputStream和Reader还支持如下方法来移动流中的指针位置:
// 标记了流的当前位置,可以用reset函数重新定位到此位置
// 如果执行reset函数时的位置超过当时mark位置+readlimit,则重定位失败
void mark(int readlimit)
// 测试此输入流是否支持 mark 方法
boolean markSupported()
// 跳过和丢弃此输入流中数据的 n 个字节/字符
long skip(long n)
// 尝试重新定位到最后一次对此输入流调用 mark 方法时的位置
void reset()
3. OutputStream
OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下四个方法:
// 向输出流中写入一个字节数据,该字节数据为参数b的低8位。
void write(int b) ;
// 将一个字节类型的数组b中的数据写入输出流。
void write(byte[] b);
// 从字节类型数组b的位置off开始,写出len个字节的数据到输出流。
void write(byte[] b, int off, int len);
// 将输出流中缓冲的数据全部写出到目的地。
void flush();
4. Writer
Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含如下六个方法:
// 向输出流中写入一个字符数据,该字节数据为参数b的低16位。
void write(int c);
// 将字符类型数组cbuf中的数据写入输出流。
void write(char[] cbuf)
// 从字符类型数组cbuf的位置off开始,写出len个字符的数据到输出流。
void write(char[] cbuf, int off, int len);
// 将字符串string写入输出流。
void write(String string);
// 从字符串string的位置off开始,写出len个字符的数据到输出流。
void write(String string, int off, int len);
// 将输出流中缓冲的数据全部写出到目的地。
void flush()
可以看出,Writer 比 OutputStream 多出两个方法,主要是支持写入字符和字符串类型的数据。
使用 Java 的 IO 流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close(方法之前,自动执行输出流的flush()方法)
四、NIO
NIO(Non-blocking I/O,在Java领域也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。
笔者这里以后再展开讲(还没学..)