Java中IO流分为3种:
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以分为字节流和字符流
- 按照流的角色划分为节点流和处理流
字节流:处理字节数据的流设备。字节流可以处理设备上的所有数据,所以字节流可以处理字符数据。
字符流:由于字符每个国家都使用得不一样,所以涉及到了字符编码问题,为了方便文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。
InputStream/Reader:所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。
BIO:同步阻塞I/O模型,数据的读取写入必须阻塞在一个线程内等待其完成。
NIO:同步非阻塞的I/O模型,对应java.nio包,提供了Channel,Selector,Buffer等抽象。NIO提供了与传统BIO模型中的Socket和ServerSocket对应的SocketChannel和ServerSocketChannel两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。
AIO:进化版NIO,它是异步非阻塞的IO模型。异步IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会阻塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续操作。
IO中使用到的设计模式:装饰器模式
装饰器设计模式解决:对一组类进行功能的增强。
包装:写一个类(包装类)对被包装对象进行包装:
1. 包装类和被包装类要实现同样的接口;
2. 包装类要持有一个被包装类的引用;
3. 包装类在实现接口时,大部分方法是靠调用被包装对象来实现的。
new BufferedReader(new FileReader(""));
new BufferedReader(new InputStreamReader(System.in));一层嵌套一层,字节流转字符流等等,这样方式的使用就是装饰器模式的一种体现。
流的操作规律:
1. 明确源和目的:
- 数据源:需要读取,可以使用两个体系:InputStream、Reader;
- 目的:需要写入,可以使用两个体系:OutputStream、Writer;
2. 操作的数据是否属于纯文本数据:
如果是:
- 数据源:Reader
- 目的:Writer
如果不是:
- 数据源:InputStream
- 目的:OutputStream
3.体系中的对象
- 源:硬盘(File)、内存(byte[])、键盘(System.in)
- 目的:硬盘(File)、内存(byte[])、键盘(System.out)
缓存区(Buffer):是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态。Channel提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer,如下图所示:
该图模拟了从一个客服端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入Buffer中,然后将Buffer中的内容写入通道。服务端这边接收数据必须通过Channel将数据读入到Buffer中,然后再从Buffer中取出数据来处理。
通道(Channel):Channel和IO中的Stream(流)是类似的,例如FileInputStream对象,用来建立到目标(文件、网络套接字、硬件设备等)的一个链接,但是:BIO中的stream是单向的,例如FileInputStream对象只能进行读取数据操作,而NIO中的通道(Channel)是双向的,既可以用来进行读操作,也可以用来进行写操作。
Selector:Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
// 往本地文件中写数据
public class TestFileChannel {
public static void main(String[] args) throws Exception{
String str = "hello, nio, 我是哈哈";
// 通过输出流中获取到Channel
FileOutputStream fos = new FileOutputStream("basic.txt");
FileChannel fc = fos.getChannel();
// 设置缓存区的初始容量
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 在往channel中写入数据时,必须通过put方法存到ByteBuffer
byteBuffer.put(str.getBytes());
// 反转缓存区,重置位置到初始位置(将指针重置到head)
byteBuffer.flip();
fc.write(byteBuffer);
fos.close();
}
}
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class TestFileChannel2 {
public static void main(String[] args) throws Exception{
// 得到文件描述符
File file = new File("basic.txt");
// 得到流对象
FileInputStream fileInputStream = new FileInputStream(file);
// 打开通道
FileChannel fc = fileInputStream.getChannel();
// 开辟缓存
ByteBuffer byteBuffer = ByteBuffer.allocate((int)file.length());
// 读取到缓存中
fc.read(byteBuffer);
// 读到
System.out.println(new String(byteBuffer.array()));
fileInputStream.close();
}
}