学习IO部分的知识后,被处理流的各种类搞得有点乱,所以得写篇文章总结下。IO包里面有很多输入输出类,一般我们是通过控制流来控制输入输出的。
    IO包里面对于控制流分为两大类,一类是字节流,一类是字符流。字符流的两个大类是Reader,Writer,字节流的是InputSream和OutputStream。通过这两个流以及其子类,我们可以控制系统与程序的输入输出。
1.  从类名看流(以字符流为例)
1.1 根据命名规则来选择类


      由上图可以看出Reader、Writer类有很多子类,每个子类的名称构造都是“功能+对应的类别”,这就是IO包类命名的规则。所以我们可以从类名的后一部分看出它是属于字符流还是字节流的,然后从前一部分看出它的功能是什么。根据这个规则,我们可以选择合适的类来处理流。
1.2 各类的作用以及关系
   IO包的类不少,但是他们之间都是有关系的,想Reader用来读取字节流,其子类是根据要读取不同的源而需要不同的功能而划分的。比如Reader的子类BufferedReader就是提供缓冲读取,提高效率。CharArrayReader则是把字符数组当做源来读取流数据,FilterReader则是对已过滤流的读取,OutputStreamReader则是转换流,是字符流与字节流之间的桥梁,因为他可以把字节流当做源,读取数据,当做字节流输出,所以它在初始化可以选择编码,如果不设置,则设置为系统的默认编码。这个类会被经常用到。而StringReader跟CharArrayReader类似,只是他是把字符串当做源来读数据。另外常用到的还有InputSreamReader的子类FileReader,它可以直接从File对象读取数据,一般能直接操作文件的类都会比较常用到。
至于Writer类跟Reader类似,但是它有个功能比较强大的子类,就是PrintWriter。该类在开发中很经常用到。


       上图是PrintWriter的构造方法,可以看出他面向的目的流是多样化的,可以直接往File对象(同时可以为其采用相应的字符集)、OutputStream输出流(可设置是否每次写入后自动flush)写数据,可以通过字符串构造的文件路径名建立文件并写写入,同时可以传入Writer对象,增强Writer的功能。而且它还可以使用print,printf,println往目的流输入各种数据。一下是其使用的一个案例:
package   tst.IODemo;
import   java.io. * ;
publicclass   PrintWriterDemo {
publicstaticvoid   main(String[] args)   throws   IOException {
         writeMethods ();
}
publicstaticvoid   writeMethods()   throws   FileNotFoundException {
        PrintWriter pw =new   PrintWriter( "d:\\a.txt" );    //可以写到指定的文件里
char [] buf = { 'a' , 'b' , 'c' };
        pw.write(buf);      //可以写入字符数据
        pw.println( "123" );   //可以直接用 println 方法往目的流输入数据
        pw.print( "456" );
        pw.println();    //写入一个换行符
        pw.write( 49 );    //可以写入整数,但是写到文本里会根据ASCII表写入相应的字符
        pw.write( "weuroei" );   //可以直接写字符串
        pw.format( "%s" ,   "wcc" );    //可以给字符串规定格式写入
        pw.close();
    }
}    各类的关系:虽然上面讲的都是字符流,但是他们的道理是相同的,只是一个是以字符形式读取和写入,而一个是以字节形式读取写入。他们的区别是源或目的不同,所以他们的构造方法传入的参数不同,会根据是什么作用而传入何种参数,比如StringReader的构造参数会传入字符串,而CharArrayReader传入的则是字节数组。大体方法相同,但是不同功能的类会有区别于其他类的特有功能,比如处理字符串的,处理字节数组的,缓冲的。
2. 程序实践
2.1 实践中使用的思路总结
    一般该类的使用前,首先判断是要操作字节流还是字符流,再者确定是要读取或者是写入。确定后一般分三层进行,必须要有源流或是目的流,然后根据该流是文件的,字符串的还是字节的。接下来就是是否需要字节流与字符流之间的转换,需要则选InputSreamReader、OutputStreamWriter。然后就考虑读写效率的提高,这个就可以选择带Buffered的了。还有其他各种各样的功能,比如合并流的SequenceInputStream、处理音频的AudioInputStream等等,记住大概使用思路,使用时忘记不懂再查API就OK了。
2.2 各种程序实践
(1)FileReader的使用(Reader-InputSreamReader子类)
由该类的名字我们可以知道他是操作文件的,可以直接打开文件,读取文件的数据到输入流。
package  tst.IODemo;
import  java.io.*;
publicclass  FileReaderDemo {
publicstaticvoid  main(String[] args)  throws  IOException {
        FileReader fr =new  FileReader( "WriterException.java" );
char [] buf =newchar [1024];
int  num;
while ((num = fr.read(buf)) !=-1) {
            System. out .print( new  String(buf,0,num));
        }        
        fr.close();
    }
}(2)拥戴Buffered相关类提高FileReader和FileWriter读写效率:
    该时间里面写用BufferedWriter但是自己根据BufferedReader的原理,自己写了一个MyBufReader模拟BufferedReader的运行,结果同样可读取数据。该例子里的BufferedReader和BufferedWriter主要是用于提高FileReader和FileWriter两者个工作效率,读N个数据放到缓存,再一次性写到输出流,避免读一个字节写一次,提高效率。
package  tst.IODemo;
import  java.io.*;
publicclass  BufCopyText {
publicstaticvoid  main(String[] args) {
        MyBufReader fr =  null ;
        BufferedWriter fw =  null ;
try  {
            fr =new  MyBufReader( new  FileReader( "E:\\学习资源\\cmd.exe" ));
            fw =new  BufferedWriter( new  FileWriter( "E:\\学习资源\\cmd1.exe" ));
            String buf =  null ;
while ((buf = fr.myReadLine()) !=  null ) {
                fw.write(buf);
                fw.newLine();
                fw.flush();
            }
        }  catch  (IOException e) {
thrownew  RuntimeException( "读写失败!" );
        }  finally  {
if  (fr !=  null ) {
try  {
                    fr.close();
                }  catch  (IOException e) {
thrownew  RuntimeException( "读缓存关闭失败。" );
                }
            }
if  (fw !=  null ) {
try  {
                    fw.close();
                }  catch  (IOException e) {
thrownew  RuntimeException( "写缓存关闭失败。" );
                }
            }
        }    
    }
}
/**
 * 该类模拟BufferedReader的工作原理,作用相同。
 * */
class  MyBufReader {
    FileReader  r ;
int   buf ;
public  MyBufReader(FileReader r) {
this . r  = r;
    }
public  String myReadLine()  throws  IOException {
        StringBuilder sb =new  StringBuilder();
while (( buf  =  r .read()) !=-1) {
if ( buf  =='\r' ) {
continue ;
            }
if ( buf  =='\n' ) {
return  sb.toString();
            }
            sb.append(( char ) buf );
        }
if (sb.length() !=0) {
return  sb.toString();
        } 
return   null ;
    }
publicvoid  close()  throws  IOException {
         r .close();
    }
}(3)接下来则是字节流的实践,从键盘往文件里面写数据:
该例子实现的功能是在控制台通过键盘输入数据,然后通过FileOutputStream打开一个文件,通过OutputStreamWriter把输入的字节流转换成字符流输出到a.txt文本,同时运用BufferedWriter提高写入效率。
package  tst.IODemo;
import  java.io.*;
publicclass  OutputStreamWriterDemo {
publicstaticvoid  main(String[] args)  throws  IOException {
        BufferedReader br =new  BufferedReader( new  InputStreamReader(System. in ));
        BufferedWriter bw =new  BufferedWriter( new  OutputStreamWriter( new  FileOutputStream( "a.txt" ), "UTF-8" ));
        String buf =  null ;
while ((buf = br.readLine()) !=  null ) {
if (buf.equals( "over" ))  break ;    //当输入“over”时则写入结束,over不写入文件
            bw.write(buf);
            bw.newLine();
        }        
        br.close();
        bw.close();        
    }
}(4)读取一个文件的内容,并输出到控制台上:
     读取一个文件,把内容输出到控制台上,同时获取开始和结束时间,比较BufferedInputSream和BufferedOutputStream与DataInputStream和DataOutputStream的效率,果然发现Buffered类的效率搞了将近一倍,使用Buffered类的我们不需要去控制他的缓冲区,只要跟其他类一样正常使用就行,他内部自己有使用缓冲方法的机制。
package  tst.IODemo;
import  java.io.*;
publicclass  BufInputStream {
publicstaticvoid  main(String[] args)  throws  IOException {
          BufferedInputStream bis =new  BufferedInputStream( new  FileInputStream( "SysInfo.txt" ));
          BufferedOutputStream bop =new  BufferedOutputStream(System. out );
//        DataInputStream  bis  = new DataInputStream(new FileInputStream("SysInfo.txt"));
//        DataOutputStream  bop  = new DataOutputStream(System.out);
int  buf;
long  start = System. currentTimeMillis ();
while ((buf = bis.read()) !=-1) {
            bop.write(buf);
            bop.flush();            
         }        
long  end = System. currentTimeMillis ();
          System. out .println((end - start) +"毫秒" );
          bis.close();
          bop.close();
    }
}(5)合并流
     把多个输入流合并成一个大的输入流,然后一起写到输出流中。
package tst.IODemo;
import java.io.*;
import java.util.*;
publicclass SequenceDemo {
publicstaticvoid main(String[] args) throws IOException {
        Vector<FileInputStream> v =new Vector<FileInputStream>();
        PrintWriter pw;
try {
//从当前目录读取三个txt文件1.txt,2.txt,3.txt
            FileInputStream fis1 =new FileInputStream("D:\\1.txt");
            FileInputStream fis2 =new FileInputStream("D:\\2.txt");
            FileInputStream fis3 =new FileInputStream("D:\\3.txt");
//创建4.txt文件
            pw =new PrintWriter(new FileOutputStream("D:\\4.txt"));
//把3个文件存入向量集合v中,是为了生成下面的Enumeration<FileInputStream>集合
            v.add(fis1);
            v.add(fis2);
            v.add(fis3);
        } catch(FileNotFoundException e) {
thrownew RuntimeException("文件打开失败。请查询文件是否存在!");
        }
        Enumeration<FileInputStream> e = v.elements();
//SequenceInputStream构造函数只要两个方法,一个是传入两个InputSream,这只能合并两个流,
//想要合并多个流,则需要传入一个Enumeration集合才可以。
        SequenceInputStream sis =new SequenceInputStream(e);
int buf;
while((buf = sis.read()) !=-1) {
            pw.write(buf);                  //三个流一起写入到输出流
        }   
        sis.close();
        pw.close();
        System.out.println("Done!");
    }
}转载于:https://blog.51cto.com/cthhqu/1262101