黑马程序员—IO流(字符流)

来源:互联网 发布:知乎神经性耳鸣5年 编辑:程序博客网 时间:2024/04/30 06:42

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

大纲:

         一、IO概述

         二、字符流概述

         三、FileWriter的用法

         四、IO异常的处理方式

         五、FileReader的用法

         六、复制文件

         七、缓冲技术

         八、缓冲方式复制文件

         九、readLine原理

         十、装饰设计模式


一、IO概述:

1、IO流用来处理设备之间的数据传输。
2、Java对数据的操作是通过流的方式。
3、Java用于操作流的对象都封装在IO包中。
4、流按操作数据分为两种:字节流与字符流。
5、流按流向分为:输入流,输出流。
字符流的抽象基类
         Reader,Writer
字节流的抽象基类
 InputStrea,OutputStream
注意:由这四个类派生出来的子类名称都是以其父类名作为子类明的后缀。
既然IO流是用于操作数据的,
那么数据的最常见体现形式是:文件

二、字符流概述

其底层是字节流,只不过融合了码表,更适合操作文本数据。

字节流也能操作文本数据,但不如字符流方便。

每个国家字符都不一样,所以涉及到了字符编码问题,

那么GBK编码的中文用unicode编码解析是有问题的,

所以需要获取中文字节数据的同时并指定编码表才可以解析正确数据。

为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流

只要操作字符数据,优先考虑使用字符流体系。 

字符流的基类是Reader和Writer,其子类都是以基类作为后缀名。


三、FileWriter的用法

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2. class  FileWriterDemo  
  3. {  
  4.     public static void main(String[] args) throws IOException  
  5.     {  
  6.         //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。  
  7.         //而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。  
  8.         //其实该步就是在明确数据要存放的目的地。  
  9.         FileWriter fw = new FileWriter("demo.txt");  
  10.         //调用write方法,将字符串写入到流中。  
  11.         fw.write("abcde");  
  12.         //刷新流对象中的缓冲中的数据。  
  13.         //将数据刷到目的地中。  
  14.         //fw.flush();  
  15.   
  16.         //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。  
  17.         //将数据刷到目的地中。  
  18.         //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。  
  19.         fw.close();  
  20.     }  
  21. }  

注意:IO调用的是系统资源,所以用完之后一定要关掉,否则浪费资源。close在关闭资源前,

会先调用flush方法进行刷新。

扩展:

1、如果有同名文件,如何实现不覆盖原有文件,而在原有基础上续写。

只需要在参数里面加上true即可,如:

FileWriter fw = new FileWriter("demo.txt",true);

2、如何将write方法中的数据在记事本中进行换行。

不同系统换行符不一样,Windows系统里的是\r\n,Unix系统的是\n。

如:fw.write("nihao\r\nxiexie")

四、IO异常的处理方式

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class  FileWriterDemo2  
  4. {  
  5.     public static void main(String[] args)   
  6.     {  
  7.         FileWriter fw = null;  
  8.         try  
  9.         {  
  10.             fw = new FileWriter("demo.txt");  
  11.             fw.write("abcdefg");  
  12.         }  
  13.         catch (IOException e)  
  14.         {  
  15.             System.out.println("catch:"+e.toString());  
  16.         }  
  17.         finally  
  18.         {  
  19.             try  
  20.             {  
  21.                 if(fw!=null)  
  22.                     fw.close();               
  23.             }  
  24.             catch (IOException e)  
  25.             {  
  26.                 System.out.println(e.toString());  
  27.             }  
  28.               
  29.         }         
  30.   
  31.     }  
  32. }  

流关闭的时候必须得判断流对象是否为空。

五、FileReader的用法

第一种方式:一个一个字符的读取

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class FileReaderDemo2 {  
  4.     public static void main(String[] args) throws IOException {  
  5.         // 创建一个文件读取流对象,和指定名称的文件相关联。  
  6.         // 要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException  
  7.         FileReader fr = new FileReader("E:\\demo.txt");  
  8.   
  9.         // 调用读取流对象的read方法。  
  10.         // read():一次读一个字符。而且会自动往下读。  
  11.         int ch = 0;  
  12.         while ((ch = fr.read()) != -1) {  
  13.             System.out.println((char)ch);  
  14.         }  
  15.         /* 
  16.          * while(true) { int ch = fr.read(); if(ch==-1) break; 
  17.          * System.out.println("ch="+(char)ch); } 
  18.          */  
  19.         fr.close();  
  20.   
  21.     }  
  22. }  


第二种方式:通过字符数组进行读取。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class FileReaderDemo2   
  4. {  
  5.     public static void main(String[] args) throws IOException  
  6.     {  
  7.         FileReader fr = new FileReader("demo.txt");  
  8.           
  9.   
  10.         //定义一个字符数组。用于存储读到字符。  
  11.         //该read(char[])返回的是读到字符个数。  
  12.         char[] buf = new char[1024];  
  13.   
  14.         int num = 0;  
  15.         while((num=fr.read(buf))!=-1)  
  16.         {  
  17.             System.out.println(new String(buf,0,num));  
  18.         }  
  19.           
  20.         fr.close();  
  21.     }  
  22. }  

六、复制文件

 复制的原理:
 其实就是将C盘下的文件数据存储到D盘的一个文件中。
 步骤:
 1,在D盘创建一个文件。用于存储C盘文件中的数据。
 2,定义读取流和C盘文件关联。
 3,通过不断的读写完成数据存储。
 4,关闭资源。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class CopyText {  
  4.     public static void main(String[] args) throws IOException {  
  5.         copy_2();  
  6.     }  
  7.   
  8.     public static void copy_2() {  
  9.         FileWriter fw = null;  
  10.         FileReader fr = null;  
  11.         try {  
  12.             fw = new FileWriter("SystemDemo_copy.txt");  
  13.             fr = new FileReader("SystemDemo.java");  
  14.   
  15.             char[] buf = new char[1024];  
  16.   
  17.             int len = 0;  
  18.             while ((len = fr.read(buf)) != -1) {  
  19.                 fw.write(buf, 0, len);  
  20.             }  
  21.         } catch (IOException e) {  
  22.             throw new RuntimeException("读写失败");  
  23.   
  24.         } finally {  
  25.             if (fr != null)  
  26.                 try {  
  27.                     fr.close();  
  28.                 } catch (IOException e) {  
  29.                 }  
  30.             if (fw != null)  
  31.                 try {  
  32.                     fw.close();  
  33.                 } catch (IOException e) {  
  34.                 }  
  35.         }  
  36.     }  
  37.   
  38.     // 从C盘读一个字符,就往D盘写一个字符。  
  39.     public static void copy_1() throws IOException {  
  40.         // 创建目的地。  
  41.         FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");  
  42.   
  43.         // 与已有文件关联。  
  44.         FileReader fr = new FileReader("RuntimeDemo.java");  
  45.   
  46.         int ch = 0;  
  47.   
  48.         while ((ch = fr.read()) != -1) {  
  49.             fw.write(ch);  
  50.         }  
  51.   
  52.         fw.close();  
  53.         fr.close();  
  54.   
  55.     }  
  56.   
  57. }  

七、缓冲技术

缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。

对应类:

        BufferedWriter (extends Reader)

        BufferedReader (extends Writer)

这两个类同样具有内部缓存机制,并可以以行为单位进行输入/输出.


1、BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

该缓冲区中提供了一个跨平台的换行符:newLine(),只有缓冲区才有这个方法。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class  BufferedWriterDemo  
  4. {  
  5.     public static void main(String[] args) throws IOException  
  6.     {  
  7.         //创建一个字符写入流对象。  
  8.         FileWriter fw = new FileWriter("buf.txt");  
  9.   
  10.         //为了提高字符写入流效率。加入了缓冲技术。  
  11.         //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。  
  12.         BufferedWriter bufw = new BufferedWriter(fw);  
  13.   
  14.         for(int x=1; x<5; x++)  
  15.         {  
  16.             bufw.write("abcd"+x);  
  17.             bufw.newLine();  
  18.             bufw.flush();  
  19.         }  
  20.   
  21.         //记住,只要用到缓冲区,就要记得刷新。  
  22.         //bufw.flush();  
  23.   
  24.         //其实关闭缓冲区,就是在关闭缓冲区中的流对象。  
  25.         bufw.close();  
  26.   
  27.   
  28.     }  
  29. }  
 2、BufferedReader

字符读取流缓冲区,该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class BufferedReaderDemo {  
  4.     public static void main(String[] args) throws IOException {  
  5.         // 创建一个读取流对象和文件相关联。  
  6.         FileReader fr = new FileReader("E:\\demo.txt");  
  7.   
  8.         // 为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。  
  9.         BufferedReader bufr = new BufferedReader(fr);  
  10.         String line = null;  //中转站  
  11.         while ((line = bufr.readLine()) != null) {  
  12.             //System.out.print(line);//输出的内容不会换行,readLine不读取换行符  
  13.             System.out.println(line);  
  14.         }  
  15.         bufr.close();  
  16.     }  
  17.   
  18. }  

八、缓冲方式复制文件

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 
  2. 通过缓冲区复制一个.java文件。 
  3. */  
  4. import java.io.*;  
  5.   
  6. class  CopyTextByBuf  
  7. {  
  8.     public static void main(String[] args)   
  9.     {  
  10.         BufferedReader bufr = null;  
  11.         BufferedWriter bufw = null;  
  12.   
  13.         try  
  14.         {  
  15.             bufr = new BufferedReader(new FileReader("E:\\demo.txt"));  
  16.             bufw = new BufferedWriter(new FileWriter("E:\\copy_demo.txt"));  
  17.             String line = null;  
  18.             while((line=bufr.readLine())!=null)  
  19.             {  
  20.                 bufw.write(line);//line中没有换行符  
  21.                 bufw.newLine();//加入换行符  
  22.                 bufw.flush();  
  23.             }  
  24.         }  
  25.         catch (IOException e)  
  26.         {  
  27.             throw new RuntimeException("读写失败");  
  28.         }  
  29.         finally  
  30.         {  
  31.             try  
  32.             {  
  33.                 if(bufr!=null)  
  34.                     bufr.close();  
  35.             }  
  36.             catch (IOException e)  
  37.             {  
  38.                 throw new RuntimeException("读取关闭失败");  
  39.             }  
  40.             try  
  41.             {  
  42.                 if(bufw!=null)  
  43.                     bufw.close();  
  44.             }  
  45.             catch (IOException e)  
  46.             {  
  47.                 throw new RuntimeException("写入关闭失败");  
  48.             }  
  49.         }  
  50.     }  
  51. }  

九、readLine原理

无论是读取一行还是读取多个字符,其实最终都是在硬盘中一个一个读取,

所以最终使用的还是read方法一个一个读取。先把读取到的字符存储到临时

数组中,当读到\r\n换行符时,把临时数组装的的字符转换成字符串返回。

模拟readLine方法

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2. class MyBufferedReader extends Reader  
  3. {     
  4.     private Reader r;  
  5.     MyBufferedReader(Reader r)  
  6.     {  
  7.         this.r = r;  
  8.     }  
  9.   
  10.     //可以一次读一行数据的方法。  
  11.     public String myReadLine()throws IOException  
  12.     {  
  13.         //定义一个临时容器。原BufferReader封装的是字符数组。  
  14.         //为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。  
  15.         StringBuilder sb = new StringBuilder();  
  16.         int ch = 0;  
  17.         while((ch=r.read())!=-1)  
  18.         {  
  19.             if(ch=='\r')  
  20.                 continue;  
  21.             if(ch=='\n')  
  22.                 return sb.toString();  
  23.             else  
  24.                 sb.append((char)ch);  
  25.         }  
  26.   
  27.         if(sb.length()!=0)//没有这行代码,最后一行文本打印不出来  
  28.             return sb.toString();  
  29.         return null;          
  30.     }  
  31.   
  32.     /* 
  33.     覆盖Reader类中的抽象方法。 
  34.  
  35.     */  
  36.     public int read(char[] cbuf, int off, int len) throws IOException  
  37.     {  
  38.         return r.read(cbuf,off,len) ;  
  39.     }  
  40.   
  41.     public void close()throws IOException  
  42.     {  
  43.         r.close();  
  44.     }  
  45.     public void myClose()throws IOException  
  46.     {  
  47.         r.close();  
  48.     }  
  49. }  
  50.   
  51.   
  52. class  MyBufferedReaderDemo  
  53. {  
  54.     public static void main(String[] args) throws IOException  
  55.     {  
  56.         FileReader fr = new FileReader("buf.txt");  
  57.   
  58.         MyBufferedReader myBuf = new MyBufferedReader(fr);  
  59.   
  60.         String line = null;  
  61.   
  62.         while((line=myBuf.myReadLine())!=null)  
  63.         {  
  64.             System.out.println(line);  
  65.         }  
  66.   
  67.         myBuf.myClose();  
  68.     }  
  69. }  

十、装饰设计模式

就是把原有的类包装一下,增强其功能。

通过继承使子类具备更强的功能,容易造成程序臃肿,结构太复杂。

如MyReader下面的三个子类MyTextReader、MyMediaReader、MyDataReader,如果给

这三个子类增强为带缓冲功能的类,那么就需要在三个子类下面分别再建一个子类。

类的结构变为:

MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader

类的结构很臃肿,不易扩展。

MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader

继承体系就变得简单明了。

找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}

现在优化的思想就是单独描述一下缓冲内容。
将需要被缓冲的对象传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。

1、LineNumberReader带行号缓冲流

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class LineNumberReaderDemo   
  4. {  
  5.     public static void main(String[] args)throws IOException   
  6.     {  
  7.         FileReader fr = new FileReader("PersonDemo.java");  
  8.         LineNumberReader lnr = new LineNumberReader(fr);  
  9.   
  10.         String line = null;  
  11.         lnr.setLineNumber(100);  
  12.         while((line=lnr.readLine())!=null)  
  13.         {  
  14.             System.out.println(lnr.getLineNumber()+":"+line);  
  15.         }  
  16.   
  17.         lnr.close();  
  18.     }  
  19. }  

2、模拟带行号缓冲流(按装饰设计模式的思想)

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.*;  
  2.   
  3. class MyLineNumberReader extends MyBufferedReader {  
  4.     private int lineNumber;  
  5.   
  6.     MyLineNumberReader(Reader r) {  
  7.         super(r);  
  8.     }  
  9.   
  10.     public String myReadLine() throws IOException {//对已有的myReadLine方法进行增强  
  11.         lineNumber++;  
  12.         return super.myReadLine();  
  13.     }  
  14.   
  15.     public void setLineNumber(int lineNumber) {  
  16.         this.lineNumber = lineNumber;  
  17.     }  
  18.   
  19.     public int getLineNumber() {  
  20.         return lineNumber;  
  21.     }  
  22. }  
  23.   
  24. class MyLineNumberReaderDemo {  
  25.     public static void main(String[] args) throws IOException {  
  26.         FileReader fr = new FileReader("copyTextByBuf.java");  
  27.   
  28.         MyLineNumberReader mylnr = new MyLineNumberReader(fr);  
  29.   
  30.         String line = null;  
  31.         mylnr.setLineNumber(100);  
  32.         while ((line = mylnr.myReadLine()) != null) {  
  33.             System.out.println(mylnr.getLineNumber() + "::" + line);  
  34.         }  
  35.   
  36.         mylnr.myClose();  
  37.     }  
  38. }  


----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

0 0
原创粉丝点击