Java IO

1. 概述 

Java IO一般包含两个部分:

  1.java.io包中堵塞型IO;

  2.java.nio包中的非堵塞型IO,通常称为New IO。

java.io包下,分为四大块近80个类:

1、基于字节操作的I/O接口:InputStream和OutputStream

2、基于字符操作的I/O接口:Writer和Reader

3、基于磁盘操作的I/O接口:File

4、基于网络操作的I/O接口:Socket(不在java.io包下)

  影响IO性能的无非就是两大因素:数据的格式及存储的方式,前两类主要是数据格式方面的,后两个类是存储方式方面的:本地和网络。

  我们很少单独使用哪个类来实现IO操作,平时都是几个类合起来使用,这其实体现了一种装饰器模式

2. 字节&字符

字节流可以处理任意类型的数据,而字符只能处理字符类型的数据

在Java中把不同的输入/输出源抽象表述为"流"。流是一组有顺序的字节集合,是对数据传输的总称或抽象。

流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。

  • 关于字节,每个字节(byte)有8bit组成。
  • 关于字符,我们可能知道代表一个汉字或者英文字母。

3. 编码

Java采用unicode编码,通讲,2个字节来表示一个字符。
在0~127整数之间的字符映射,unicode向下兼容ASCII,也就是1个字节表示一个字符。
一个中文或英文字符的unicode编码都占2个字节。

编码方式英文字符中文字符
GB 2312、GBK 1 2
UTF-8 1 3-4
UTF-16 2 3-4
UTF-32 4 4

4. 基于字节的I/O接口

我们先来看看类图:

  

FileInputStream:从文件中读取数据。

ByteArrayInputStream:将内存中的Byte数组适配为一个InputStream。

StringBufferInputStream:将内存中的字符串适配为一个InputStream,内部实现用的是StringBuffer。

             该类被Deprecated。主要原因是StringBuffer不应该属于字节流,所以推荐使用StringReader。

SequenceInputStream:将多个流对象转化成一个InputStream。

PipedInputStream:用于从管道中读取数据,在流中实现了管道的概念。

ObjectInputStream: 该流允许读取或写入用户自定义的类(对象)。

FilterInputStream:装饰器类,为其它InputStream类提供功能。java对I/O访问多提供的同步机制是过滤流,保证某时刻只有一个线程访问一个I/O流。


    DataInputStream:一般和DataOutputStream配对使用,完成基本数据类型的读写。

    BufferedInputStream:使用该对象阻止每次读取一个字节都会频繁操作IO。将字节读取一个缓存区,从缓存区读取。

    LineNumberInputStream:跟踪输入流中的行号。可调用getLineNumber和 setLineNumber(int)方法得到和设置行号。

    PushbackInputStream: 可以在读取最后一个byte 后将其放回到缓存中。主要用在编译器的语法、词法分析部分。

ByteArrayOutputStream:在内存中创建一个buffer。所有写入此流中的数据都被放入到此buffer中。

FileOutputStream:将信息写入文件中。

PipedOutputStream:任何写入此对象的信息都被放入对应PipedInputStream 对象的缓存中,从而完成线程的通信,实现了“管道”的概念。

FilterOutputStream:实现装饰器功能的抽象类。为其它OutputStream对象增加额外的功能。

    DataOutputStream:使用它可以写入基本数据类型。

    BufferedOutputStream:使用它可以避免频繁地向IO写入数据,数据一般都写入一个缓存区,在调用flush方法后会清空缓存、一次完成数据的写入。

    PrintStream:产生具有格式的输出信息。

        System.in, System.out, System.error(注:Java标准输入、输出、错误输出)是PrintStream的实例。

 

4.1 ObjectInputStream & ObjectOutputStream

  我们在写入文件的时候,常常因为要保存的是一个对象,也就是一个obj,但是里面的变量又很多,我们不可能挨个申明,一个个写入,这时候,我们就可以使用对象的序列化与反序列化。

序列化就是对象到保存文件的过程。
反序列化就是从保存的文件,转换为对象的过程。

一般在以下几种情况下,我们可能会用到序列化:
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候; 
b)当你想用套接字在网络上传送对象的时候; 
c)当你想通过RMI传输对象的时候。

 

4.2 PipedInputStream & PipedOutputStream

  管道主要用来实现同一个虚拟机进程中的两个线程进行交流。因此,一个管道既可以作为数据源媒介也可作为目标媒介。

  需要注意的是java中的管道和Unix/Linux中的管道含义并不一样,在Unix/Linux中管道可以作为两个位于不同空间进程通信的媒介,而在java中,管道只能为同一个JVM进程中的不同线程进行通信。

 

4.3 BufferedInputStream & BufferedOutputStream

  BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。

  在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。

  使用BufferedInputStream十分简单,只要把普通的输入流和BufferedInputStream组合到一起即可。

 

4.4 DataInputStream & DataOutputStream

 (1)编码方式:而DataOutputStream则采用的是UTF-8。 

 (2)异常机制:DataOutputStream在通过write()向“输出流”中写入数据时,若产生IOException,会抛出。

 (3)构造函数:DataOutputStream的构造函数只有一个:DataOutputStream(OutputStream out)。即它只支持以输出流out作为“DataOutputStream的输出流”。

 (4)目的:DataOutputStream的作用是装饰其它的输出流,它和DataInputStream配合使用:允许应用程序以与机器无关的方式从底层输入流中读写java数据类型。

 

4.5 PrintStream

  (1)编码方式:PrintStream是输出时采用的是用户指定的编码(创建PrintStream时指定的),若没有指定,则采用系统默认的字符编码。

  (2)异常机制:与其他输出流不同,PrintStream 永远不会抛出 IOException;它产生的IOException会被自身的函数所捕获并设置错误标记,

           用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。

  (3)构造函数:在PrintStream的构造函数中,能“指定字符集”和“是否支持自动flush()操作”。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数

  (4)目的:PrintStream为其它输出流提供打印各种数据值表示形式,使其它输出流能方便的通过print(), println()或printf()等输出各种格式的数据。

 

4.6 System.out.println

  (01) out是System.java的静态变量
  (02) 而且out是PrintStream对象,PrintStream.java中有许多重载的println()方法。

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));

将这两句话细分,可以划分为以下几步:
第1步 FileDescriptor fd = FileDescriptor.out;
第2步 FileOutputStream fdOut = new FileOutputStream(fd);
第3步 BufferedOutputStream bufOut = new BufferedOutputStream(fdOut, 128);
第4步 PrintStream ps = new PrintStream(bufout, true);
第5步 setOut0(ps);

  (01) 获取FileDescriptor.java中的静态成员out,out是一个FileDescriptor对象,它实际上是“标准输出(屏幕)”的标识符。

  (02) 创建“标准输出(屏幕)”对应的“文件输出流”。

  (03) 创建“文件输出流”对应的“缓冲输出流”。目的是为“文件输出流”添加“缓冲”功能。

  (04) 创建“缓冲输出流”对应的“打印输出流”。目的是为“缓冲输出流”提供方便的打印接口,如print(), println(), printf();使其能方便快捷的进行打印输出。

  (05) System.java中setOut0() 执行setOut0(ps),setOut0()是一个native本地方法,就是将ps设置为System.java的out静态变量。

 

5. 基于字符的I/O接口

  Reader是所有的输入字符流的父类,它是一个抽象类。

    CharReader、StringReader是两种基本的介质流,它们分别将Char数组、String中读取数据。

    PipedReader是从与其它线程共用的管道中读取数据。

    BufferedReader很明显就是一个装饰器,它和其子类负责装饰其它Reader对象。

    FilterReader是所有自定义具体装饰流的父类。    

Writer和Reader操作的目的就是操作字符,不是字节,设计Writer和Reader的目的是国际化,使IO操作支持16位的Unicode。

 

5.1 InputStreamReader & OutputStreamReader

对于IO操作,不管是磁盘还是网络,最终都是对字节的操作,而我们平时写的程序都是字符形式的,所以在传输的过程中需要进行转换。

在字符到字节的转换过程中,我们需要用到一个类:InputStreamReader。

 InputStreamReader是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。

注意:inputStream和reader,outputStream与write的函数都很相似,并且每次进行了IO操作,要记得close,

因为IO资源并不属于内存资源,并不会被GC回收,所以需要显示的手动的回收资源。

对于输出操作,close还会自动flush。

 

6、基于磁盘的I/O操作(File)

 

6.1 File

  文件和文件夹的操作都可以用File来完成。

 

6.2 RandomAccessFile

优点: RandomAccessFile 可以实现对文件的随机读写,但是他并不是继承于以上4中基本虚拟类。

缺点:RandomAccessFile的方法有一个最大的局限,就是只能读写文件,不能读写其他IO节点。

使用场景:RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。 

mode中,有4中启动的方式:

  "r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。

  "rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

  "rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备

  "rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备

注意:RandomAccessFile虽然可以设置了偏移的方法,但他不能实现中间插入的效果,如果你需要实现文本中间插入的话,要先将后面的文件内容拷贝,然后写入,最后在写入的写一行,将拷贝的东西复制回来。

 

6.3 FileDescriptor

  FileDescriptor 是“文件描述符”。
  FileDescriptor 可以被用来表示开放文件、开放套接字等。
  以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。

  
  FileDescriptor的对象in, out, err介绍

  (01) in -- 标准输入(键盘)的描述符

  (02) out -- 标准输出(屏幕)的描述符

  (03) err -- 标准错误输出(屏幕)的描述符

 

7、基于网络的I/O操作(Socket)

8. 总结

 

9. 参考

java io系列01之 "目录"(很好值得推荐)

java io系列17之 System.out.println("hello world")原理

Java之美[从菜鸟到高手演变]之Java中的IO

Java IO详解

Java IO用法详解

Java IO完全总结(转载) --- 重点在源码分析

深入分析 Java I/O 的工作机制

http://ifeve.com/java-io/

http://tutorials.jenkov.com/java-io/file.html

posted @ 2020-06-08 18:36  mzjnumber1  阅读(204)  评论(0编辑  收藏  举报