Java-IO系列

IO基础

按数据流的方向划分:输入流、输出流

按处理单位划分:字节流、字符流

字节流和字符流的区别:
    字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。
    读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

ByteArrayInputStream和ByteArrayOutputStream

默认字节数组大小为32.

PipedInputStream和PipedOutputStream

    它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
    使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。

建立PipedInputStream和PipedOutputStream的连接

PipedInputStream的缓冲区buffer的默认大小是1024个字节

ObjectInputStream 和 ObjectOutputStream 

主要的作用是用于写入对象信息与读取对象信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了。作用是,对基本数据和对象进行序列化操作支持。
创建“文件输出流”对应的ObjectOutputStream对象,该ObjectOutputStream对象能提供对“基本数据或对象”的持久存储;当我们需要读取这些存储的“基本数据或对象”时,可以创建“文件输入流”对应的ObjectInputStream,进而读取出这些“基本数据或对象”。
注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!

ObjectInputStream怎么判断是否读到末尾

方法之一:将若干个对象(数量不定)都装入一个容器中(如:ArrayList之类),然后将容器这一个对象写入就行了。读取时,只要读取一个对象(即容器对象)就行了。

方法之二:使用EOFException来判断结束。

try{
     while(true)
      {
         Object o=ois.radObject();
         //处理已读出的对象o;
      }
 }catch(EOFxception e){
     //已从流中读完。
 }
finallly{
    //流的关闭。
}

备注:序列化/反序列化,是Java提供一种专门用于的保存/恢复对象状态的机制。

    实现对象序列化的原因:1、释放内存,如防止session占用过多内存,保存session到服务器。2、跨网络环境信息传输,Java的远程方法调用(Remote Method Invocation简称RMI)能让你像调用自己机器上的对象那样去调用其它机器上的对象。(transient和static变量的信息是不可被保存的)

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

    1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
    2. 对象的反序列化创建对象的时候并不会调用到构造方法的、(这点文中没有说到,想要验证的同学在构造方法后面加一句System.out.println("构造方法执行吗?");,实际上构造方法是不执行的,自然这句话也没有输出了)
    3. serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
    4. 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID进行对比,如果这两个id不一致,反序列则失败。
    5. 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
    6. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
    7. 如果一个类维护了另外一个类的引用,则另外一个类也需要实现Serializable接口。

序列化/反序列化,只支持保存/恢复对象状态,即仅支持保存/恢复类的成员变量,但不支持保存类的成员方法!

    (01) 序列化对static和transient变量,是不会自动进行状态保存的。 transient的作用就是,用transient声明的变量,不会被自动序列化,“序列化不会自动保存static和transient变量”,因此我们若要保存它们,则需要通过writeObject()和readObject()去手动读写。
    (02) 对于Socket, Thread类,不支持序列化。若实现序列化的接口中,有Thread成员;在对该类进行序列化操作时,编译会出错!  这主要是基于资源分配方面的原因。如果Socket,Thread类可以被序列化,但是被反序列化之后也无法对他们进行重新的资源分配;再者,也是没有必要这样实现。

实现序列化,除了Serializable之外,还有其它的方式,就是通过实现Externalizable来实现序列化。

    (01) 实现Externalizable接口的类,不会像实现Serializable接口那样,会自动将数据保存。
    (02) 实现Externalizable接口的类,必须实现writeExternal()和readExternal()接口!否则,程序无法正常编译!
    (03) 实现Externalizable接口的类,必须定义不带参数的构造函数!否则,程序无法正常编译!
    (04) writeExternal() 和 readExternal() 的方法都是public的,不是非常安全!

FileInputStream 和 FileOutputStream

    (01) FileOutputStream fileOut2 = new FileOutputStream(file, true);它是以“追加模式”将内容写入文件的。即写入的内容,追加到原始的内容之后。
    (02) FileOutputStream fileOut2 = new FileOutputStream(file, false);它是以“新建模式”将内容写入文件的。即删除文件原始的内容之后,再重新写入。

    补充:File 是“文件”和“目录路径名”的抽象表示形式。
    File 直接继承于Object,实现了Serializable接口和Comparable接口。实现Serializable接口,意味着File对象支持序列化操作。而实现Comparable接口,意味着File对象之间可以比较大小;File能直接被存储在有序集合(如TreeSet、TreeMap中)。

FilterInputStream 和 FilterOutputStream

    FilterInputStream 的作用是用来“封装其它的输入流,并为它们提供额外的功能”。它的常用的子类有BufferedInputStream和DataInputStream。
    FilterOutputStream 的作用是用来“封装其它的输出流,并为它们提供额外的功能”。它主要包括BufferedOutputStream, DataOutputStream和PrintStream。

BufferedInputStream和BufferedOutputStream 

BufferedInputStream 是缓冲输入流。它继承于FilterInputStream。    BufferedInputStream会将该输入流数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从输入流中读取下一部分的数据。
    为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash等存储介质中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
    那干嘛不干脆一次性将全部数据都读取到缓冲中呢?第一,读取全部的数据所需要的时间可能会很长。第二,内存价格很贵,容量不像硬盘那么大。

BufferedOutputStream 是缓冲输出流。它继承于FilterOutputStream。

InputStreamReader和OutputStreamWriter

InputStreamReader 的作用是将“字节输入流”转换成“字符输入流”。它继承于Reader。
OutputStreamWriter 的作用是将“字节输出流”转换成“字符输出流”。它继承于Writer。

FileReader和FileWriter

FileReader 是用于读取字符流的类,它继承于InputStreamReader。要读取原始字节流,请考虑使用 FileInputStream。
FileWriter 是用于写入字符流的类,它继承于OutputStreamWriter。要写入原始字节流,请考虑使用 FileOutputStream。

BufferedReader和

BufferedReader 是缓冲字符输入流。它继承于Reader。作用是为其他字符输入流添加一些缓冲功能。

    BufferReader的作用是为其它Reader提供缓冲功能。创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数。 BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。
    为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
    那干嘛不干脆一次性将全部数据都读取到缓冲中呢?第一,读取全部的数据所需要的时间可能会很长。第二,内存价格很贵,容量不想硬盘那么大。

BufferedWriter 是缓冲字符输出流。它继承于Writer。作用是为其他字符输出流添加一些缓冲功能。

PrintWriter

PrintWriter 是字符类型的打印输出流,它继承于Writer。用于向文本输出流打印对象的格式化表示形式。它实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

RandomAccessFile

RandomAccessFile 是随机访问文件(包括读/写)的类。它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据。
需要注意的是,RandomAccessFile 虽然属于java.io包,但它不是InputStream或者OutputStream的子类;它也不同于FileInputStream和FileOutputStream。 FileInputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进行写操作;但是,RandomAccessFile 同时支持文件的读和写,并且它支持随机访问。

RandomAccessFile共有4种模式:"r", "rw", "rws"和"rwd"。

"r"    以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw"   打开以便读取和写入。
"rws"  打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。  
"rwd"  打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。  

猜你喜欢

转载自blog.csdn.net/u013263066/article/details/87205306