java IO流全面总结

流的概念和作用

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。 

Java流操作有关的类或接口:

File类

File类是对文件系统中文件以及文件夹进行封装的对象,可以通过对象的思想来操作文件和文件夹。 File类保存文件或目录的各种元数据信息,包括文件名、文件长度、最后修改时间、是否可读、获取当前文件的路径名,判断指定文件是否存在、获得当前目录中的文件列表,创建、删除文件和目录等方法。  

RandomAccessFile类

该对象并不是流体系中的一员,其封装了字节流,同时还封装了一个缓冲区(字符数组),通过内部的指针来操作字符数组中的数据。 该对象特点:

  1. 该对象只能操作文件,所以构造函数接收两种类型的参数:a.字符串文件路径;b.File对象。
  2. 该对象既可以对文件进行读操作,也能进行写操作,在进行对象实例化时可指定操作模式(r,rw)

注意:该对象在实例化时,如果要操作的文件不存在,会自动创建;如果文件存在,写数据未指定位置,会从头开始写,即覆盖原有的内容。 可以用于多线程下载或多个线程同时写数据到文件。

Java流类图结构:

java输入/输出流体系中常用的流的分类表

分类字节输入流字节输出流字符输入流字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串     StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流     InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream    
抽象基类 FilterInputStream FilterOutputStream FilterReader FilterWriter
打印流   PrintStream   PrintWriter
推回输入流 PushbackInputStream   PushbackReader  
特殊流 DataInputStream DataOutputStream    

注:表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:红色斜体字标出的类代表抽象基类,无法直接创建实例。

  • FileInputStream类的使用:读取文件内容
 1 package com.app;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileNotFoundException;
 5 import java.io.IOException;
 6 
 7 public class A1 {
 8 
 9     public static void main(String[] args) {
10         A1 a1 = new A1();
11     
12         //电脑d盘中的abc.txt 文档
13         String filePath = "D:/abc.txt" ;
14         String reslut = a1.readFile( filePath ) ;
15         System.out.println( reslut ); 
16     }
17 
18 
19     /**
20      * 读取指定文件的内容
21      * @param filePath : 文件的路径
22      * @return  返回的结果
23      */
24     public String readFile( String filePath ){
25         FileInputStream fis=null;
26         String result = "" ;
27         try {
28             // 根据path路径实例化一个输入流的对象
29             fis  = new FileInputStream( filePath );
30 
31             //2. 返回这个输入流中可以被读的剩下的bytes字节的估计值;
32             int size =  fis.available() ;
33             //3. 根据输入流中的字节数创建byte数组;
34             byte[] array = new byte[size];
35             //4.把数据读取到数组中;
36             fis.read( array ) ; 
37 
38             //5.根据获取到的Byte数组新建一个字符串,然后输出;
39             result = new String(array); 
40 
41         } catch (FileNotFoundException e) {
42             e.printStackTrace();
43         }catch (IOException e) {
44             e.printStackTrace();
45         }finally{
46             if ( fis != null) {
47                 try {
48                     fis.close();
49                 } catch (IOException e) {
50                     e.printStackTrace();
51                 }
52             }
53         }
54 
55         return result ;
56     }
57 
58 
59 }
  • FileOutputStream 类的使用:将内容写入文件
 1 package com.app;
 2 import java.io.FileNotFoundException;
 3 import java.io.FileOutputStream;
 4 import java.io.IOException;
 5 
 6 public class A2 {
 7 
 8     public static void main(String[] args) {
 9         A2 a2 = new A2();
10 
11         //电脑d盘中的abc.txt 文档
12         String filePath = "D:/abc.txt" ;
13 
14         //要写入的内容
15         String content = "今天是2017/1/9,天气很好" ;
16         a2.writeFile( filePath , content  ) ;
17 
18     }
19 
20     /**
21      * 根据文件路径创建输出流
22      * @param filePath : 文件的路径
23      * @param content : 需要写入的内容
24      */
25     public void writeFile( String filePath , String content ){
26         FileOutputStream fos = null ;
27         try {
28             //1、根据文件路径创建输出流
29             fos  = new FileOutputStream( filePath );
30 
31             //2、把string转换为byte数组;
32             byte[] array = content.getBytes() ;
33             //3、把byte数组输出;
34             fos.write( array );
35 
36         } catch (FileNotFoundException e) {
37             e.printStackTrace();
38         }catch (IOException e) {
39             e.printStackTrace();
40         }finally{
41             if ( fos != null) {
42                 try {
43                     fos.close();
44                 } catch (IOException e) {
45                     e.printStackTrace();
46                 }
47             }
48         }
49     }
50 
51 
52 }

注意:

  1. 在实际的项目中,所有的IO操作都应该放到子线程中操作,避免堵住主线程。
  2. FileInputStream在读取文件内容的时候,我们传入文件的路径("D:/abc.txt"), 如果这个路径下的文件不存在,那么在执行readFile()方法时会报FileNotFoundException异常。
  3. FileOutputStream在写入文件的时候,我们传入文件的路径("D:/abc.txt"), 如果这个路径下的文件不存在,那么在执行writeFile()方法时, 会默认给我们创建一个新的文件。还有重要的一点,不会报异常。

 缓冲流

首先抛出一个问题,有了InputStream为什么还要有BufferedInputStream?

BufferedInputStreamBufferedOutputStream这两个类分别是FilterInputStreamFilterOutputStream的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。

我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!

同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReaderBufferedWriter两个类。

现在就可以回答在本文的开头提出的问题:

BufferedInputStreamBufferedOutputStream类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。

总结:

  • BufferedInputStream 是缓冲输入流。它继承于FilterInputStream

  • BufferedInputStream 的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持mark()标记reset()重置方法

  • BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。

BufferedInputStream API简介

源码关键字段分析

private static int defaultBufferSize = 8192;//内置缓存字节数组的大小 8KB
    
protected volatile byte buf[];  //内置缓存字节数组
    
protected int count;    //当前buf中的字节总数、注意不是底层字节输入流的源中字节总数
    
protected int pos;      //当前buf中下一个被读取的字节下标
    
protected int markpos = -1; //最后一次调用mark(int readLimit)方法记录的buf中下一个被读取的字节的位置
    
protected int marklimit;    //调用mark后、在后续调用reset()方法失败之前云寻的从in中读取的最大数据量、用于限制被标记后buffer的最大值

构造函数

BufferedInputStream(InputStream in) //使用默认buf大小、底层字节输入流构建bis 

BufferedInputStream(InputStream in, int size) //使用指定buf大小、底层字节输入流构建bis  

一般方法介绍

int available();  //返回底层流对应的源中有效可供读取的字节数      
  
void close();  //关闭此流、释放与此流有关的所有资源  
  
boolean markSupport();  //查看此流是否支持mark
  
void mark(int readLimit); //标记当前buf中读取下一个字节的下标  
  
int read();  //读取buf中下一个字节  
  
int read(byte[] b, int off, int len);  //读取buf中下一个字节  
  
void reset();   //重置最后一次调用mark标记的buf中的位子  
  
long skip(long n);  //跳过n个字节、 不仅仅是buf中的有效字节、也包括in的源中的字节 

BufferedOutputStream API简介

关键字段

protected byte[] buf;   //内置缓存字节数组、用于存放程序要写入out的字节  
  
protected int count;   //内置缓存字节数组中现有字节总数 

构造函数

BufferedOutputStream(OutputStream out); //使用默认大小、底层字节输出流构造bos。默认缓冲大小是 8192 字节( 8KB )
  
BufferedOutputStream(OutputStream out, int size);  //使用指定大小、底层字节输出流构造bos  

构造函数源码:

/**
  * Creates a new buffered output stream to write data to the
  * specified underlying output stream.
  * @param   out   the underlying output stream.
  */
 public BufferedOutputStream(OutputStream out) {
     this(out, 8192);
 }

 /**
  * Creates a new buffered output stream to write data to the
  * specified underlying output stream with the specified buffer
  * size.
  *
  * @param   out    the underlying output stream.
  * @param   size   the buffer size.
  * @exception IllegalArgumentException if size <= 0.
  */
 public BufferedOutputStream(OutputStream out, int size) {
     super(out);
     if (size <= 0) {
         throw new IllegalArgumentException("Buffer size <= 0");
     }
     buf = new byte[size];
 }

一般方法

//在这里提一句,`BufferedOutputStream`没有自己的`close`方法,
//当他调用父类`FilterOutputStrem`的方法关闭时,会间接调用自己实现的`flush`方法将buf中残存的字节flush到out中,
//再`out.flush()`到目的地中,DataOutputStream也是如此。
void flush(); 将写入bos中的数据flush到out指定的目的地中、注意这里不是flush到out中、因为其内部又调用了out.flush() write(byte b); 将一个字节写入到buf中 write(byte[] b, int off, int len); 将b的一部分写入buf中

那么什么时候flush()才有效呢?
答案是:当OutputStream是BufferedOutputStream时。

当写文件需要flush()的效果时,需要
FileOutputStream fos = new FileOutputStream("c:a.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
也就是说,需要将FileOutputStream作为BufferedOutputStream构造函数的参数传入,然后对BufferedOutputStream进行写入操作,才能利用缓冲及flush()。

查看BufferedOutputStream的源代码,发现所谓的buffer其实就是一个byte[]。
BufferedOutputStream的每一次write其实是将内容写入byte[],当buffer容量到达上限时,会触发真正的磁盘写入。
而另一种触发磁盘写入的办法就是调用flush()了。

1.BufferedOutputStreamclose()时会自动flush
2.BufferedOutputStream在不调用close()的情况下,缓冲区不满,又需要把缓冲区的内容写入到文件或通过网络发送到别的机器时,才需要调用flush.

用缓冲流复制文件

 1 package com.app;
 2 import java.io.BufferedInputStream;
 3 import java.io.BufferedOutputStream;
 4 import java.io.File;
 5 import java.io.FileInputStream;
 6 import java.io.FileNotFoundException;
 7 import java.io.FileOutputStream;
 8 import java.io.IOException;
 9 import java.io.InputStream;
10 import java.io.OutputStream;
11 
12 
13 public class A3 {
14 
15     public static void main(String[] args) throws IOException {
16 
17         String filePath = "F:/123.png" ;
18         String filePath2 = "F:/abc.png" ;
19         File file = new File( filePath ) ;
20         File file2 = new File( filePath2 ) ;
21         copyFile( file , file2 );
22 
23     }
24     
25     /**
26      * 复制文件
27      * @param oldFile
28      * @param newFile
29      */
30     public static void copyFile( File oldFile , File newFile){
31         InputStream inputStream = null ;
32         BufferedInputStream bufferedInputStream = null ;
33 
34         OutputStream outputStream = null ;
35         BufferedOutputStream bufferedOutputStream = null ;
36 
37         try {
38             inputStream = new FileInputStream( oldFile ) ;
39             bufferedInputStream = new BufferedInputStream( inputStream ) ;
40 
41             outputStream = new FileOutputStream( newFile ) ;
42             bufferedOutputStream = new BufferedOutputStream( outputStream ) ;
43 
44             byte[] b=new byte[1024];   //代表一次最多读取1KB的内容
45 
46             int length = 0 ; //代表实际读取的字节数
47             while( (length = bufferedInputStream.read( b ) )!= -1 ){
48                 //length 代表实际读取的字节数
49                 bufferedOutputStream.write(b, 0, length );
50             }
51             //缓冲区的内容写入到文件
52             bufferedOutputStream.flush();
53         } catch (FileNotFoundException e) {
54             e.printStackTrace();
55         }catch (IOException e) {
56             e.printStackTrace();
57         }finally {
58 
59             if( bufferedOutputStream != null ){
60                 try {
61                     bufferedOutputStream.close();
62                 } catch (IOException e) {
63                     e.printStackTrace();
64                 }
65             }
66 
67             if( bufferedInputStream != null){
68                 try {
69                     bufferedInputStream.close();
70                 } catch (IOException e) {
71                     e.printStackTrace();
72                 }
73             }
74             
75             if( inputStream != null ){
76                 try {
77                     inputStream.close();
78                 } catch (IOException e) {
79                     e.printStackTrace();
80                 }
81             }
82             
83             if ( outputStream != null ) {
84                 try {
85                     outputStream.close();
86                 } catch (IOException e) {
87                     e.printStackTrace();
88                 }
89             }
90 
91         }
92     }
93 }

如何正确的关闭流

在上面的代码中,我们关闭流的代码是这样写的。

finally {

            if( bufferedOutputStream != null ){
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if( bufferedInputStream != null){
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if( inputStream != null ){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if ( outputStream != null ) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
思考:在处理流关闭完成后,我们还需要关闭节点流吗?

让我们带着问题去看源码:

bufferedOutputStream.close();

   /**
 * Closes this input stream and releases any system resources
 * associated with the stream.
 * Once the stream has been closed, further read(), available(), reset(),
 * or skip() invocations will throw an IOException.
 * Closing a previously closed stream has no effect.
 *
 * @exception  IOException  if an I/O error occurs.
 */
public void close() throws IOException {
    byte[] buffer;
    while ( (buffer = buf) != null) {
        if (bufUpdater.compareAndSet(this, buffer, null)) {
            InputStream input = in;
            in = null;
            if (input != null)
                input.close();
            return;
        }
        // Else retry in case a new buf was CASed in fill()
    }
}
  • close()方法的作用
    1、关闭输入流,并且释放系统资源
    2、BufferedInputStream装饰一个 InputStream 使之具有缓冲功能,is要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会调用真正数据源对象的 close()方法。因此,可以只调用外层流的close方法关闭其装饰的内层流。

那么如果我们想逐个关闭流,我们该怎么做?

答案是:先关闭外层流,再关闭内层流。一般情况下是:先打开的后关闭,后打开的先关闭;另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b

看懂了怎么正确的关闭流之后,那么我们就可以优化上面的代码了,只关闭外层的处理流。

finally {

            if( bufferedOutputStream != null ){
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if( bufferedInputStream != null){
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

BufferedReader

  • 构造函数
BufferedReader(Reader in, int sz) //创建一个使用指定大小输入缓冲区的缓冲字符输入流。 

BufferedReader(Reader in) //创建一个使用默认大小输入缓冲区的缓冲字符输入流。
  • 方法
int  read()  //读取单个字符。
int  read(char[] cbuf, int off, int len)  //将字符读入数组的某一部分。
String  readLine()  //读取一个文本行。
boolean  ready()  //判断此流是否已准备好被读取。
void  reset()  //将流重置到最新的标记。
long  skip(long n)  //跳过字符。
void  close() //关闭该流并释放与之关联的所有资源。
void  mark(int readAheadLimit) //标记流中的当前位置。
boolean  markSupported() //判断此流是否支持 mark() 操作(它一定支持)。

BufferedWriter

  • 构造函数
BufferedWriter(Writer out, int sz) //创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

BufferedWriter(Writer out) //建一个使用默认大小输出缓冲区的缓冲字符输出流。
  • 方法
void  close()  // 关闭此流,但要先刷新它。
void  flush()  //刷新该流的缓冲。
void  newLine() //写入一个行分隔符。
void  write(char[] cbuf, int off, int len) //写入字符数组的某一部分。
void  write(int c) //写入单个字符。
void  write(String s, int off, int len) //写入字符串的某一部分。

实战演练
复制F盘里面的一个txt文本

 1 package com.app;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.BufferedWriter;
 5 import java.io.File;
 6 import java.io.FileNotFoundException;
 7 import java.io.FileReader;
 8 import java.io.FileWriter;
 9 import java.io.IOException;
10 import java.io.Reader;
11 import java.io.Writer;
12 
13 public class A4 {
14     public static void main(String[] args) {
15 
16         String filePath = "F:/123.txt" ;
17         String filePath2 = "F:/abc.txt" ;
18 
19         File file = new File( filePath ) ;
20         File file2 = new File( filePath2 ) ;
21         copyFile( file , file2 );
22     }
23 
24     private static void copyFile( File oldFile , File newFile ){
25         Reader reader = null ;
26         BufferedReader bufferedReader = null ;
27 
28         Writer writer = null ;
29         BufferedWriter bufferedWriter  = null ;
30         try {
31             reader = new FileReader( oldFile ) ;
32             bufferedReader = new BufferedReader( reader ) ;
33 
34             writer = new FileWriter( newFile ) ;
35             bufferedWriter = new BufferedWriter( writer ) ;
36 
37             String result = null ; //每次读取一行的内容
38             while (  (result = bufferedReader.readLine() ) != null ){
39                 bufferedWriter.write( result );  //把内容写入文件
40                 bufferedWriter.newLine();  //换行,result 是一行数据,所以没写一行就要换行 
41             }
42 
43             bufferedWriter.flush();  //强制把数组内容写入文件
44 
45         } catch (FileNotFoundException e) {
46             e.printStackTrace();
47         }catch (IOException e) {
48             e.printStackTrace();
49         }finally {
50             try {
51                 bufferedWriter.close();  //关闭输出流
52             } catch (IOException e) {
53                 e.printStackTrace();
54             }
55 
56             try {
57                 bufferedReader.close();  //关闭输入流
58             } catch (IOException e) {
59                 e.printStackTrace();
60             }
61         }
62     }
63 }

 转换流

InputStreamReader简介

InputStreamReader 是字符流Reader的子类,是字节流通向字符流的桥梁。你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。一次只读一个字符。

  • InputStreamReader构造函数
InputStreamReader(Inputstream  in) //创建一个使用默认字符集的 InputStreamReader。

InputStreamReader(Inputstream  in,Charset cs) //创建使用给定字符集的 InputStreamReader。

InputStreamReader(InputStream in, CharsetDecoder dec) //创建使用给定字符集解码器的 InputStreamReader。

InputStreamReader(InputStream in, String charsetName)  //创建使用指定字符集的 InputStreamReader。
  • 一般方法
void  close() // 关闭该流并释放与之关联的所有资源。

String  getEncoding() //返回此流使用的字符编码的名称。

int  read()  //读取单个字符。

int  read(char[] cbuf, int offset, int length) //将字符读入数组中的某一部分。

boolean  ready() //判断此流是否已经准备好用于读取。

OutputStreamWriter简介

OutputStreamWriter 是字符流Writer的子类,是字符流通向字节流的桥梁。每次调用 write()方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。一次只写一个字符。

  • OutputStreamWriter构造函数
OutputStreamWriter(OutputStream out) //创建使用默认字符编码的 OutputStreamWriter

OutputStreamWriter(OutputStream out, String charsetName) //创建使用指定字符集的 OutputStreamWriter。

OutputStreamWriter(OutputStream out, Charset cs) //创建使用给定字符集的 OutputStreamWriter。

OutputStreamWriter(OutputStream out, CharsetEncoder enc) //创建使用给定字符集编码器的 OutputStreamWriter。
  • 一般方法
void  write(int c)   //写入的字符长度

void  write(char cbuf[])  //写入的字符数组

void  write(String str)  //写入的字符串

void  write(String str, int off, int len)  //应该写入的字符串,开始写入的索引位置,写入的长度

void  close() //关闭该流并释放与之关联的所有资源。

需要注意的事项

InputStreamReaderOutputStreamWriter实现从字节流到字符流之间的转换,使得流的处理效率得到提升,但是如果我们想要达到最大的效率,我们应该考虑使用缓冲字符流包装转换流的思路来解决问题。比如:

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

实战演练,复制文本

 1 package com.app;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.InputStream;
 9 import java.io.InputStreamReader;
10 import java.io.OutputStream;
11 import java.io.OutputStreamWriter;
12 
13 public class A5 {
14 
15     public static void main(String[] args) {
16         String filePath = "F:/123.txt" ;
17         String filePath2 = "F:/abc.txt" ;
18         File file = new File( filePath ) ;
19         File file2 = new File( filePath2 ) ;
20         copyFile( file , file2 );
21 
22     }
23 
24     private static void copyFile( File oldFile , File newFile ){
25         InputStream inputStream = null ;
26         InputStreamReader inputStreamReader = null ;
27 
28         OutputStream outputStream = null ;
29         OutputStreamWriter outputStreamWriter = null ;
30 
31         try {
32             inputStream = new FileInputStream( oldFile ) ; //创建输入流
33             inputStreamReader = new InputStreamReader( inputStream ) ; //创建转换输入流
34 
35             outputStream = new FileOutputStream( newFile ) ; //创建输出流
36             outputStreamWriter = new OutputStreamWriter( outputStream ) ; //创建转换输出流
37 
38             int result = 0 ;
39 
40             while( (result = inputStreamReader.read()) != -1){  //一次只读一个字符
41                 outputStreamWriter.write( result ); //一次只写一个字符
42             }
43 
44             outputStreamWriter.flush();  //强制把缓冲写入文件
45 
46         } catch (FileNotFoundException e) {
47             e.printStackTrace();
48         }catch (IOException e) {
49             e.printStackTrace();
50         }finally{
51 
52             if ( outputStreamWriter != null) {
53                 try {
54                     outputStreamWriter.close();
55                 } catch (IOException e) {
56                     e.printStackTrace();
57                 }
58             }
59 
60             if ( inputStreamReader != null ) {
61                 try {
62                     inputStreamReader.close();
63                 } catch (IOException e) {
64                     e.printStackTrace();
65                 }
66             }
67         }
68 
69     }
70 }

PrintWriter

1、       类功能简介:

            打印字符流、用于将各种java数据一字符串的形式打印到底层字符输出流中、本身不会产生任何IOException、但是可以通过他的一个方法来查看是否抛出异常、可以指定autoFlush、若为true则当调用newLine、println、format方法时都会自动刷新、即将底层字符输出流out中的字符flush到目的地中。对PrintWriter有许多构造方法、和一般方法、但是这些方法都有个核心方法、构造方法:一个是传入字符流时的构造方法PrintWriter(Writer out, autoFlush)、一个是传入字节流时的构造方法PrintWriter(OutputStream out, boolean autoFlush)、一般方法:一个是write(char[] cbuf, int off, int len)、一个是write(String, int off, int len);

2、PrintWriter  API简介:

            A:关键字

protected Writer out;   传入的底层字符输出流  
  
private boolean autoFlush = false;      是否自动刷新  
  
private boolean trouble = false;    是否抛异常     
  
private Formatter formatter;    格式化类  
  
private PrintStream psOut = null;       字节打印流、用于checkError方法  

            B:构造方法

 
   PrintWriter(File file)       使用指定文件创建不具有自动行刷新的新 PrintWriter。   
     
   PrintWriter(File file, String csn)       创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。   
     
   PrintWriter(OutputStream out     根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。   
          
   PrintWriter(OutputStream out, boolean autoFlush)     通过现有的 OutputStream 创建新的 PrintWriter。   
     
PrintWriter(String fileName)    创建具有指定文件名称且不带自动行刷新的新 PrintWriter。   
  
PrintWriter(String fileName, String csn)    创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。   
  
PrintWriter(Writer out)     创建不带自动行刷新的新 PrintWriter。   
  
PrintWriter(Writer out, boolean autoFlush)      创建新 PrintWriter。   

            C:一般方法

  
 PrintWriter append(char c)    将指定字符添加到此 writer。   
     
PrintWriter append(CharSequence csq)     将指定的字符序列添加到此 writer。   
  
PrintWriter append(CharSequence csq, int start, int end)     将指定字符序列的子序列添加到此 writer。   
  
boolean checkError()     如果流没有关闭,则刷新流且检查其错误状态。   
  
protected  void clearError()     清除此流的错误状态。   
  
void close()     关闭该流并释放与之关联的所有系统资源。   
  
void flush()     刷新该流的缓冲。   
  
PrintWriter format(Locale l, String format, Object... args)      使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。   
  
PrintWriter format(String format, Object... args)        使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。   
  
void print(boolean b)         打印 boolean 值。   
  
void print(char c)        打印字符。   
  
void print(char[] s)      打印字符数组。   
  
void print(double d)      打印 double 精度浮点数。   
  
void print(float f)      打印一个浮点数。   
  
void print(int i)         打印整数。   
  
void print(long l)       打印 long 整数。   
  
void print(Object obj)       打印对象。   
  
void print(String s)     打印字符串。   
  
PrintWriter printf(Locale l, String format, Object... args)      使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。   
  
PrintWriter printf(String format, Object... args)        使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。   
  
void println()       通过写入行分隔符字符串终止当前行。   
  
void println(boolean x)       打印 boolean 值,然后终止该行。   
  
void println(char x)      打印字符,然后终止该行。   
  
void println(char[] x)       打印字符数组,然后终止该行。   
  
void println(double x)        打印双精度浮点数,然后终止该行。   
  
void println(float x)        打印浮点数,然后终止该行。   
  
void println(int x)       打印整数,然后终止该行。   
  
void println(long x)      打印 long 整数,然后终止该行。   
  
void println(Object x)       打印 Object,然后终止该行。   
  
void println(String x)       打印 String,然后终止该行。   
  
protected  void setError()       指示已发生错误。   
  
void write(char[] buf)        写入字符数组。   
  
void write(char[] buf, int off, int len)      写入字符数组的某一部分。   
  
void write(int c)         写入单个字符。   
  
void write(String s)     写入字符串。   
  
void write(String s, int off, int len)       写入字符串的某一部分。

3、源码分析

 
  1 package com.chy.io.original.code;  
  2   
  3 import java.io.File;  
  4 import java.io.FileNotFoundException;  
  5 import java.io.IOException;  
  6 import java.io.InterruptedIOException;  
  7 import java.io.UnsupportedEncodingException;  
  8 import java.util.Formatter;  
  9 import java.util.IllegalFormatException;  
 10 import java.util.Locale;  
 11   
 12 /** 
 13  * 
 14  * @version     1.1, 13/11/17 
 15  * @author      andyChen 
 16  */  
 17   
 18 public class PrintWriter extends Writer {  
 19   
 20     /** 
 21      * 被PrintWriter装饰的底层Writer、OutputStream实现类out 
 22      */  
 23     protected Writer out;  
 24   
 25     //是否自动刷新  
 26     private boolean autoFlush = false;  
 27       
 28     //是否抛异常  
 29     private boolean trouble = false;  
 30       
 31     //格式化类  
 32     private Formatter formatter;  
 33       
 34     //字节打印流、用于checkError方法  
 35     private PrintStream psOut = null;  
 36   
 37     /** 
 38      * 换行符 
 39      */  
 40     private String lineSeparator;  
 41   
 42     /** 
 43      * 根据传入的Writer实现类out创建PrintWriter、不具有自动flush功能。 
 44      */  
 45     public PrintWriter (Writer out) {  
 46         this(out, false);  
 47     }  
 48   
 49     /** 
 50      * 创建PrintWriter、指定autoFlush、并且根据平台不同初始化linSeparator 
 51      */  
 52     public PrintWriter(Writer out,  
 53                boolean autoFlush) {  
 54         super(out);  
 55         this.out = out;  
 56         this.autoFlush = autoFlush;  
 57         lineSeparator = (String) java.security.AccessController.doPrivileged(  
 58                    new sun.security.action.GetPropertyAction("line.separator"));  
 59     }  
 60   
 61     /** 
 62      * 根据OutputStream out创建PrintWriter。不具有自动flush功能。 
 63      */  
 64     public PrintWriter(OutputStream out) {  
 65         this(out, false);  
 66     }  
 67   
 68     /** 
 69      * 根据底层OutputStream out创建pw、指定是否具有autoFlush功能。 
 70      * 本质只是将out转换成writer、然后为其添加缓冲功能、再用PrintWriter装饰一下。 
 71      * 如果传入的是PrintStream、则赋给全局变量psOut 
 72      */  
 73     public PrintWriter(OutputStream out, boolean autoFlush) {  
 74         this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);  
 75       
 76         // save print stream for error propagation  
 77         if (out instanceof java.io.PrintStream) {   
 78             psOut = (java.io.PrintStream) out;  
 79         }  
 80     }  
 81   
 82     /** 
 83      * 根据传入的文件名构造不具有自动flush功能的PrintWriter。 
 84      * 本质还是根据文件名创建文件字节输出流out、再将out转换成writer、对其添加缓冲功能、最后用pw装饰。 
 85      */  
 86     public PrintWriter(String fileName) throws FileNotFoundException {  
 87         this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),  
 88              false);  
 89     }  
 90   
 91     /** 
 92      * 与上面一样、只是多了一个可以使用指定编码读取的功能。 
 93      */  
 94     public PrintWriter(String fileName, String csn)  
 95         throws FileNotFoundException, UnsupportedEncodingException  
 96         {  
 97         this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), csn)),  
 98              false);  
 99     }  
100   
101     /** 
102      * 根据传入的文件构造不具有自动flush功能的PrintWriter。 
103      * 本质还是根据文件名创建文件字节输出流out、再将out转换成writer、对其添加缓冲功能、最后用pw装饰。 
104      */  
105     public PrintWriter(File file) throws FileNotFoundException {  
106         this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),  
107              false);  
108     }  
109   
110     /** 
111      * 与上面一样、只是多了一个可以使用指定编码读取的功能。 
112      */  
113     public PrintWriter(File file, String csn)  
114         throws FileNotFoundException, UnsupportedEncodingException  
115         {  
116         this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), csn)),  
117              false);  
118     }  
119   
120     /** 检测底层流是否关闭 */  
121     private void ensureOpen() throws IOException {  
122         if (out == null)  
123             throw new IOException("Stream closed");  
124     }  
125   
126     /** 
127      * flush底层流 
128      */  
129     public void flush() {  
130         try {  
131             synchronized (lock) {  
132             ensureOpen();  
133             out.flush();  
134             }  
135         }  
136         catch (IOException x) {  
137             trouble = true;  
138         }  
139     }  
140   
141     /** 
142      * 关闭此流、释放与此流有关的所有资源 
143      */  
144     public void close() {  
145         try {  
146             synchronized (lock) {  
147             if (out == null)  
148                 return;  
149             out.close();  
150             out = null;  
151             }  
152         }  
153         catch (IOException x) {  
154             trouble = true;  
155         }  
156     }  
157   
158     /** 
159      * 同PrintStream一样、不会抛出IO异常、出现异常时只会自己内部消化掉、 
160      * 可以通过此方法来查看print过程中是否有异常出现。 
161      */  
162     public boolean checkError() {  
163         if (out != null) {  
164             flush();  
165         }  
166         if (out instanceof java.io.PrintWriter) {  
167             PrintWriter pw = (PrintWriter) out;   
168             return pw.checkError();  
169         } else if (psOut != null) {  
170             return psOut.checkError();  
171         }  
172         return trouble;  
173     }  
174   
175     /** 
176      * 可以显示的设置print产生异常。 
177      */  
178     protected void setError() {  
179         trouble = true;  
180     }  
181   
182     /** 
183      * 清除异常信息 
184      */  
185     protected void clearError() {  
186         trouble = false;  
187     }  
188    
189   
190     /** 
191      * 写入一个字符、 
192      * 这里产生IOException的原因是此方法是继承自Writer 
193      */  
194     public void write(int c) {  
195         try {  
196             synchronized (lock) {  
197             ensureOpen();  
198             out.write(c);  
199             }  
200         }  
201         catch (InterruptedIOException x) {  
202             Thread.currentThread().interrupt();  
203         }  
204         catch (IOException x) {  
205             trouble = true;  
206         }  
207     }  
208   
209     /** 
210      * 将从下标off开始、len个字符写入到out中。 
211      * 这里也许会有个疑问?当传入的out是OutputStream时还能使用 
212      * out.write(char[] buf, int off, int len)吗? 
213      * 可以:当传入OutputStream时、调用的构造方法内部会将OutputStream 
214      * 包装成OutputStreamWriter、同时使用BufferedWriter来封装、最后再用PrintWriter来装饰。 
215      * 这里的out表示的已经变成BufferedWriter。 
216      */  
217     public void write(char buf[], int off, int len) {  
218         try {  
219             synchronized (lock) {  
220             ensureOpen();  
221             out.write(buf, off, len);  
222             }  
223         }  
224         catch (InterruptedIOException x) {  
225             Thread.currentThread().interrupt();  
226         }  
227         catch (IOException x) {  
228             trouble = true;  
229         }  
230     }  
231   
232     /** 
233      * 将一个字符数组中所有字符写入到out中 
234      */  
235     public void write(char buf[]) {  
236         write(buf, 0, buf.length);  
237     }  
238   
239     /** 
240      * 将s的一部分写入out中 
241      */  
242     public void write(String s, int off, int len) {  
243         try {  
244             synchronized (lock) {  
245             ensureOpen();  
246             out.write(s, off, len);  
247             }  
248         }  
249         catch (InterruptedIOException x) {  
250             Thread.currentThread().interrupt();  
251         }  
252         catch (IOException x) {  
253             trouble = true;  
254         }  
255     }  
256   
257     /** 
258      * 将String写入out中 
259      */  
260     public void write(String s) {  
261         write(s, 0, s.length());  
262     }  
263   
264     /** 
265      * 将一个换行符写入out中、如果设置了自动刷新、则刷新out。 
266      */  
267     private void newLine() {  
268         try {  
269             synchronized (lock) {  
270             ensureOpen();  
271             out.write(lineSeparator);  
272             if (autoFlush)  
273                 out.flush();  
274             }  
275         }  
276         catch (InterruptedIOException x) {  
277             Thread.currentThread().interrupt();  
278         }  
279         catch (IOException x) {  
280             trouble = true;  
281         }  
282     }  
283   
284   
285     /** 
286      * 将一个boolean写入out中、不管有没有设置autoFlush、都不会自动flush 
287      */  
288     public void print(boolean b) {  
289         write(b ? "true" : "false");  
290     }  
291   
292     /** 
293      * 将一个char写入out中、  
294      */  
295     public void print(char c) {  
296         write(c);  
297     }  
298   
299     /** 
300      * 将一个int写入out中、  
301      */  
302     public void print(int i) {  
303         write(String.valueOf(i));  
304     }  
305   
306     /** 
307      * 将一个long写入out中、  
308      */  
309     public void print(long l) {  
310         write(String.valueOf(l));  
311     }  
312   
313     /** 
314      * 将一个float写入out中、 rinted 
315      */  
316     public void print(float f) {  
317         write(String.valueOf(f));  
318     }  
319   
320     /** 
321      * 将一个double写入out中、  
322      */  
323     public void print(double d) {  
324         write(String.valueOf(d));  
325     }  
326   
327     /** 
328      *将一个char[]写入out中、  
329      */  
330     public void print(char s[]) {  
331         write(s);  
332     }  
333   
334     /** 
335      * 将一个String写入out中、  
336      */  
337     public void print(String s) {  
338         if (s == null) {  
339             s = "null";  
340         }  
341         write(s);  
342     }  
343   
344     /** 
345      * 将一个Object写入out中、  
346      */  
347     public void print(Object obj) {  
348         write(String.valueOf(obj));  
349     }  
350   
351     /* Methods that do terminate lines */  
352   
353     /** 
354      * 将一个换行符写入out中、  
355      */  
356     public void println() {  
357         newLine();  
358     }  
359   
360     /** 
361      * 将一个换行符写入out中、  
362      */  
363     public void println(boolean x) {  
364         synchronized (lock) {  
365             print(x);  
366             println();  
367         }  
368     }  
369   
370     /** 
371      * 将一个换行符写入out中、  
372      */  
373     public void println(char x) {  
374         synchronized (lock) {  
375             print(x);  
376             println();  
377         }  
378     }  
379   
380     /** 
381      * 将一个换行符写入out中、  
382      */  
383     public void println(int x) {  
384         synchronized (lock) {  
385             print(x);  
386             println();  
387         }  
388     }  
389   
390     /** 
391      * 将一个换行符写入out中、  
392      */  
393     public void println(long x) {  
394         synchronized (lock) {  
395             print(x);  
396             println();  
397         }  
398     }  
399   
400     /** 
401      * 将一个换行符写入out中、  
402      */  
403     public void println(float x) {  
404         synchronized (lock) {  
405             print(x);  
406             println();  
407         }  
408     }  
409   
410     /** 
411      * 将一个换行符写入out中、  
412      */  
413     public void println(double x) {  
414         synchronized (lock) {  
415             print(x);  
416             println();  
417         }  
418     }  
419   
420     /** 
421      * 将一个换行符写入out中、  
422      */  
423     public void println(char x[]) {  
424         synchronized (lock) {  
425             print(x);  
426             println();  
427         }  
428     }  
429   
430     /** 
431      * 将一个换行符写入out中、  
432      */  
433     public void println(String x) {  
434     synchronized (lock) {  
435         print(x);  
436         println();  
437     }  
438     }  
439   
440     /** 
441      * 将一个换行符写入out中、  
442      */  
443     public void println(Object x) {  
444         String s = String.valueOf(x);  
445         synchronized (lock) {  
446             print(s);  
447             println();  
448         }  
449     }  
450   
451     /** 
452      *  使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。 
453      */  
454     public PrintWriter printf(String format, Object ... args) {  
455         return format(format, args);  
456     }  
457   
458     /** 
459      *  使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。 
460      */  
461     public PrintWriter printf(Locale l, String format, Object ... args) {  
462         return format(l, format, args);  
463     }  
464   
465     /** 
466      *使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。 
467      */  
468     public PrintWriter format(String format, Object ... args) {  
469         try {  
470             synchronized (lock) {  
471             ensureOpen();  
472             if ((formatter == null)  
473                 || (formatter.locale() != Locale.getDefault()))  
474                 formatter = new Formatter(this);  
475             formatter.format(Locale.getDefault(), format, args);  
476             if (autoFlush)  
477                 out.flush();  
478             }  
479         } catch (InterruptedIOException x) {  
480             Thread.currentThread().interrupt();  
481         } catch (IOException x) {  
482             trouble = true;  
483         }  
484         return this;  
485     }  
486   
487     /** 
488      *使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。 
489      */  
490     public PrintWriter format(Locale l, String format, Object ... args) {  
491         try {  
492             synchronized (lock) {  
493             ensureOpen();  
494             if ((formatter == null) || (formatter.locale() != l))  
495                 formatter = new Formatter(this, l);  
496             formatter.format(l, format, args);  
497             if (autoFlush)  
498                 out.flush();  
499             }  
500         } catch (InterruptedIOException x) {  
501             Thread.currentThread().interrupt();  
502         } catch (IOException x) {  
503             trouble = true;  
504         }  
505         return this;  
506     }  
507   
508     /** 
509      * 将一个有序字符序列追加到out中 
510      */  
511     public PrintWriter append(CharSequence csq) {  
512         if (csq == null)  
513             write("null");  
514         else  
515             write(csq.toString());  
516             return this;  
517     }  
518   
519       
520     /** 
521      * 将一个有序字符序列一部分追加到out中  
522      */  
523     public PrintWriter append(CharSequence csq, int start, int end) {  
524         CharSequence cs = (csq == null ? "null" : csq);  
525         write(cs.subSequence(start, end).toString());  
526             return this;  
527     }  
528       
529     /** 
530      * 将一个字符追加到out中 
531      */  
532     public PrintWriter append(char c) {  
533         write(c);  
534         return this;  
535     }  
536 }  

4、实例演示:

 1 package com.chy.io.original.test;  
 2   
 3 import java.io.BufferedReader;  
 4 import java.io.File;  
 5 import java.io.FileOutputStream;  
 6 import java.io.IOException;  
 7 import java.io.PrintWriter;  
 8   
 9 import com.chy.io.original.code.FileWriter;  
10 import com.chy.io.original.utils.StudentDTO;  
11   
12 @SuppressWarnings("all")  
13 public class PrintWriterTest {  
14       
15     private final static String fileName = "D:\pw.txt";  
16     private final static File file = new File(fileName);  
17     private final static String targetStr = "马心心";  
18       
19     public static void showPrintWriterconstructor() throws IOException{  
20           
21           
22         //下面这一组个构造方法效果一样、使用默认编码  
23         PrintWriter pw1 = new PrintWriter(new FileWriter(file));  
24         PrintWriter pw2 = new PrintWriter(new FileWriter(fileName));  
25         PrintWriter pw3 = new PrintWriter(file);  
26         PrintWriter pw4 = new PrintWriter(fileName);  
27           
28         //下面这组构造方法效果一样、具有自动刷新功能  
29         PrintWriter pw5 = new PrintWriter(new FileWriter(file), true);  
30         PrintWriter pw6 = new PrintWriter(new FileWriter(fileName), true);  
31         PrintWriter pw7 = new PrintWriter(new FileOutputStream(file), true);  
32         PrintWriter pw8 = new PrintWriter(new FileOutputStream(fileName), true);  
33           
34           
35         //下面这一组构造方法效果一样、使用指定的GBK编码  
36         PrintWriter pw9 = new PrintWriter(file, "GBK");  
37         PrintWriter pw10 = new PrintWriter(fileName, "GBK");  
38     }  
39       
40       
41     /** 
42      * 这里只测试一下向文件流中打印一个中文字符串、然后读取出来查看乱码情况、 
43      * 和写入一个Object对象、再读取出来以字符串的形式打印出来。 
44      */  
45     public static void testPrintWriterDefault() throws IOException{  
46         PrintWriter pw = new PrintWriter(file);  
47         pw.println(targetStr);  
48           
49         StudentDTO student = new StudentDTO(1,"陈华应");  
50         pw.println(student);  
51         pw.close();  
52           
53         BufferedReader br = new BufferedReader(new java.io.FileReader(file));  
54         String str;  
55         while((str = br.readLine()) != null){  
56                 System.out.println(str);  
57         }  
58         br.close();  
59     }  
60       
61     /** 
62      * 构造打印流时使用指定GBK编码 
63      * @throws IOException 
64      */  
65     public static void testPrintWriterGBK() throws IOException{  
66         PrintWriter pw = new PrintWriter(file, "GBK");  
67         pw.println(targetStr);  
68           
69         StudentDTO student = new StudentDTO(1,"陈华应");  
70         pw.println(student);  
71         pw.close();  
72           
73         BufferedReader br = new BufferedReader(new java.io.FileReader(file));  
74         String str;  
75         while((str = br.readLine()) != null){  
76                 System.out.println(str);  
77         }  
78         br.close();  
79     }  
80       
81       
82     public static void main(String[] args) throws IOException {  
83         //testPrintWriterDefault();  
84         testPrintWriterGBK();  
85     }  
86       
87 }  

总结:

            PrintWriter与PrintStream很类似、要注意的就是一个是自动刷新的问题、另一个就是PrintStream可以实现将字节写入底层流中write(intb)     write(byte[] b, int off, int len)、而PrintWriter没有相关方法来直接将字节写入到out中、还有一点就不管是PrintWriter还是PrintStream、他写入到底层流中的java基础类型或者java对象都是以字符串的形式写入的、所以当我们读取之后要做一些处理、而关于print(Object)只是将Object的表示此对象的Object.toString()这个结果写入到out中、所以读取出来并不是强转一下就可以使用、要想操作java基础类型和对象、应该使用对应的流DataInputStream/DataOutputStream        ObjectInputStream/ObjectOutputStream。

原文地址:https://www.cnblogs.com/syp172654682/p/8007407.html