接上文
IO
- 42.Java 中 IO 流分为几种?
- Java IO体系中的装饰器模式
- 抽象组件(Component)
- 具体组件(Concrete Component)
- 抽象装饰器(Decorator)
- 具体装饰器(Concrete Decorator)
- 使用装饰器
- 总结
- 43.既然有了字节流,为什么还要有字符流?
- 44.BIO、NIO、AIO?
42.Java 中 IO 流分为几种?
流按照不同的特点,有很多种划分方式。
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的角色划分为节点流和处理流
Java Io流共涉及40多个类,看上去杂乱,其实都存在一定的关联, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
IO流用到了什么设计模式?
其实,Java的IO流体系还用到了一个设计模式——装饰器模式。
InputStream相关的部分类图如下,篇幅有限,装饰器模式就不展开说了。
装饰器模式(Decorator Pattern)是一种设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有类的一个包装。
装饰器模式通常用于以下场景:
- 在不影响其他对象的情况下,想要动态地给单个对象添加一些额外的职责。
- 当不能采用生成子类的方法进行扩充时,这种情况可能是因为类定义被隐藏,或者类定义不能用于生成子类。
在Java的IO体系中,装饰器模式被广泛使用,用于对输入输出流进行功能扩展。
Java IO体系中的装饰器模式
在Java的IO库中,有四个基本抽象类:
InputStream
:所有输入流的超类。OutputStream
:所有输出流的超类。Reader
:所有读取字符流的超类。Writer
:所有写入字符流的超类。
这些抽象类有多个实现,装饰器类也实现了这些抽象类,并提供了额外的功能。
下面是一个简单的例子来说明装饰器模式在Java IO体系中的应用:
抽象组件(Component)
public abstract class InputStream {
// 抽象方法,子类必须实现
public abstract int read() throws IOException;
// 其他方法...
}
具体组件(Concrete Component)
public class FileInputStream extends InputStream {
// 实现了从文件读取字节的方法
public int read() throws IOException {
// 读取文件内容的实现...
}
}
抽象装饰器(Decorator)
public abstract class FilterInputStream extends InputStream {
protected volatile InputStream in; // 被装饰的对象
protected FilterInputStream(InputStream in) {
this.in = in;
}
// 默认实现,直接调用被装饰对象的相应方法
public int read() throws IOException {
return in.read();
}
// 其他方法...
}
具体装饰器(Concrete Decorator)
public class BufferedInputStream extends FilterInputStream {
// 构造方法,接收一个InputStream对象
public BufferedInputStream(InputStream in) {
super(in);
}
// 重写read方法,添加缓冲功能
public int read() throws IOException {
// 添加缓冲逻辑...
return in.read();
}
// 其他方法...
}
使用装饰器
InputStream in = new FileInputStream("file.txt");
InputStream bufferedIn = new BufferedInputStream(in);
int data = bufferedIn.read(); // 使用带缓冲的读取方式
在这个例子中,BufferedInputStream
是一个装饰器,它给 FileInputStream
添加了缓冲的功能。客户端代码可以透明地使用 BufferedInputStream
,就像使用普通的 InputStream
一样,但获得了额外的缓冲功能。
总结
装饰器模式的关键优点是它提供了比继承更灵活的扩展功能的方式。通过使用装饰器,可以动态地添加或删除功能,而不需要修改现有代码。Java IO体系中的装饰器模式允许开发者根据需要组合不同的流来创建具有复杂功能的流处理管道。
43.既然有了字节流,为什么还要有字符流?
其实字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还比较耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。
所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
44.BIO、NIO、AIO?
BIO(blocking I/O) : 就是传统的IO,同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过连接池机制改善(实现多个客户连接服务器)。
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,程序简单易理解。
NIO :全称 java non-blocking IO,是指 JDK 提供的新 API。从JDK1.4开始,Java 提供了一系列改进的输入/输出的新特性,被统称为NIO(即New IO)
NIO是同步非阻塞的,服务器端用一个线程处理多个连接,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有IO请求就进行处理:
NIO的数据是面向缓冲区Buffer的,必须从Buffer中读取或写入。
可以看出,NIO的运行机制:
-
每个Channel对应一个Buffer
-
Selector对应一个线程,一个线程对应多个Channel。
-
Selector会根据不同的事件,在各个通道上切换。
-
Buffer是内存块,底层是数据。
AIO:JDK 7 引入了 Asynchronous I/O,是异步不阻塞的 IO。在进行 I/O 编程中,常用到两种模式:Reactor 和 Proactor。Java 的 NIO 就是 Reactor,当有事件触发时,服务器端得到通知,进行相应的处理,完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
Linux IO模型和Java IO模型的对应关系: