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

115848757.png

115848508.png

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