• 【JAVA IO流之字符流】


    一、概述。

    java对数据的操作是通过流的方式。
    java用于操作流的对象都在IO包中。
    流按照操作数据不同分为两种,字节流和字符流。
    流按照流向分为输入流,输出流。

    输入输出的“入”和“出”是相当于内存来说的。

    字符流:字节流读取文字字节数据后,不直接操作,而是先查指定的编码表,获取对应的文字,再对这个文字进行操作。简单来说就是字节流+码表

    在IO流中,字节流的顶层父类是Writer和Reader。

    二、java.io.FileWriter类。

    public class FileWriterextends OutputStreamWriter

    Writer

      |--OutputStreamWriter

        |--FileWriter

    该类是操作字符文件的流,用于将数据写入到文件中。

    1.方法摘要

    (1).构造方法。

    有两个重要的构造方法:

    FileWriter(File file)
              根据给定的 File 对象构造一个 FileWriter 对象。
    FileWriter(File file, boolean append)
              根据给定的 File 对象构造一个 FileWriter 对象。

    后者相对于前者来说多了一个boolean型的参数,该参数的作用是决定写入文件的方式是追加方式还是覆写方式。

    默认的构造方法(前者)构造的FileWriter流对象向文件中写入的时候会默认的将文件的内容清空然后再写入,如果使用后者并将true传入该构造方法,则写入的方式就变成了追加方式。

    (2).write方法。

    该类没有自己的write方法,全部从父类或者超类中继承而来的write方法。

    从OutputStreamWriter中继承而来的方法:

    void write(char[] cbuf, int off, int len)
              写入字符数组的某一部分。
     void write(int c)
              写入单个字符。
     void write(String str, int off, int len)
              写入字符串的某一部分。

    从Writer中继承而来的write方法:

     void write(char[] cbuf)
              写入字符数组。
     void write(String str)
              写入字符串。

    (3).flush方法。

    void flush()
              刷新该流的缓冲。

    该方法是从OutputStreamWriter中继承而来的,作用是将流中的数据数据刷到文件中。文件关闭前会默认调用此方法。如果不调用此方法,则当缓冲区满了也会自动调用该方法。

    (4).close方法。

     void close()
              关闭此流,但要先刷新它。

    2.flush与close比较

    使用flush方法和close方法均可以保存文件,使用这两者各有什么好处?

    举一个形象的例子我们在使用记事本软件的时候,会常常使用“保存”按钮保存住当前内容到文件,如果没有保存,关闭文件的时候就会出现提示信息“是否要保存文件内容?”,然后再关闭文件。在这个例子中,“保存”相当于flush的功能,而关闭文件则相当于close的功能,经常点保存按钮的目的就是为了防止断电丢失和节约内存。

    因此,使用flush的目的就是为了防止断电丢失和节约内存,因此在写程序的时候,尽量写入一句刷新一句;如果文件不关闭,最明显的影响就是“删不掉文件”。

    3.FileWriter细节:换行和续写

    在Windows字符文件中,文件的换行符是 ,在linux中则为 ,这样很有可能导致同一个java程序在linux中的运行结果和在windows中的运行结果不一致。解决方法是使用System类中的方法getProperty,得到当前系统中的行分隔符。具体用法:String line_sparator=System.getProperty("line.separator");

    如果想要在已存在的文件末尾添加新内容,则需要使用第二种构造方法。

    4.异常处理标准模板。

     1 package p02.FileWriterDemo.p01.ExceptionCaptureDemo;
     2 
     3 import java.io.FileReader;
     4 import java.io.FileWriter;
     5 import java.io.IOException;
     6 
     7 public class ExceptionHandleDemo {
     8 
     9     public static void main(String[] args) {
    10         standarExceptionHandleDemo();
    11     }
    12 
    13     private static void standarExceptionHandleDemo() {
    14         FileWriter fw=null;
    15         FileReader fr=null;
    16         try
    17         {
    18             fr=new FileReader("c:\source.txt");//可能会抛出FileNotFindException
    19             fw=new FileWriter("c:\aim.txt");//可能会抛出IOException
    20             char buf[]=new char[1024];
    21             int length=fr.read(buf);
    22             fw.write(buf,0,length);
    23             fw.flush();
    24         }
    25         catch(IOException e)
    26         {
    27             System.out.println("读写失败!");
    28         }
    29         finally
    30         {
    31             if(fr!=null)
    32             {
    33                 try
    34                 {
    35                     fr.close();
    36                 } catch (IOException e)
    37                 {
    38                     //异常处理程序
    39                 }
    40             }
    41             if(fw!=null)
    42             {
    43                 try
    44                 {
    45                     fw.close();
    46                 } catch (IOException e)
    47                 {
    48                     //异常处理程序
    49                 }
    50             }
    51         }
    52     }
    53 
    54 }
    View Code

    二、FileReader类。

    public class FileReaderextends InputStreamReader

    Reader

      |--InputStreamReader

        |--FileReader

    在IO流中,字符输入流的顶层父类是Reader类。

    1.方法摘要。

    (1)构造方法。

    FileReader(File file)
              在给定从中读取数据的 File 的情况下创建一个新 FileReader
    FileReader(FileDescriptor fd)
              在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader
    FileReader(String fileName)
              在给定从中读取数据的文件名的情况下创建一个新 FileReader

     构造方法必须传递一个参数,如果参数不正确,则会抛出FileNotFindException异常。

    (2)read方法。

    该流不具备自己特有的read方法,其读取方法全部继承自父类或者其超类。

    从InputStreamReader中继承的方法:

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

    从Reader类中继承的方法:

     int read(char[] cbuf)
              将字符读入数组。
     int read(CharBuffer target)
              试图将字符读入指定的字符缓冲区。

    经常使用的方法只有两个:read()与read(char[] cbuf);前者返回字符的编码值,后者返回字符的长度。

    (3)close方法。略。

    2.读取字符使用的两种方法。

    方法1:使用read()读取一个字符。

    1 private static void function1() throws IOException {
    2         FileReader fr=new FileReader("c:\source.java");
    3         int ch;
    4         while((ch=fr.read())!=-1)
    5         {
    6             System.out.println((char)ch);
    7         }
    8         fr.close();
    9     }
    View Code

    方法2:使用read(buf)读取到缓冲数组。

     1 private static void function2() throws IOException {
     2         FileReader fr=new FileReader("c:\source.java");
     3         char buf[]=new char[1024];
     4         int length=0;
     5         while((length=fr.read(buf))!=-1)
     6         {
     7             System.out.print(new String(buf,0,length));
     8         }
     9         fr.close();
    10     }
    View Code

    3.复制文本文件小练习。

    第一种方法:一次读取一个字符。

     1 private static void withoutParameterFunction() {
     2         FileReader fr=null;
     3         FileWriter fw=null;
     4         try
     5         {
     6             fr=new FileReader("c:/source.java");
     7             fw=new FileWriter("c:/aim.java");
     8             int ch;
     9             while((ch=fr.read())!=-1)
    10             {
    11                 fw.write(ch);
    12                 fw.flush();
    13             }
    14         }
    15         catch(IOException e)
    16         {
    17             throw new RuntimeException("读写失败!");
    18         }
    19         finally
    20         {
    21             if(fr!=null)
    22             {
    23                 try 
    24                 {
    25                     fr.close();
    26                 } catch (IOException e)
    27                 {
    28                     e.printStackTrace();
    29                 }
    30             }
    31             if(fw!=null)
    32             {
    33                 try 
    34                 {
    35                     fw.close();
    36                 } catch (IOException e)
    37                 {
    38                     e.printStackTrace();
    39                 }
    40             }
    41         }
    42     }
    View Code

    第二种方法:一次读取多个字符。

     1 private static void withParameterFunction() {
     2         FileReader fr=null;
     3         FileWriter fw=null;
     4         try
     5         {
     6             fr=new FileReader("c:/source.java");
     7             fw=new FileWriter("c:/aim.java");
     8             char buf[]=new char[1024];
     9             int len;
    10             while((len=fr.read(buf))!=-1)
    11             {
    12                 fw.write(buf, 0, len);
    13                 fw.flush();
    14             }
    15         }
    16         catch(IOException e)
    17         {
    18             throw new RuntimeException("读写失败!");
    19         }
    20         finally
    21         {
    22             if(fr!=null)
    23             {
    24                 try 
    25                 {
    26                     fr.close();
    27                 } catch (IOException e)
    28                 {
    29                     e.printStackTrace();
    30                 }
    31             }
    32             if(fw!=null)
    33             {
    34                 try 
    35                 {
    36                     fw.close();
    37                 } catch (IOException e)
    38                 {
    39                     e.printStackTrace();
    40                 }
    41             }
    42         }
    43     }
    View Code

    三、BufferedWriter缓冲流。

    public class BufferedWriterextends Writer

    Reader

      |--BufferedWriter

    1.方法摘要。

    (1)构造方法

    BufferedWriter(Writer out)
              创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    BufferedWriter(Writer out, int sz)
              创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

    可以看出,构造方法中必须传入一个Writer对象,该对象是被缓冲的对象,如果该对象不存在则缓冲流就没有了存在的意义。

    该缓冲流使用了装饰设计模式,是对Writer类的功能增强。

    (2)write方法。

    该类中的write方法和FileWriter中的方法一致,不赘述。

    但是应当注意,虽然方法相同,但是底层实现却完全不同。该类的write方法是将数据写入缓冲区中,而FileWriter的write方法是将数据写入文件(先写入流中)。

    (3)flush方法和close方法。

    void close()
              关闭此流,但要先刷新它。
     void flush()
              刷新该流的缓冲。

    特别需要注意的是,close方法调用的是Writer的close方法,所以关闭流的时候一旦关闭了缓冲流就会关闭Writer的流,所以只需要关闭缓冲流就可以了。

    (4)特有的方法:newLine。

    public void newLine()throws IOException
    写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 (' ') 符。

    该方法封装了System.getProperty("line.separator");的动作,使得程序员提高了工作效率。

    四、BufferedReader类。

    public class BufferedReaderextends Reader

    Reader

      |--BufferedReader

    该类是字符输入缓冲流,用于缓冲Reader类的流对象,以提高输入效率。

    1.方法摘要。

    (1)构造方法。

    BufferedReader(Reader in)
              创建一个使用默认大小输入缓冲区的缓冲字符输入流。
    BufferedReader(Reader in, int sz)
              创建一个使用指定大小输入缓冲区的缓冲字符输入流。

    该构造方法和BufferedWriter类似,也有两个,其中一个可以指定缓冲区的大小。

    (2)read方法。

    该read方法和FileReader中的read方法相同,不赘述。但是应当注意,虽然方法名完全相同,但是底层的实现却完全不同,该流对象操作的对象是内存(从内存中读,所以速度更快),而FileReader操作的是文件。

    (3)特有方法:readLine()

    public String readLine()throws IOException
    读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (' ')、回车 (' ') 或回车后直接跟着换行。
    返回:
    包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
    抛出:
    IOException - 如果发生 I/O 错误

    由于该流是针对字符的流,所以对于“行”有着特殊的方法,它是根据回车符来判断一行是否结束的(在文件中体现为“ ”),而不是根据肉眼观察到底有几行。

    (4)close方法。

    不赘述。

    五、是用缓冲区的方式复制文本文件。

    第一种方法:一次读取一个字符

     1 private static void CopyTextFileUseRead() {
     2         BufferedReader br=null;
     3         BufferedWriter bw=null;
     4         FileReader fr=null;
     5         FileWriter fw=null;
     6         try
     7         {
     8             fr=new FileReader("c:/source.java");
     9             fw=new FileWriter("c:/aim.java");
    10             br=new BufferedReader(fr);
    11             bw=new BufferedWriter(fw);
    12             int ch;
    13             while((ch=br.read())!=-1)
    14             {
    15                 bw.write(ch);
    16                 bw.flush();
    17             }
    18         }
    19         catch(IOException e)
    20         {
    21             throw new RuntimeException("读写失败!");
    22         }
    23         finally
    24         {
    25             if(fr!=null)
    26             try
    27             {
    28                 fr.close();
    29             }
    30             catch(IOException e)
    31             {
    32             }
    33             if(fw!=null)
    34             {
    35                 try 
    36                 {
    37                     fw.close();
    38                 } catch (IOException e)
    39                 {
    40                 }
    41             }
    42             
    43         }
    44     }
    View Code

    第二种方法:使用readLine方法,一次读取“一行”

     1 private static void CopyTextFileUseReadLine() {
     2         BufferedReader br=null;
     3         BufferedWriter bw=null;
     4         FileReader fr=null;
     5         FileWriter fw=null;
     6         try
     7         {
     8             fr=new FileReader("c:/source.java");
     9             fw=new FileWriter("c:/aim.java");
    10             br=new BufferedReader(fr);
    11             bw=new BufferedWriter(fw);
    12             String buf=null;
    13             while((buf=br.readLine())!=null)
    14             {
    15                 bw.write(buf);
    16                 bw.newLine();
    17                 bw.flush();
    18             }
    19         }
    20         catch(IOException e)
    21         {
    22             throw new RuntimeException("读写失败!");
    23         }
    24         finally
    25         {
    26             if(fr!=null)
    27             try
    28             {
    29                 fr.close();
    30             }
    31             catch(IOException e)
    32             {
    33             }
    34             if(fw!=null)
    35             {
    36                 try 
    37                 {
    38                     fw.close();
    39                 } catch (IOException e)
    40                 {
    41                 }
    42             }
    43         }
    44     }
    View Code

    六、模拟BufferedReader类。

    对于BufferedReader类,有三个个方法经常被使用:read、readLine、close,现在自定义类MyBufferedReader,模拟三个方法,以加深对BufferedReader的理解。

    首先read与reanLine原理图:

    MyBufferedReader类:

     1 class MyBufferedReader
     2 {
     3     private Reader r;
     4     private int size=1024;//默认的缓冲区大小
     5     private char buf[];//存放字符的缓冲数组
     6     private int pos=0;//记录当前的指针
     7     private int count=0;//记录数组的长度
     8     public MyBufferedReader(Reader r)
     9     {
    10         this.r=r;
    11         buf=new char[size];
    12     }
    13     public MyBufferedReader(Reader r,int size)
    14     {
    15         this.r=r;
    16         this.size=size;
    17         buf=new char[size];
    18     }
    19     public int read() throws IOException
    20     {
    21         if(count==0)
    22         {
    23             count=r.read(buf);
    24             pos=0;
    25         }
    26         if(count<0)
    27             return -1;
    28         char ch=buf[pos];
    29         pos++;
    30         count--;
    31         return ch;
    32     }
    33     public String readLine() throws IOException
    34     {
    35         StringBuilder sb=new StringBuilder();
    36         int ch;
    37         while((ch=read())!=-1)
    38         {
    39             if(ch=='
    ')
    40                 continue;
    41             if(ch=='
    ')
    42                 return sb.toString();
    43             sb.append((char)ch);
    44         }
    45         if(sb.length()!=0)//这里进行健壮性判断是必不可少的,否则很有可能丢失最后一行
    46             return sb.toString();
    47         return null;
    48     }
    49     public void close() throws IOException
    50     {    
    51         r.close();
    52     }
    53 }
    View Code

    此类中,有一句应当特别注意:

    在readLine方法中:

    if(sb.length()!=0)//这里进行健壮性判断是必不可少的,否则很有可能丢失最后一行
                return sb.toString();

    这句不能丢,因为丢了这个判断很有可能会丢失最后一行(如果最后一行没有回车)。

    通过模拟,可以发现,BufferedReader封装了一个缓冲数组,该缓冲数组缓存r中的数据,以提高程序对数据读取效率。该数组当然也可以自定义,之前已经演示,不赘述。

     六、BufferedReader和BufferedWriter类使用了装饰模式

    所谓的装饰模式是针对某一类进行功能增强的类。在这里,BufferedReader类对Reader(及其子类)类进行了功能增强,BufferedWriter类对Writer类(及其子类)进行了功能增强。

    使用装饰模式和使用继承都能达到增强某一类的功能的目的,但是使用装饰模式更加灵活。装饰模式是针对某一类进行功能增强,而如果使用继承设计模式,则只能针对某个特别的类进行功能增强。比如,如果使用继承达到功能增强的目的,则继承层次将会变成这样:

    Reader

      |--InputStreamReader

        |--FileReader

          |--BufferedFileReader

        |--BufferedInputStreamReader

    相对于原本的继承层次:

    Reader

      |--InputStreamReader

        |--FileReader

      |--BufferedReader

    来说,前者很明显多了一个缓冲流,但是绝不仅仅是多了一个,因为Reader的子类很多,如果针对每个具体的类进行功能增强,则整个继承体系将会变得非常臃肿。但是如果使用装饰模式进行功能增强,则可以几乎不需要改变原本的继承层次,只需要多出一个缓冲流即可。由此可见,使用装饰模式要灵活很多,因此,如果想要针对某一类进行功能增强,最好使用装饰模式而不是使用继承的方式

  • 相关阅读:
    SEO优化---学会建立高转化率的网站关键词库
    从一个程序员的角度看——微信小应用
    当AngularJS POST方法碰上PHP
    angularJS(6)
    彻底解决显示Opencv中Mat图像到Mfc窗口问题
    数据结构与算法基础总结
    java类别问题
    java基础知识
    逻辑地址、线性地址、物理地址和虚拟地址的区别
    TCP协议中的三次握手和四次挥手(图解)
  • 原文地址:https://www.cnblogs.com/kuangdaoyizhimei/p/4034232.html
Copyright © 2020-2023  润新知