Java IO 笔记

来源:互联网 发布:淘宝网上商城 编辑:程序博客网 时间:2024/06/05 09:24

========================================================================================================================================================
Java IO总框架

OutputStream

  1. ByteArrayOutputStream

    在内存中创建一个缓冲区,发送给流的所有数据都会置入这个缓冲区##一般将其和FilterOutputStream套接得到额外的功能。建议首先和BufferedOutputStream套接实现缓冲功能。 通过toByteArray方法可以得到流中的数据(不透明装饰器的用法) ByteArrayOutputStream() ByteArrayOutputStream(int size)
  2. FileOutputStream

    将信息发给一个文件##可以加上一个代表写入的方式是否为append的标记。一般将其和FilterOutputStream套接得到额外的功能 FileOutputStream(File file) FileOutputStream(String name)
  3. PipedOutputStream 输出的任何信息都会自动变成对应的一个PipedInputStream的输入,从而实现了”管道化”的概念

  4. FilterOutputStream 作为装饰类一个接口使用的抽象类:为其他OutputStream类提供了有用的功能。如DataInputStream,BufferedInputStream等

     FilterOutputStream(OutputStream out)
  5. ObjectOutputStream 作用是对象写入流中

     ObjectOutputStream() ObjectOutputStream(OutputStream out)

    装饰类

  6. DataOutputStream

        与DataInputStream配合使用,以便采用"与平台无关"的形式,将原始数据类型(int, char, long等)写入一个数据流     DataOutputStream(OutputStream out)
  7. PrintStream

        用于产生格式化输出,注意DataOutputStream控制的是数据的"存储",而PrintStream控制的是数据的"显示"##一般在Java程序中,DataOutputStream用于数据的存储,     即J2EE中持久层完成的功能,PrintStream完成显示的功能,类似于J2EE中表现层的功能     PrintStream(File file)     PrintStream(OutputStream out)     PrintStream(String filename)
  8. BufferedOutputStream

        避免每次发出一点儿数据的时候都要进行物理性的写入,要求"先用缓冲区",可随时调用flush(),对缓冲区进行刷新,从而完成物理性的写入##提供和其他OutputStream     一致的接口,只是内部提供一个缓存的功能。     BufferedOutputStream(OutputStream out)     BufferedOutputStream(OutputStream out, int size)
  9. OutputStream类方法

        void close()    void flush()    void write(byte[] b)    void write(byte[] b, int off, int len)    abstract void write(int b)

Writer类

  1. BufferedWriter 用于将文本写入到输出流中去,同时对写入的字符提供缓冲,提供有效的输出,缓冲区的大小是可以指定的,也可以是默认的

         BufferedWriter(Writer out)     BufferedWriter(Writer out, int size)
  2. PipedWriter 用于向PipedReader写入字符

         PipedWriter()     PipedWriter(PipedReader snk)
  3. PrintWriter 用于写格式化数据

         PrintWriter(File file)     PrintWrtier(OutputStream out)     PrintWriter(String filename)     PrintWriter(Writer out)
  4. StringWriter 用于向字符串写

         StringWriter()     StringWriter(int initialSize)
  5. FilterWriter 用于写过滤后的流

         FilterWriter(Writer out)
  6. OutputStreamWriter 用于写字符流

         OutputStreamWriter(OutputStream out)
  7. FileWriter 用来写入字符文件的便捷类

             FileWriter(File file)         FileWriter(String fileName)
  8. CharArrayWriter 用于向字符数组写

         CharArrayWriter()     CharArrayWriter(int initialSize)
  9. Writer类方法

        Writer append(char c)    Writer append(CharSequence csq)    Writer append(CharSequence csq, int start, int end)    abstract void close()    abstract void flush()    void write(char[] cbuf)    abstract void write(char[] cbuf, int off, int len)    void write(int c)    void write(String str)    void Write(String str, int off, int len)

InputStream类

  1. AudioInputStream 被用来读取指定格式的音频数据,数据的长度是以帧为单位,而不是以byte为单位
  2. SequenceInputStream 把多个InputStream类的对象串联在一起,再依次读出数据
  3. FileInputStream

    从文件中读取数据[可能无法显示文件中的中文,涉及到编码]##通过一个代表文件路径的String,File对象或者FileDescriptor对象创建,一般作为数据源,同样会使用其他装饰器提供额外的功能 FileInputStream(File file) FileInputStream(String name)
  4. ObjectInputStream 被用来反序列化的读出数据(包括原形数据和对象数据),它经常与ObjectOutputStream配合使用

     ObjectInputStream() ObjectInputStream(InputStream in)
  5. ByteArrayInputStream

    用于读取字节数组##将内存中的Byte数组适配为一个InputStream,可以从内存中的Byte数组创建该对象,一般作为数据源,会使用其他装饰流提供额外的功能,一般都建议加个缓冲功能 ByteArrayInputStream(byte[] buf) ByteArrayInputStream(byte[] buf, int off, int len)
  6. StringBufferInputStream 已过时,从JDK1.0开始使用StringReader从字符串创建流

  7. PipedInputStream 用于读取管道流

  8. FilterInputStream 用于过滤现有流的输入,为装饰类提供基类

     FilterInputStream(InputStream in)

    FilterInputStream类

    1. BufferedInputStream

      在内存中缓冲一个流的输入,以使读操作更有效##使用该对象阻止每次读取一个字节都会频繁操作IO。将字节读取一个缓冲区,从缓冲区读取。可以利用一个InputStream, 或者带上一个自定义的缓存去的大小构造。使用InputStream的方法读取,只是背后多一个缓存的功能。设计模式中透明装饰器的应用。BufferedInputStream(InputStream in)BufferedInputStream(InputStream in, int size)
    2. DataInputStream

      从二进制文件读取基本类型数据,即将int等直接读取出来##一般和DataOutputStream配对使用,完成基本数据类型的读写,利用一个InputStream构造 DataInputStream(InputStream in)
    3. LineNumberInputStream

      跟踪输入流的行号,可以调用getLineNumber()和setLineNumber(int)方法得到和设置行号, 利用一个InputStream构造,仅仅增加一个行号,可以像使用其他InputStream一样使用。
  9. InputStream类方法

        int available()    void close()    void mark(int readlimit)    boolean markSupported()    abstract int read()    int read(byte[] b)    int read(byte[] b, int off, int len)    void reset()    long skip(long n)

Reader类

  1. InputStreamReader 用于读取字符流

         InputStreamReader(InputStream in)
  2. FileReader 用来读取字符文件的便捷类

             FileReader(File file)         FileReader(String fileName)
  3. PipedReader 用于读取PipedWriter中的数据

         PipedReader()     PipedReader(PipedWriter src)
  4. BufferedReader 用于缓冲其他的Reader类

         BufferedReader(Reader in)     BufferedReader(Reader in , int size)
  5. LineNumberReader 跟踪行号的缓冲字符输入流

             LineNumberReader(Reader in)         LineNumberReader(Reader in, int size)
  6. CharArrayReader 用于读取字符数组

         CharArrayReader(char[] buf)     CharArrayReader(char[] buf, int off, int len)
  7. FilterReader 用于读取过滤后的流

         FilterReader(Reader in)
  8. PushbackReader 用于读取已过滤的字符流的抽象类

             PushbackReader(Reader in)
  9. StringReader 用于读取字符串

         StringReader(String s)
  10. Reader类方法

        abstract void close()    void mark(int readAheadLimit)    int read()    int read(char[] cbuf)    abstract int read(char[] cbuf, int off, int len)    int read(CharBuffer target)    boolean ready()    void reset()    long skip(long n)

RandomAccessFile类

  1. RandomAccessFile(File file, String mode)
  2. RandomAccessFile(String name, String mode)

===============================================================================================================================================
Java IO总解说

  1. Java的IO主要包含三部分:

    1,流式部分——IO的主体部分;
    2,非流式部分——主要包含一些辅助流式部分的类,如File, RandomAccessFile和FileDescriptor
    3,文件读取部分与安全相关,如SerializablePermission等

  2. 流式部分可以概括为:

    两个对应一个桥梁。
    两个对应:字节流和字符流的对应 输入和输出的对应
    一个桥梁:从字节流到字符流的桥梁,对应于输入和输出为InputStreamReader和OutputStreamReader

  3. 流的具体类可以分为

    1,介质流(Media Stream或者原始流Raw Stream)-主要指一些基本的流,从具体的介质如文件,内存缓冲区(byte 数组, char 数组, StringBuffer对象)等读取数据

    2,过滤流(Filter Stream)-主要指所有FilterInputStream/FilterOutputStream和FilterReader/FilterWriter的子类,主要是对其包装的类进行某些特定的处理,如缓存等。

    可能有些场合我们需要在文件中随机插入数据、在流中来来回回地执行某些操作,这时候我们绝对不可以使用流相关的对象。很幸运JDK的设计者为我们设计了一个单独的类RandomAccessFile,它可以完成打开、关闭文件、以基本数据类型的方式读取数据、读取下一个行、以UTF等格式读取数据、写入各种类型的数据、比较特殊的是他可以通过文件指针的seek方法让文件指针移到某个位置,可以通过getFilePointer方法得到当前指针的位置、可以通过length()方法得到当前文件的容量、通过getFD得到FileDescriptor对象,通过getChannel方法得到FileChannel对象,从而和New IO整合。

=====================================================================================================================

1,针对文件的读写,JDK专门提供了两个类,分别是FileInputStream和FileOutputStream。FileInputStream是InputStream的子类,是操作文件的字节输入流,专门用于读取文件的数据。需要注意的是,在读取文件数据时,必须保证文件是存在并且可读的,否则会抛出文件找不到的异常FileNotFoundException。而如果是通过FileOutputStream向一个已经存在的文件中写入数据,那么该文件中的数据首先会被清空,再写入新的数据。若希望在已经存在的文件内容后添加新的内容,则可使用FileOutputStream的构造函数FileOutputStream(String fileName, boolean append)来创建文件输出流对象,并把append参数的值设置为true。IO流在进行读写操作时会出现异常,为了代码的简洁,在程序中使用throws关键字将抛出异常。然而一旦遇到IO异常,IO流的close方法将无法得到执行,流对象所占用的资源将得不到释放。因此,为了保护IO流的close方法必须执行,通常将关闭流的操作写在finally代码块中。具体代码如下

finally{        try{            if(in != null)                  // 如果in不为空,关闭输入流                in.close();        }catch(Exception e)        {            e.printStackTrace();        }        try{            if(out != null)                 // 如果out不为空,关闭输出流                out.close();        }catch(Exception e)        {            e.printStackTrace();        }}

FileInputStream读取:    FileInputStream in = new FileInputStream(new File(fileName));    while(in.available() > 0)    {        System.out.println((char)in.read());    }    in.close();FileOutputStream写入:[如果程序无法写入或创建文件,检查当前目录是否有写权限]    FileOutputStream out = new FileOutputStream(aFile);    byte[] b = new byte[1024];                  // 建立输出流    String str = "This is a test file";    b = str.getBytes();                         // 进行String到byte[]的转化    out.write(b);

2,当通过流的方式拷贝文件时,为了提高效率也可以定义一个字节数组作为缓冲区。在拷贝文件时,可以一次性读取多个字节的数据,并保存在字节数组中,然后将字节数组的数据一次性写入文件。

3,InputStream类和OutputStream类在读写文件时操作的都是字节,如果希望在程序中操作字符,使用这两个类就不太方便,为此JDK提供了字符流。同样字符流也有两个抽象的顶级父类,分别是Reader和Writer。其中Reader是字符输入流,用于从某个源设备读取字符,Writer是字符输出流,用于向某个目标设备写入字符。

4,想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从关联的文件中读取一个或一组字符。需要注意的是,字符输入流的read方法返回的是int类型的值,如果想获得字符就需要强制类型转换。FileWriter同FileOutputStream一样,如果指定的文件不存在,就会创建该文件,再写入数据;如果文件存在,则会首先清空文件中内容,再进行写入。

5,包装流可以对一个已存在的流进行包装来实现数据读写功能,利用包装流可以有效地提高读写数据的效率。字符流同样提供了带缓冲区的包装流,分别是BufferedWriter和BufferedReader,其中BufferedReader用于对字符输入流进行包装,BufferedWriter用于对字符输出流进行包装,需要注意的是,在BufferedReader中有一个重要的方法readLine(),该方法用于一次读取一行文本。需要注意的是,由于包装流内部使用了缓冲区,在循环中调用BufferedWriter的write方法写字符时,这些字符首先会被写入缓冲区,当缓冲区写满时或调用close方法时,缓冲区的字符才会被写入目标文件。因此在循环结束时一定要调用close方法,否则极有可能会导致部分存在于缓冲区中的数据没有被写入目标文件

6,Java程序在编译或运行期间经常会出现一些错误,在错误中会报告出错的行号,为了方便查找错误,需要在代码中加入行号。JDK提供了一个可以跟踪行号的输入流。LineNumberReader,是BufferedReader的直接子类。在拷贝过程中,使用LineNumberReader类跟踪行号,首先调用setLineNumber()方法设置行号的初始值,本例中将行号起始值设置为0。调用getLineNumber()方法读取行号时,行号是从1开始的。这是因为LineNumberReader类在读取到换行符'\n',回车符'\r'或者回车后紧跟换行符时,会将行号自动加1。

7,有时字节流和字符流之间也需要进行转换。在JDK中提供了两个类可以将字节流转换为字符流,它们分别是InputStreamReader和OutputStreamWriter。转换流也是一种包装流,其中OutputStreamWriter是Writer的子类,它可以将一个字节输出流包装成字符输出流,方便直接写入字符,而InputStreamReader是Reader的子类,它可以将一个字节输入流包装成字符输入流,方便直接读取字符。图示如下:(源设备)字节流    -- InputStreamReader --> 字符流(应用程序) -- OutputStreamWriter -->字节流(源设备)e.g.// 将字节流输入转换成字符输入流   InputStreamReader isr = new InputStreamReader(new FileInputStream("src.txt"));// 对字符流对象进行包装BufferedReader br = new BufferedReader(isr);e.g.// 将字节输出流转换成字符输出流OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src.txt"));// 对字符输出流对象进行包装BufferedWriter bw = new BufferedWriter(osw);需要注意的是,在使用转换流时,只能针对操作文本文件的字节流进行转换,如果字节流操作的是一张图片,此时转换为字符流就会造成数据丢失。

8,如果希望永久保存这些对象,则可以将对象转为字节数据写入到硬盘上,这个过程称为对象序列化。为此JDK中提供了ObjectOutputStream(对象输出流)来实现对象的序列化。当对象进行序列化时,必须保证该对象实现Serializable接口,否则程序会出现NotSerializableException 异常。类对象被序列化后会生成二进制数据保存在文件中,通过这些二进制数据可以恢复序列化之前的Java对象,此过程称为反序列化。JDK提供了ObjectInputStream类(对象输入流),可以实现对象的反序列化。

9,有的时候,并不需要存储整个对象的信息,而只需要存储对象的成员数据,这些成员数据的类型又都是基本数据类型,这时,不必使用对象Object相关的流,可以使用IO包中提供的另外两个数据操作流,即DataInputStream和DataOutputStream。这是与平台无关的数据操作流,它们不仅提供了读写各种基本类型数据的方法,而且还提供了readUTF()和writeUTF()方法。DataInputStream的readUTF方法能够从输入流中读取采用UTF-8字符编码的字符串,DataOutputStream的writeUTF方法则可向输出流中写入采用UTF-8字符编码的字符串。// 建立输出流    DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("cih.cih")));// 建立输入流    DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("cih.cih")));// 建立输入Reader    BufferedReader reader = new BufferedReader(new InputStreamReader(in));

10,输出流在通过write方法写数据时,只能输出字节或字符类型的数据。如果希望输出其他类型,例如一个基本数据类型的int值19,一个Student类型的对象,此时需要将数据先转为字符串再输出。这种操作方式显然比较麻烦,为此,在IO包中提供了一个PrintStream类,它提供了一系列用于打印数据的print和println方法,被称作打印流。PrintStream可以实现将基本数据类型的数据或引用数据类型的对象格式化成字符串后再输出。

11,标准输出输入流e.g.StringBuffer sb = new StringBuffer();int ch;while((ch = System.in.read()) != -1){    if(ch == '\r' || ch == '\n')        break;    sb.append((char)ch);}System.out.println(sb);有时候,程序会向命令行窗口输出大量的数据,由于输出数据滚动得太快,会导致无法阅读,这时可以将标准输出流重新定向到其他的输出设备,例如输出到一个文件中。在System类提供了一些静态方法。void setIn(InputStream in)void setOut(PrintStream out)void setErr(PrintStream out)

12,多个线程之间也可以通过IO流实现数据的传输,为此JDK中提供了一种管道流。管道流分为管道输入流和管道输出流,它是一种比较特殊的流,必须先建立连接才能进行彼此间的通信。PipedOutputStream用于向管道中写入数据,PipedInputStream用于从管道中读取写入的数据。

13,前面介绍的IO流有一个共同特点,就是只能按照数据的先后顺序读取源设备中的数据,或者按照数据的先后顺序向目标设备写入数据。但如果希望从文件的任意位置开始执行读写操作,则字节流和字符流都无法实现。为此,IO包中提供一个类RandomAccessFile,它不属于流类,但具有读写文件数据的功能,可以随机地从文件中的任何位置开始执行读写数据的操作。public class RandomAccessFile extends Object implements DataOutput, DataInput, Closeable

RandomAccessFile(File file, String mode)RandomAccessFile(String name, String mode)long getFilePointer()               // 返回当前读写指针所处的位置void seek(long pos)                 // 设定读写指针的位置,与文件开头相隔pos个字节数int skipBytes(int n)                // 使读写指针从当前位置开始,跳过n个字节void setLength(long newLength)      // 设置此文件的长度

14,字符编码ASCII           使用7位二进制数来表示所有的大小写字母,数字0~9,标点符号以及在美式英语中使用的特殊控制字符ISO8859-1       拉丁码表,兼容ASCII,还包括了西欧语言,希腊语,泰语,阿拉伯语等GB2312          兼容ASCII,每个英文占1个字节,中文占2个字节(2个字节都为负数,最高位都为1)GBK,GB18030     兼容GB2312,包含更多中文,每个英文占一个字节,中文占2个字节(第1个字节为负数,第2个字节可正可负)Unicode         国际标准码,为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言,跨平台进行文本转换,处理的要求,每个字符占2个字节            Java中存储的字符类型就是使用Unicode编码UTF-8           是针对Unicode的可变长编码,可以用来表示Unicode标准中的任何字符,其中,英文占1个字节,中文占3个字节,这是程序开发中最常用的字符码表。一般来说,把字符串转换成计算机识别的字节序列称为编码,而把字节序列转换为普通人能看懂的明文字符串称为解码。在计算机程序中,如果要把字节数组转换为字符串,可以通过String类的构造方法String(byte[] bytes, String charsetName)把字节数组按照指定的码表解码成字符串(如果没有指定字符码表,则用操作系统默认的字符码表,如中文的Windows系统默认使用的字符码就是GBK);反之,可以通过使用String类的getBytes(String charsetName)方法把字符串按照指定的码表编码成字节数组。e.g.    String str = "传智播客";byte[] b1 = str.getBytes();byte[] b2 = str.getBytes("GBK");byte[] b3 = str.getBytes("UTF-8");String result2 = new String(b2, "GBK");String result3 = new String(b3, "UTF-8");

这是IO的API框架图

备注:
  • 程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件
  • IO操作属于资源操作,一定要记得关闭
  • 文件中换行为 \r\n
  • 字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是用到缓冲区的。字节流在操作文件时,即使不关闭资源,文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容。
  • 在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成,所以使用字节的操作是最多的。如果要java程序实现一个拷贝功能,应该选用字节流进行操作(可能拷贝的是图片),并且采用边读边写的方式节省内存。

关于序列化

序列化(Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。    序列化使其他代码可以查看或修改那些不序列化便无法访问的对象实例数据。确切地说,代码执行序列化需要特殊地权限:即指定了SerializationFormatter标志地SecurityPermission。在默认策略下,通过Internet下载的代码或Internet代码不会授权该权限;只有本地计算机上的代码才被授予该权限。通过,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化地表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存不可序列化的重要数据。如果无法实现这一点,则应该注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。我们都知道一个对象只要实现了 Serilizable 接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。  然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上 transient 关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。  总之,java 的 transient 关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字 transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
1 0
原创粉丝点击