黑马程序员——IO(一)——概述、字符流、字节流、流操作规律 .

时间:2023-02-25 19:32:33

黑马程序员--IO(一)--概述、字符流、字节流、流操作规律

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、概述

1、IO流(Input Output):用来处理设备之间的数据传输。java对数据的操作通过流的方式。java用于操作流的对象都在IO包中。
分类:
流按流分为:输入流和输出流。将外设中的数据读取到内存中:输入。将内存中数据写入到外设中:输出。
流按操作数据分为:字节流和字符流。
PS:
流只能操作数据,不能操作文件。
2、常用基类
字节流的抽象基类:InputStream  OutputStream
字符流的抽象基类:Reader  Writer
PS:
由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:Reader的子类FileReader。

二、字符流

1、简述
由于字节流是单字节处理方式,当处理汉字这样双字节字符时,它就显得不太方便,这时我们通过字节流+编码表的方式获取字符流。
即让字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。来便捷文字操作。
字符流的抽象基类:Reader  Writer
2、字符流的读写
读取方式有两种:
read():一次读一个字符。而且会自动往下读。
read(char[] cbuf):将字符读入数组。更优。
[java] view plaincopyprint?
  1. //字符流读取数据方式有两种   
  2. import java.io.*;  
  3. class FileReaderDemo  
  4. {  
  5.     public static void main(String[] args)throws IOException  
  6.     {  
  7.         //创建一个文件读取流对象和指定名称的文件相关联。  
  8.         //要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException  
  9.         FileReader fr = new FileReader("demo.txt");  
  10.   
  11.         int ch = 0;  
  12.   
  13.         //第一种方式:调用读取流对象的read()方法,它一次读一个字符。而且会自动往下读。  
  14.         while((ch=fr.read())!=-1)     
  15.         {  
  16.             System.out.println("ch="+(char)ch);  
  17.         }  
  18.   
  19.         FileReader fr2 = new FileReader("demo.txt");  
  20.   
  21.         //第二种方式:read(char[] cbuf):将字符读入数组。以数组形式读取,一次性输出。更优。  
  22.         char[] buf = new char[1024];  
  23.   
  24.         int num = 0;  
  25.         while((num=fr2.read(buf))!=-1)  
  26.         {  
  27.             System.out.println(new String(buf,0,num));  
  28.         }  
  29.   
  30.         fr.close();  
  31.     }  
  32. }  

[java] view plaincopyprint?
  1. /* 
  2. 字符流:写入、续写、异常处理。 
  3. 需求:在硬盘上,创建一个文件并写入一些文字数据。处理文字数据首先要考虑字符流。 
  4.  
  5. FileWriter:专门用于操作文件的Writer子类对象。后缀名是父类名;前缀名是流对象功能。 
  6. FileWriter会将文件创建到指定目录下,如果该目录下已有同名文件,将被覆盖。 
  7. flush():用于对文件刷新。作用:将写入字符流输出到指定文件中。 
  8. close和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。 
  9. */  
  10. import java.io.*;  
  11. class FileWriterDemo  
  12. {  
  13.     public static void main(String[] args)  
  14.     {  
  15.         FileWriter fw = null;  
  16.         try  
  17.         {   //创建一个FileWriter对象。初始化时必须明确被操作文件。在构造函数中加入true,可实现对文件续写。  
  18.             fw= new FileWriter("demo.txt",true);  
  19.   
  20.             //调用Write方法,字符串写入到流中。   
  21.             fw.write("---www\r\neeeee");  
  22.         }  
  23.         //处理异常   
  24.         catch(IOException e)  
  25.         {  
  26.             System.out.println("wrong:"+e.toString());  
  27.         }  
  28.         //用finally关闭流,可防止代码异常时流无法关闭的问题。   
  29.         finally  
  30.         {  
  31.             try  
  32.             {  
  33.                 if(fw!=null)  
  34.                     //关闭流资源,但是关闭前会刷新一次内部的缓冲的数据。  
  35.                     fw.close();  
  36.             }  
  37.             catch(IOException e)  
  38.             {  
  39.                 System.out.println("wrong too:"+e.toString());  
  40.             }  
  41.         }  
  42.     }  
  43. }  

3、字符流的缓冲区-->BufferedWriter、BufferedReader
a 作用:在流的基础上对流的功能进行了增强。所以缓冲区创建之前,必须有对应的流对象。
b 原理:将数据以数组形式储存在内部,最后一次性取出。减少了数据的在内存上的读取频率,提高效率。
newLine():写入换行,返回数据及回车符。属于BufferedWriter类。可以跨平台。
readLine():读取一行数据。属于BufferedReader类。
原理:使用read方法,缓冲读取到的字符并判读换行标记,将标记前的缓冲数据变成字符串返回。

[java] view plaincopyprint?
  1. //缓冲区的应用:通过缓冲区复制一个java文件   
  2. import java.io.*;  
  3. class CopyByBuf  
  4. {  
  5.     public static void main(String[] args)  
  6.     {  
  7.         //缓冲区要先初始化,不然作用域只在try{}范围内。  
  8.         BufferedWriter bufw = null;  
  9.         BufferedReader bufr = null;  
  10.         try  
  11.         {  
  12.             //创建字符流缓冲区并加载字符流文件   
  13.             bufr = new BufferedReader(new FileReader("helloworld.java"));  
  14.             bufw = new BufferedWriter(new FileWriter("hello_copy.txt"));  
  15.   
  16.             String line = null;  
  17.           
  18.             //写入信息   
  19.             while((line=bufr.readLine())!=null)  
  20.             {  
  21.                 bufw.write(line);  
  22.                 bufw.newLine();  
  23.                 bufw.flush();  
  24.             }  
  25.         }  
  26.         catch(IOException e)  
  27.         {  
  28.             throw new RuntimeException("读写失败!");  
  29.         }  
  30.         finally  
  31.         {  
  32.             try  
  33.             {  
  34.                 if(bufr!=null)  
  35.                     bufr.close();  
  36.             }  
  37.             catch(IOException e)  
  38.             {  
  39.                 throw new RuntimeException("读取关闭失败!");  
  40.             }  
  41.             finally  
  42.             {  
  43.                 try  
  44.                 {  
  45.                     if(bufw!=null)  
  46.                         bufw.close();  
  47.                 }  
  48.                 catch(IOException e)  
  49.                 {  
  50.                     throw new RuntimeException("写入关闭失败!");  
  51.                 }                 
  52.             }  
  53.         }  
  54.     }  
  55. }  
LineNumberReader:跟踪行号的缓冲字符输入流。LineNumberReader是BufferedReader的子类。
此类定义了方法setLineNumber(int)和getLineNumber(),它们可分别用于设置和获取当前行号。
[java] view plaincopyprint?
  1. import java.io.*;  
  2. class LineNumberReaderDemo  
  3. {  
  4.     public static void main(String[] args)throws IOException  
  5.     {  
  6.         FileReader fr = new FileReader("helloworld.java");  
  7.       
  8.         //读取标的文件   
  9.         LineNumberReader lnr = new LineNumberReader(fr);  
  10.   
  11.         String line = null;  
  12.   
  13.         //设置行号起点   
  14.         lnr.setLineNumber(100);  
  15.   
  16.         //获取行号   
  17.         while((line=lnr.readLine())!=null)  
  18.         {  
  19.             System.out.println(lnr.getLineNumber()+":"+line);  
  20.         }  
  21.   
  22.         lnr.close();  
  23.     }  
  24. }  
PS:
1 缓冲区要结合流才可以使用。
2 无论是读一行还是多个字符,最终在硬盘上仍是一个一个读取。
3 read()和readLine()区别:
read():读取字符数据,它覆盖了父类的read方法。
readLine():另外开辟一个缓冲区,存储的是原缓冲区一行的数据,不包含换行符。

4、装饰设计模式
装饰设计模式:对原有类进行功能的改变、增强。装饰类和被装饰类通常都属于一个体系中。
装饰和继承比较:

[java] view plaincopyprint?
  1. //装饰设计模式   
  2. class Person  
  3. {  
  4.     public void chifan()  
  5.     {     
  6.         System.out.println("吃饭");  
  7.     }  
  8. }  
  9. //继承方法-->覆盖   
  10. class NewPerson extends Person  
  11. {  
  12.     public void chifan()  
  13.     {  
  14.         System.out.println("漱口");  
  15.         super.chifan();  
  16.         System.out.println("甜点");         
  17.     }  
  18. }  
  19. //装饰类-->补充   
  20. class SuperPerson  
  21. {  
  22.     private Person p;  
  23.     SuperPerson(Person p)  
  24.     {  
  25.         this.p = p;  
  26.     }  
  27.     public void superChifan()  
  28.     {  
  29.         //基于原来方法并给予丰富   
  30.         System.out.println("漱口");  
  31.         p.chifan();  
  32.         System.out.println("甜点");  
  33.     }  
  34. }  
  35. class DecorateDemo  
  36. {  
  37.     public static void main(String[] args)  
  38.     {  
  39.         Person p = new Person();  
  40.   
  41.         NewPerson np = new NewPerson();  
  42.         np.chifan();  
  43.         System.out.println("-------------------");  
  44.         SuperPerson sp = new SuperPerson(p);  
  45.         sp.superChifan();  
  46.     }  
  47. }  
继承的体系:
Reader 专门用于读取数据的类。
  |--TextReader:用于读取文本
  |--MediaReader:用于读取媒体
功能扩展:提高读取效率,加入缓冲技术
Reader
  |--TextReader
  |--BufferTextReader
  |--MediaReader
  |--BufferMediaReader
加入再进行功能扩展,就会发现这个体系会越来越臃肿,不够灵活。

装饰体系思路:
既然加入的都是同一种技术--缓冲。继承是让缓冲和自己的流对象相结合。根据劳动分工,提高效率的特点。
我们可以将缓冲(同一属性的扩展功能)进行单独封装,各司其职,当需要时再针对性调用。
1、将缓冲进行单独封装

class Buffer
{
  Buffer(TextReader text){}
  Buffer(MediaReader media){}
}
2、优化。用多态封装,提高扩展性。
class BufferReader extends Reader
{
        private Reader r;
  BufferReader(Reader r){}
}
3、装饰体系
Reader 专门用于读取数据的类。
  |--TextReader
  |--MediaReader
  |--BufferReader

由上面可知装饰和继承的异同:
相同点:都是用于功能的扩展。
不同点:
a 装饰扩展性更强;b 装饰避免了继承臃肿的体系;c 装饰降低了类之间的关联性。
d 装饰是对原有类的补充,继承是覆盖或者说是替代。

三、字节流

1、简述
a 字节流:基本操作和字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。
b 由于媒体文件数据都是以字节存储的,所以字节流对象可直接将媒体文件的数据写入到文件中,不用进行刷新动作。
c 字节流基类:InputStream(读)、OutputStream(写)
[java] view plaincopyprint?
  1. //读取字节流方式   
  2. import java.io.*;  
  3. class FileStream  
  4. {  
  5.     public static void main(String[] args)throws IOException  
  6.     {  
  7.         writerFile();  
  8.         readFile_1();  
  9.         readFile_2();  
  10.         readFile_3();  
  11.     }  
  12.     //方法一:单个字节获取   
  13.     public static void readFile_1()throws IOException  
  14.     {  
  15.         //读取指定文件字节流   
  16.         FileInputStream fis = new FileInputStream("fos.txt");  
  17.   
  18.         int ch = 0;  
  19.       
  20.         while((ch=fis.read())!=-1)  
  21.         {  
  22.             System.out.println((char)ch);  
  23.         }  
  24.   
  25.         fis.close();  
  26.     }  
  27.     //方法二:固定数组容量获取。建议用这种方式。   
  28.     public static void readFile_2()throws IOException  
  29.     {  
  30.         FileInputStream fis = new FileInputStream("fos.txt");  
  31.   
  32.         byte[] buf = new byte[1024];  
  33.         int len = 0;  
  34.         while((len=fis.read(buf))!=-1)//循环是为了获取数据的大小  
  35.         {  
  36.             System.out.println(new String(buf,0,len));  
  37.         }     
  38.         fis.close();      
  39.     }  
  40.     //方法三:以恰好容量数组读取。可能因为获取的数据太大造成内存溢出。   
  41.     public static void readFile_3()throws IOException  
  42.     {  
  43.         FileInputStream fis = new FileInputStream("fos.txt");  
  44.   
  45.         //int num = fis.available();  
  46.   
  47.         //定义一个恰好的缓冲区,不用循环操作。   
  48.         byte[] buf = new byte[fis.available()];  
  49.   
  50.         fis.read(buf);  
  51.           
  52.         //System.out.println("num="+num);   
  53.         System.out.println(new String(buf));  
  54.   
  55.         fis.close();          
  56.     }  
  57.     public static void writerFile()throws IOException  
  58.     {  
  59.         //输出字节流到指定文件   
  60.         FileOutputStream fos = new FileOutputStream("fos.txt");  
  61.           
  62.         //不涉及任何转换,所以不用刷新。   
  63.         fos.write("abcde".getBytes());  
  64.   
  65.         fos.close();  
  66.     }  
  67. }  
PS:
1 FileOutputStream、FileInputStream的flush方法内容为空,没有任何实现,调用没有意义。
2 int available():获取对象的数据大小。它是InputStream的特有方法。因为字节流可以操作媒体文件,所以直接获取数据大小,
可能因为获取的数据太大造成内存溢出。虚拟机默认内存是64M。

2、字节流的缓冲区:提高字节流读写效率。
2.1 读写存在类型提升和强转现象
read():会将字节byte类型提升为int类型。原因:防止直接返回byte类型值,造成read误判为程序结束标记的情形。
write():会将int类型强转为byte类型。原因:有始有终,保障数据唯一性。

[java] view plaincopyprint?
  1. /* 
  2. 自定义字节流的缓冲区: 
  3. 1,定义数组 
  4. 2,定义指针 
  5. 3,定义计数器。 
  6. */  
  7. import java.io.*;  
  8. //装饰类   
  9. class MyBufferedInputStream  
  10. {  
  11.     private InputStream in;  
  12.   
  13.     private byte[] buf = new byte[1024];  
  14.   
  15.     private int pos = 0, count = 0;  
  16.   
  17.     MyBufferedInputStream(InputStream in)  
  18.     {  
  19.         this.in = in;  
  20.     }  
  21.   
  22.     //返回数据类型是int类型不是byte类型,这样避免了值为-1,正常情形下程序的停止。  
  23.     public int myRead() throws IOException  
  24.     {  
  25.         //每句第一次读取数据的情形。通过in对象读取硬盘上数据,并存储在buf中。并计数。  
  26.         if(count==0)   
  27.         {  
  28.             count = in.read(buf);  
  29.             if(count<0)  
  30.                 return -1;  
  31.             pos = 0;  
  32.   
  33.             //数组以byte类型存储。   
  34.             byte b = buf[pos];  
  35.    
  36.             count--;  
  37.             pos++;  
  38.             //b&255,此时的b已经是int类型,它&255会只获取一个8位,补充位补0,防止-1的出现。  
  39.             return b&255;  
  40.         }  
  41.         //每句话第二次读取数据的情形   
  42.         else if(count>0)  
  43.         {  
  44.             byte b = buf[pos];  
  45.   
  46.             count--;  
  47.             pos++;  
  48.             //0xff=255   
  49.             return b&0xff;            
  50.         }  
  51.   
  52.         return -1;  
  53.     }  
  54.     public void myClose() throws IOException  
  55.     {  
  56.         in.close();  
  57.     }  
  58. }  
PS:
它们的转变原理是:
因为数据的字节流形式是二进制:0,1;
该文件中,因为字节流中会存在一个字节全是1的情形:1111-1111
定理:负数的二进制等于整数二进制取反+1。
0000-0001   1的二进制
1111-1110 取反
0000-0001 +1
1111-1111   -1的二进制

因为返回指针值是byte类型,但是类的返回值是int类型,所以返回值存在类型提升问题。
这样定义其实是为了解决直接返回值为-1,造成read方法误判断为程序结束标记的情形。即:while((ch=fis.read())!=-1)

byte:-1  ---->  int:  -1; 
00000000 00000000 00000000 11111111类型提升时,若补位为0,则值为 255
11111111 11111111 11111111 11111111   类型提升时,若补位为1,则值为 -1

当byte:-1提升到int类型,若补位为1,其值仍为-1。read方法以读到-1为停止标记,造成程序停
止,无法满足需求。
所以我们要在前面补0,这样就可以避免-1的出现。

补0方式:&255
    11111111 11111111 11111111 11111111
 & 00000000 00000000 00000000 11111111
------------------------------------------------------------
    00000000 00000000 00000000 11111111
同时这也意味着,我们读取完后有4个字节需要写入,这样其实改变了原数据,
所以write实际执行时,会执行一个强转动作,只执行最后一个有效字节。

四、流操作规律

1、键盘录入
1.1 标准输入输出流
System.in:对应的标准输入设备:键盘。 它是InputStream类型。
System.out:对应的标准的输出设备,控制台。它是PrintStream类型。

1.2整行录入
当使用输入流进行键盘录入时,只能单字节录入。为了提高效率,可以自定义一个数组将一行字节进行储存。当一行录入完毕,
再放回本行录入内容。这种方式正是字符流的readLine方法的原理。
那我们为什么不直接使用readLine方法呢?但由于readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流
InputStream的方法。这时我们必须将字节流转换成字符流再使用readLine方法。这样我们就用到了转换流。

1.3 转换流:InputStreamReader、OutputStreamWriter
作用:当字节流中数据都是字符时,将字节流转换成字符流。

1.3.1 InputStreamReader:将字节流转成字符流步骤:
         a 获取键盘录入对象
         InputStream in = System.in;
         b 将字节流对象转成字符流对象,使用转换流。
         InputStreamReader isr = new InputStreamReader(in);
         c 为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
         BufferedReader br = new BufferedReader(isr);
         以上步骤可简化为:最常用方式
         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

1.3.2 OutputStreamWriter:将字符流转成字节流。
         以字符形式录入,以字节形式存储到硬盘。步骤和InputStreamReader相似。

[java] view plaincopyprint?
  1. //转换流演示   
  2. import java.io.*;  
  3. class TransStreamDemo  
  4. {  
  5.     public static void main(String[] args) throws IOException  
  6.     {  
  7.         //获取键盘录入对象   
  8.         //InputStream in = System.in;   
  9.   
  10.         //将字节流对象转成字符流对象,使用转换流--> InputStreamReader  
  11.         //InputStreamReader isr = new InputStreamReader(in);  
  12.   
  13.         //加入缓冲区   
  14.         //BufferedReader bufr = new BufferedReader(isr);  
  15.   
  16.         //键盘的最常见写法。   
  17.         BufferedReader bufr =   
  18.             new BufferedReader(new InputStreamReader(System.in));  
  19.   
  20.   
  21.         //字符流转字节流。   
  22.         //OutputStream out = System.out;   
  23.         //OutputStreamWriter osw = new OutputStreamWriter(out);  
  24.         //BufferedWriter bufw = new BufferedWriter(osw);  
  25.         BufferedWriter bufw =   
  26.             new BufferedWriter(new OutputStreamWriter(System.out));  
  27.   
  28.         String line = null;  
  29.   
  30.         while((line=bufr.readLine())!=null)  
  31.         {  
  32.             if("over".equals(line))  
  33.                 break;  
  34.             bufw.write(line.toUpperCase());  
  35.             bufw.newLine();  
  36.             bufw.flush();  
  37.         }  
  38.         bufr.close();  
  39.     }  
  40. }  
2、流操作的基本规律:
2.1
  源:键盘录入。
  目的:控制台。
2.2 需求:想把键盘录入的数据存储到一个文件中。
  源:键盘。
  目的:文件。
  文件是字符形式。使用InputStreamReader。
2.3 需求:想要将一个文件的数据打印在控制台上。
  源:文件。
  目的:控制台。
  控制台获取的是字节形式。使用OutputStreamWriter。
2.4 如何选择流对象?
通过三个明确,确定流对象的使用:
a 源和目的。
  源:读取流。InputStream  Reader
  目的:输出流。OutputStream Writer
b 明确体系:操作的数据是否是纯文本。
  是:字符流
  否:字节流
c 明确要使用的具体对象。通过设备来进行区分:
  源设备:内存,硬盘,键盘。
  目的设备:内存,硬盘,控制台。
2.5 事例分析:
2.5.1 需求:将一个文件中数据存储到另一个文件中。复制文件
1)源:读取流。InputStream  Reader
      明确体系:数据类型:纯文本:Reader
      明确对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是:FileReader。
  FileReader fr = new FileReader("a.txt");
  提高效率
  BufferedReader bufr = new BufferedReader(fr);
2)目的:输出流:OutputStream Writer
      明确体系:数据类型:纯文本:Writer
      明确对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。
  FileWriter fw = new FileWriter("b.txt");
  提高效率
  BufferedWriter bufw = new BufferedWriter(fw);

2.5.2 需求:将键盘录入的数据保存到一个文件中。
1)源:InputStream Reader

      明确体系:数据类型:纯文本:Reader。
      明确对象:键盘。对应的是System.in。
      但Reader是字符流,System.in是字节流。为了操作键盘的文本数据,所以字符流最方便。
      所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中的转换流:InputStreamReader
  InputStreamReader isr = new InputStreamReader(System.in);
  提高效率
  BufferedReader bufr = new BufferedReader(isr);
2)目的:OutputStream Writer
      明确体系:数据类型:纯文本:Writer。
      明确对象:硬盘上的一个文件:FileWriter。
  FileWriter fw = new FileWriter("c.txt");
  提高效率
  BufferedWriter bufr = new BufferedWriter(fw);
扩展:想要把录入的数据按照指定的编码表(utf-8)存到文件中。
2)目的:OutputStream  Writer
      明确体系:数据类型:纯文本:Writer。
      明确对象:硬盘上的一个文件:FileWriter。
      但是FileWriter是使用的默认编码表:GBK。但存储时,需要加入指定编码表utf-8。
      而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。
      而该转换流对象要接受一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream。
  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");

  提高效率
  BufferedWriter bufw = new BufferedWriter(osw);

[java] view plaincopyprint?
  1. /* 
  2. 练习:将键盘录入的数据按照utf-8编码表存入文件中。 
  3. 思路: 
  4. 1)源:InputStream Reader 
  5.    明确体系:数据类型:纯文本:Reader。 
  6.    明确对象:键盘:System.in。因为是文本数据,Reader最好操作, 
  7.    所以需要借助Reader体系中的转换流:InputStreamWriter。 
  8.         BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 
  9. 2)目的:OutputStream Writer 
  10.    明确体系:数据类型:纯文本:Writer 
  11.    明确对象:硬盘上的文件:FileWriter。但是FileWriter是使用的默认编码表:GBK。因为存储时需要加入 
  12.    指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。 
  13.    而该转换流对象要接受一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream。 
  14.         BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(),"UTF-8")); 
  15. */  
  16. import java.io.*;  
  17. class TransStreamDemo1  
  18. {  
  19.     public static void main(String[] args) throws IOException  
  20.     {  
  21.         //键盘录入的最常见写法:   
  22.         BufferedReader bufr =   
  23.             new BufferedReader(new InputStreamReader(System.in));  
  24.   
  25.         BufferedWriter bufw =   
  26.             new BufferedWriter(new OutputStreamWriter(new FileOutputStream("lady.txt"),"UTF-8"));  
  27.   
  28.         String line = null;  
  29.   
  30.         while((line=bufr.readLine())!=null)  
  31.         {  
  32.             if("over".equals(line))  
  33.                 break;  
  34.             bufw.write(line);  
  35.             bufw.newLine();  
  36.             bufw.flush();  
  37.         }  
  38.         bufr.close();  
  39.     }  
  40. }  
运行结果:
黑马程序员——IO(一)——概述、字符流、字节流、流操作规律 .
PS:
1 转换流什么时候使用?
通常在涉及到字符编码转换时使用。它是字符和字节之间的桥梁。

2 异常日志信息
当程序在执行出现不希望直接给用户看的问题时,我们需要以异常日志的形式储存信息,方便程序员查看、调整。

[java] view plaincopyprint?
  1. //异常的日志信息独立反映。   
  2. import java.io.*;  
  3. import java.util.*;  
  4. import java.text.*;  
  5. class ExceptionInfoDemo  
  6. {  
  7.     public static void main(String[] args)throws IOException  
  8.     {  
  9.         try  
  10.         {  
  11.             int[] arr = new int[2];  
  12.             System.out.println(arr[3]);  
  13.         }  
  14.         catch(Exception e)  
  15.         {  
  16.             try  
  17.             {  
  18.                 Date d = new Date();  
  19.                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//H:24小时制  
  20.                 String s = sdf.format(d);  
  21.   
  22.                 PrintStream ps = new PrintStream("exeception.log");  
  23.                 ps.println(s);  
  24.                 System.setOut(ps);  
  25.             }  
  26.             catch(IOException ex)  
  27.             {  
  28.                 throw new RuntimeException("日志文件创建失败");  
  29.             }  
  30.             e.printStackTrace(System.out);  
  31.         }  
  32.     }  
  33. }  
3 系统属性信息文本
Properties getProperties():获取系统信息。
void list(PrintStream out):将信息输出到指定输出流中。
new PrintStream("sysinfo.txt"):将输出流中数据存入指定文件中。

[java] view plaincopyprint?
  1. // getProperties():确定当前的系统属性。   
  2. import java.util.*;  
  3. import java.io.*;  
  4. class SystemInfo  
  5. {  
  6.     public static void main(String[] args)throws IOException  
  7.     {  
  8.         Properties prop = System.getProperties();  
  9.   
  10.         prop.list(new PrintStream("sysinfo.txt"));  
  11.     }  
  12. }  
4 通过System类的setIn、setOut方法可以对默认设备进行改变。
System.setIn(new FileInputStream("1.txt")):将源改成文件1.txt。

System.setOut(new FileOutputStream("2.txt")):将目的改成文件2.txt。

5 体系图

字节流继承体系图:

黑马程序员——IO(一)——概述、字符流、字节流、流操作规律 .

字符流继承体系图:

黑马程序员——IO(一)——概述、字符流、字节流、流操作规律 .