JavaのIO基础知识

IO

输入Input:数据输入到计算机内存的过程 。

输出Output:数据输出到外部存储的过程。

IO 流在 Java 中分为输入流输出流,而根据数据的处理方式又分为字节流字符流

四个抽象类基类:输入流的基类---InputStream【字节输入】、Reader【字符输入】

                             输出流的基类---OutputStream【字节输出】、Writer【字符输出】

字节流

InputStream(字节输入流)

从源头(通常是文件)读取数据(字节信息)到内存中,java.io.InputStream常用方法如下:

read()返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节,则代码返回 -1 ,表示文件结束。
read(byte b[ ])等价于 read(b, 0, b.length),从输入流中读取一些字节存储到数组 b 中,返回读取的字节数(无则-1)
read(byte b[], int off, int len)off---偏移量;len---要读取的最大字节数
skip(long n)忽略n个字节,返回实际忽略的字节数
available()返回输入流中可以读取的字节数
close()关闭并释放资源

Java9新增

readAllBytes()   读取输入流中的所有字节,返回字节数组

readNBytes(byte[] b, int off, int len)   阻塞直到读取 len 个字节

transferTo(OutputStream out)   将所有字节从一个输入流传递到一个输出流

FileInputStream 是一个比较常用的字节输入流对象,可直接指定文件路径,可以直接读取单字节数据,也可以读取至字节数组中。通常配合 BufferedInputStream(字节缓冲输入流)使用。

InputStream fis = new FileInputStream("input.txt")
BufferedInputStream bufferedInputStream = new BufferedInputStream(fis);
// 读取文件的内容并复制到 String 对象中
String result = new String(bufferedInputStream.readAllBytes());

OutputStream(字节输出流)

将数据(字节信息)写入到目的地(通常是文件),常用方法:write(int b)、write(byte b[ ])、write(byte[] b, int off, int len)、flush()【刷新此输出流并强制写出所有缓冲的输出字节】、close()

同输入对应FileOutputStream与BufferedOutputStream

字符流

因为 字符流由Java虚拟机将字节转换得到,耗时;乱码问题等,IO流提供了一个直接操作字符的接口,方便字符操作,在涉及字符时使用,默认Unicode编码。

Reader(字符输入流)

从源头(通常是文件)读取数据(字符信息/文本)到内存中,常用方法如下:

read()、read(char[] cbuf)、read(char[] cbuf, int off, int len)、skip(long n)、close()

InputStreamReader 是字节流转换为字符流的桥梁extends Reader,其子类 FileReader 是基于该基础上的封装,可以直接操作字符文件。

try (FileReader fileReader = new FileReader("input.txt");) {
    int content;
    long skip = fileReader.skip(3);
    System.out.println("The actual number of bytes skipped:" + skip);
    System.out.print("The content read from file:");
    while ((content = fileReader.read()) != -1) {
        System.out.print((char) content);
    }
} catch (IOException e) {
    e.printStackTrace();
}

Writer(字符输出流)

将数据(字符信息)写入到目的地(通常是文件),常用方法:

write(int c)、write(char[] cbuf)、write(char[] cbuf, int off, int len)、write(String str)、write(String str, int off, int len)、append(CharSequence csq)【将指定的字符序列附加到指定的 Writer 对象并返回该 Writer 对象】、append(char c)、flush()、close()

同输入OutputStreamWriter 是字符流转换为字节流的桥梁extends Writer,其子类 FileWriter 是基于该基础上的封装,可以直接将字符写入到文件。

字节/字符缓冲流

将数据加载至缓冲区,一次性读取/写入多个字节,避免频繁的 IO 操作,如通过 BufferedInputStream(字节缓冲输入流)来增强 FileInputStream 的功能。

BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("input.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))

BufferedReader (字符缓冲输入流)和 BufferedWriter(字符缓冲输出流)功能类似,内部都维护了一个字节数组作为缓冲区。

打印流

System.out.print("Hello!") ,System.out 实际是用于获取一个 PrintStream 对象,print方法实际调用的是 PrintStream 对象的 write 方法。PrintStream 属于字节打印流,与之对应的是 PrintWriter (字符打印流)。PrintStream 是 OutputStream 的子类,PrintWriter 是 Writer 的子类。

随机访问流

RandomAccessFile 支持随意跳转到文件的任意位置进行读写,可以指定 mode(读写模式)。

RandomAccessFile randomAccessFile = new RandomAccessFile(new File("input.txt"), "rw");

四种IO设计模式

装饰器(Decorator)模式

装饰器模式用组合代替继承扩展功能,适用于继承复杂的IO流。

对于字节流,FilterInputStream/FilterOutputStream是核心,其BufferedXxx、DataXxx等子类增强FileXxx等基础流。

适配器(Adapter Pattern)模式

        适配器模式协调接口不兼容的类,像电源转接头。分对象适配(组合)与类适配(继承)。IO中,InputStreamReader/OutputStreamWriter是对象适配器,把字节流转字符流,分别用StreamDecoder/StreamEncoder编解码。

工厂模式

        用于创建对象,NIO 中大量用到了工厂模式,比如 Files 类的 newInputStream 方法用于创建 InputStream 对象(静态工厂)、 Paths 类的 get 方法创建 Path 对象(静态工厂)、ZipFileSystem 类的 getPath 的方法创建 Path 对象(简单工厂)。

观察者模式

        NIO 中的文件目录监听服务使用到了观察者模式,基于 WatchService 接口【观察者】和 Watchable 接口【被观察者】实现。

补充:Java NIO(1.4)= Non-blocking,Channel+Selector+Buffer,面向缓冲、通道,高并发网络首选。

IO模型

开发常接触的两种IO为磁盘 IO(读写文件) 和 网络 IO(网络请求和响应),应用程序发起 IO 调用,内核真正执行 IO。

同步阻塞模型 BIO (Blocking I/O)

应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。无法处理高并发

同步非阻塞模型

read没数据立即返回,线程空档做别的事,再回来轮询,避免久堵;但高频轮询耗CPU。

I/O 多路复用模型

先 select/epoll 问哪些内核数据就绪,再阻塞 read,减少无效系统调用,省 CPU。

信号驱动IO

应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。

异步IO

相对于同步IO,异步IO不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。

NIO

弥补了同步阻塞 I/O 的不足,它在标准 Java 代码中提供了非阻塞面向缓冲基于通道的 I/O,可以使用少量的线程来处理多个连接,大大提高了 I/O 效率和并发。

传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。

零拷贝

计算机执行 IO 操作时,CPU 不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及 CPU 的拷贝时间,主要解决操作系统在处理 I/O 操作时频繁复制数据的问题。零拷贝的常见实现技术有: mmap+write、sendfile和 sendfile + DMA gather copy 。

传统

mmap+write【mmap() 替换 read(),将内核缓冲区里的数据「映射」到用户空间】

sendfile【替代前面的 read() 和 write() 这两个系统调用,这样就可以减少一次系统调用,也就减少了 2 次上下文切换的开销】

DMA gather copy【网卡的 SG-DMA 控制器,缓冲区只将描述符和数据长度传到socket缓冲区,数据直接拷贝到网卡缓冲区】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值