• java IO流——字符流


    一、概述

    流的概念:

    流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以“流”的方式进行。设备可以是文件,网络,内存等。

    流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流。

    可以将流想象成一个“水流管道”,水流就在这管道中形成了,自然就出现了方向的概念。

    当程序需要从某个数据源读入数据的时候,就会开启一个输入流,数据源可以是文件、内存或网络等等。相反地,需要写出数据到某个数据源目的地的时候,也会开启一个输出流,这个数据源目的地也可以是文件、内存或网络等等。

    小结:

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

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

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

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

    流结构:

    Java所有的流类位于java.io包中,都分别继承字以下四种抽象流类型。

      字节流 字符流
    输入流 InputStream Reader
    输出流 OutputStream Writer

    1.继承自InputStream/OutputStream的流都是用于向程序中输入/输出数据,且数据的单位都是字节(byte=8bit),如图,深色的为节点流,浅色的为处理流。

    2.继承自Reader/Writer的流都是用于向程序中输入/输出数据,且数据的单位都是字符(2byte=16bit),如图,深色的为节点流,浅色的为处理流。

    二、java.io.FileWriter类

    public class FileWriterextends OutputStreamWriter

    Writer

      |--OutputStreamWriter

        |--FileWriter

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

    1.方法摘要

    (1).构造方法

    FileWriter(File file)   根据给定的File对象构造一个FileWiter对象;
    
    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.异常处理标准模板

    package p02.FileWriterDemo.p01.ExceptionCaptureDemo;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class ExceptionHandleDemo {
    
        public static void main(String[] args) {
            standarExceptionHandleDemo();
        }
    
        private static void standarExceptionHandleDemo() {
            FileWriter fw=null;
            FileReader fr=null;
            try
            {
                fr=new FileReader("c:\source.txt");//可能会抛出FileNotFindException
                fw=new FileWriter("c:\aim.txt");//可能会抛出IOException
                char buf[]=new char[1024];
                int length=fr.read(buf);
                fw.write(buf,0,length);
                fw.flush();
            }
            catch(IOException e)
            {
                System.out.println("读写失败!");
            }
            finally
            {
                if(fr!=null)
                {
                    try
                    {
                        fr.close();
                    } catch (IOException e)
                    {
                        //异常处理程序
                    }
                }
                if(fw!=null)
                {
                    try
                    {
                        fw.close();
                    } catch (IOException e)
                    {
                        //异常处理程序
                    }
                }
            }
        }
    
    }
    View Code

     

    二、FileReader类

    public class FileReader extends InputStreamReader

    Reader

      |--InputStreamReader

        |--FileReader

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

    1.方法摘要

    (1)构造方法

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

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

    (2)read方法

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

    从InputStreamReader中继承的方法:

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

    从Reader类中继承的方法:

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

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

    (3)close方法

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

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

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

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

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

    3.复制文本文件小练习

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

    private static void withoutParameterFunction() {
            FileReader fr=null;
            FileWriter fw=null;
            try
            {
                fr=new FileReader("c:/source.java");
                fw=new FileWriter("c:/aim.java");
                int ch;
                while((ch=fr.read())!=-1)
                {
                    fw.write(ch);
                    fw.flush();
                }
            }
            catch(IOException e)
            {
                throw new RuntimeException("读写失败!");
            }
            finally
            {
                if(fr!=null)
                {
                    try 
                    {
                        fr.close();
                    } catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
                if(fw!=null)
                {
                    try 
                    {
                        fw.close();
                    } catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    View Code

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

    private static void withParameterFunction() {
            FileReader fr=null;
            FileWriter fw=null;
            try
            {
                fr=new FileReader("c:/source.java");
                fw=new FileWriter("c:/aim.java");
                char buf[]=new char[1024];
                int len;
                while((len=fr.read(buf))!=-1)
                {
                    fw.write(buf, 0, len);
                    fw.flush();
                }
            }
            catch(IOException e)
            {
                throw new RuntimeException("读写失败!");
            }
            finally
            {
                if(fr!=null)
                {
                    try 
                    {
                        fr.close();
                    } catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
                if(fw!=null)
                {
                    try 
                    {
                        fw.close();
                    } catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    View Code

    三、BufferedWriter缓冲流

    public class BufferedWriter extends 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 BufferedReader extends Reader

    Reader

      |--BufferedReader

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

    1.方法摘要

    (1)构造方法

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

    (2)read方法

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

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

    (3)特有方法:readLine()

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

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

    (4)close方法

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

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

    private static void CopyTextFileUseRead() {
            BufferedReader br=null;
            BufferedWriter bw=null;
            FileReader fr=null;
            FileWriter fw=null;
            try
            {
                fr=new FileReader("c:/source.java");
                fw=new FileWriter("c:/aim.java");
                br=new BufferedReader(fr);
                bw=new BufferedWriter(fw);
                int ch;
                while((ch=br.read())!=-1)
                {
                    bw.write(ch);
                    bw.flush();
                }
            }
            catch(IOException e)
            {
                throw new RuntimeException("读写失败!");
            }
            finally
            {
                if(fr!=null)
                try
                {
                    fr.close();
                }
                catch(IOException e)
                {
                }
                if(fw!=null)
                {
                    try 
                    {
                        fw.close();
                    } catch (IOException e)
                    {
                    }
                }
                
            }
        }
    View Code

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

    private static void CopyTextFileUseReadLine() {
            BufferedReader br=null;
            BufferedWriter bw=null;
            FileReader fr=null;
            FileWriter fw=null;
            try
            {
                fr=new FileReader("c:/source.java");
                fw=new FileWriter("c:/aim.java");
                br=new BufferedReader(fr);
                bw=new BufferedWriter(fw);
                String buf=null;
                while((buf=br.readLine())!=null)
                {
                    bw.write(buf);
                    bw.newLine();
                    bw.flush();
                }
            }
            catch(IOException e)
            {
                throw new RuntimeException("读写失败!");
            }
            finally
            {
                if(fr!=null)
                try
                {
                    fr.close();
                }
                catch(IOException e)
                {
                }
                if(fw!=null)
                {
                    try 
                    {
                        fw.close();
                    } catch (IOException e)
                    {
                    }
                }
            }
        }
    View Code

     

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

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

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

    Reader

      |--InputStreamReader

        |--FileReader

          |--BufferedFileReader

        |--BufferedInputStreamReader

    相对于原本的继承层次:

    Reader

      |--InputStreamReader

        |--FileReader

      |--BufferedReader

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

  • 相关阅读:
    mongo admin 客户端管理工具安装
    kong API gateway
    安装 docker管理 工具 页面 portainer
    elcipse 安装lombok插件解决 @Slf4j 等找不到log变量问题
    cqrs案例
    你还不知道这四点Mysql调优的话,那就赶快学起来
    python中的类型提示(type hint)
    大厂面试最常被问的Redis问题合集
    nginx gzip json [2]
    nginx gzip json 配置「1」
  • 原文地址:https://www.cnblogs.com/zxqstrong/p/5299307.html
Copyright © 2020-2023  润新知