目录
一、IO流基本认识
1.1 字节流
1.2 字符流
1.3 高级流
二、IO流基本理解
2.1 IO流的分类
2.1.1 按照流的流向
2.1.2 按照处理数据单位
2.1.3 按照流的角色
2.2 IO流的选择
2.3 IO流的4个抽象基类
2.4 字节流和字符流的区别
2.4.1 使用场景
三、IO模型
3.1 BIO(Blocking IO)
3.2 NIO(Non-blocking IO)
3.3 AIO(Asynchronous IO)
3.4 IO多路复用
3.5 BIO/NIO/AIO区别
3.6 NIO的实现原理
四、序列化和反序列化
一、IO流基本认识
Java中的IO流是用于处理输入和输出的机制。分为输入流和输出流。
Java的IO流主要分为两大类:字节流和字符流。
1.1 字节流
- InputStream和OutputStream:是所有字节输入流和输出流的抽象类,它们分别用于读取和写入字节
- FileInputStream和FileOutputStream:用于从文件中读取字节和向文件中写入字节
- ByteArrayInputStream和ByteArrayOutputStream:分别用于从字节数组中读取数据和将数据写入字节数组
InputStream in = new FileInputStream("input.txt");
OutputStream out = new FileOutputStream("output.txt");
FileInputStream fis = new FileInputStream("file.txt");
FileOutputStream fos = new FileOutputStream("output.txt");
ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
1.2 字符流
- Reader和Writer:是所有字符输入流和输出流的抽象基类,它们分别用于读取和写入字符
- FileReader和FileWriter:用于从文件中读取字符和向文件中写入字符
- BufferedReader和BufferedWriter:用于提供缓冲区,提高读取和写入的效率
Reader reader = new FileReader("input.txt");
Writer writer = new FileWriter("output.txt");
FileReader fr = new FileReader("file.txt");
FileWriter fw = new FileWriter("output.txt");
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
1.3 高级流
- ObjectInputStream和ObjectOutputStream:用于读取和写入对象。可以序列化和反序列化对象
- DataInputStream和DataOutputStream:用于读取和写入基本数据类型
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
二、IO流基本理解
2.1 IO流的分类
2.1.1 按照流的流向
分为输入流和输出流,这里的输入输出是针对程序来说的
- 输出:把程序(内存)中的内容输出到磁盘、光盘等存储设备中
- 输入:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
2.1.2 按照处理数据单位
分为字节流和字符流。
- 字节流:每次读取/写出一个字节,当传输的资料文件中有中文时,就会出现乱码
- 字符流:每次读取/写出两个字节,有中文时,使用该流就可以正确传输显示中文。1字符=2字节
2.1.3 按照流的角色
分为节点流和处理流
- 节点流:从或者向一个特定的地方(节点)读写数据。如FileInputSream
- 处理流(包装流):是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader
注意:一个IO流可以既是输入流又是字节流又或是以其他方式分类的流类型,是不冲突的
2.2 IO流的选择
根据三步选择适合自己的流:
1、选择输入流还是输出流:看是想向程序写数据,还是从程序取数据
2、考虑传输数据是每次传一个字节还是两个字节:是否存在中文
3、通过前两步可以选出一个合适的节点流了,如果想在此基础上增强功能,那么在处理流中选择一个合适的即可
2.3 IO流的4个抽象基类
- 输入字节流:InputStream
- 输出字节流:OutputStream
- 输入字符流:Writer
- 输出字符流:Reader
2.4 字节流和字符流的区别
计算机中的一切最终都是二进制的字节形式存在,底层设备永远只接受字节数据。有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。
字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的;
而字符流在操作时使用了缓冲区,通过缓冲区再操作文件
2.4.1 使用场景
- 字节流:一般用来处理图像、视频、PPT、Word类型的文件
- 字符流:一般用于处理纯文本类型的文件,如TXT文件
字节流可以用来处理纯文本文件,但字符流不能用于处理图像视频等非文本类型的文件
三、IO模型
3.1 BIO(Blocking IO)
BIO属于同步阻塞IO模型,读取或写入数据的时候,线程将一直等待,知道数据准备就绪或写入操作完成。但在高并发环境下可能导致性能问题,因为线程在等待IO操作完成时被阻塞,无法执行其他任务。
3.2 NIO(Non-blocking IO)
NIO属于非阻塞IO模型,线程执行一个IO操作时不会等待,而是继续执行其他任务,这需要通过轮询或者使用回调函数等机制来检查IO操作是否完成。
3.3 AIO(Asynchronous IO)
AIO属于异步IO模型,允许程序在执行IO操作时继续执行其他任务,而不需要等待。
3.4 IO多路复用
IO多路复用模型使用了操作系统提供的选择器(Selector)机制。通过选择器,一个线程可以监听多个通道上的IO事件,从而在单线程中处理多个连接。
3.5 BIO/NIO/AIO区别
1、BIO
- 工作原理:阻塞IO模型中,当一个线程执行IO操作时,它会被阻塞,知道IO操作完成,这会导致线程无法执行其他任务
- 适用场景:适用于连接数较少、并发不高的场景
2、NIO
- 工作原理:非阻塞IO模型中,一个线程可以处理多个连接,而不需要等待每个连接的IO操作完成。但线程需要通过轮询(polling)或者选择器(Selector)来检查哪些连接已经准备好进行IO操作
- 适用场景:适用于高并发、连接数较多的场景。Java NIO提供了Selector、Channel等组件,可以更好地支持多连接的管理
3、AIO
- 工作原理:异步IO模型中,程序发起IO操作后,可以继续执行其他任务。当IO操作完成时,系统会通知程序,并调用相应的回调函数
- 适用场景:适用于需要处理大量并发连接的场景,并希望充分利用系统资源。Java NIO.2 提供了AIO支持,通过AsynchronousChannel和CompletionHandler实现异步IO操作
总结:
BIO: 适用于连接数较少、并发不高的情况,简单易用。
NIO: 适用于高并发、连接数较多的网络应用,通过选择器实现非阻塞 I/O。
AIO: 适用于需要处理大量并发连接、希望充分利用系统资源的情况,通过异步操作实现。
3.6 NIO的实现原理
Java NIO的实现原理主要涉及到一下几个核心概念和组件:
Channel(通道):抽象概念,类似于传统的流,但更加灵活。Channel可以是读、写或者读写的,并且可以异步地进行IO操作
Buffer(缓冲区):是NIO中用于存储数据的容器。Channel从Buffer中读取数据,将数据写入Buffer中
Selector(选择器):它允许一个线程同时监控多个Channel,当其中的某个Channel发生读或写事件时,可以通过Selector得到通知。这样一个线程可以有效地管理多个网络连接
工作原理可以分为以下几个步骤:
1、打开Channel:通过FileChannel、SocketChannel、ServerSocketChannel等类的静态方法open()打开一个通道
2、创建Buffer:创建一个或多个Buffer,用于读取或写入数据
3、将数据写入Channel:将数据写入Buffer,然后将Buffer中的数据写入Channel
4、从Channel读取数据:将Channel中的数据读取到Buffer中
5、注册Channel到Selector:通过Selector监听一个或多个Channel,当Channel上发生感兴趣的事件时,Selector将通知程序
6、处理事件:在一个循环中调用Selector的select()方法,该方法会阻塞至少一个注册的Channel发生了感兴趣的事件。然后通过迭代selectedKeys()获取SelectionKey,从而得知哪个Channel上发生了事件
四、序列化和反序列化
序列化机制:将对象转换成字节序列
对象的序列化:将一个Java对象写入IO流中
对象的反序列化: 从IO流中恢复该Java对象
若对象要支持序列化机制,则它的类需要实现Serializable接口。该接口是一个标记接口,它没有提供任何方法,只是标明该类是可以序列化的。Java的很多类已经实现了Serializable接口,如包装类、String、Date等。