java中级-7-IO流知识点串讲(1)-IO流概念及字符流Writer、Reader

来源:互联网 发布:c语言算法大全 编辑:程序博客网 时间:2024/06/08 01:48


------- android培训java培训、期待与您交流! ----------



I/O  Input/Output

        Java对数据的操作是通过流的方式来完成的,我们将它操纵数据时,对应流的情况做出了划分。按操作的数据类型来分,分为两种:字节流、字符流;按流的方向来分,也分为两种:输入流、输出流。

        字符流的产生,是为了解决编码转换的问题,主要传输的是文字。这样传输的文字,我们能够指定解码方式,例如:指定使用UNI-8,或者GBK-2312。通用的是字节流,而字符流属于自节流的分支。

        这样,我们就产生了对应I/O常用的4个基类:

        字节流抽象基类:InputStream、OutputStream

        字符流抽象基类:Reader、Writer

        Input 对应读取;Output 对应写入

        此四个基类派生出来的子类,都是以父类名称作为后缀。例如:FileInputStream、FileReader。为了说明I/O的使用,我们首先来看字节流的抽象基类Writer。

  

字符流Writer

        它主要实现对文字的写入操作,我们首先以其子类OutputStreamWriter的子类FileWriter来说明它的作用,并以此类推出该类的特点。

        FileWriter主要实现对文件的写操作。该类的所有方法除构造函数外,都是继承自其父类。常用的方法有write,flush,close。这三个方法分别实现对文件的写入缓冲操作,刷新缓冲操作(刷新缓存同时,将内容写入对应文件当中),关闭流。具体用法如下例:

import java.io.*;class  FileWriterTest{       public static void main(String[] args)       {              FileWriter File_w = newFileWriter("test.txt"); //创建了一个名为test的文本              File_w.write("123");   //将123添加到输入流              File_w.flush();   //刷新输入流,将123录入文件               File_w.write("456");      //将456添加进输入流              File_w.close();   //关闭输入流,在关闭前,将456刷新入test文件中        }}

        由例子,我们不难看出,在使用FileWriter类的方法对文件操作前,我们首先得有对应的文件,这时,我们可以通过对应的方法来创建一个相应的文件,或者,把想要操纵的文件,它的文件名通过构造函数传给对应FileWriter类对象。只有这样,我们才能又可以操作的目的文件。

        在对文件进行写操作的时候,我们需要注意的一点是write、flush、close三者的顺序。这三者的调用顺序应该是这样的:


图2.Writer字符流写操作顺序图

 

        需要注意的一点是flush只是刷新流,流本身还存在,即可以继续使用;close则是关闭流。如果选择关闭流,则调用close方法的时候,close本身在关闭之前,就会执行一次flush,这次刷新操作是封装在close函数内的。所以,如果我们只想进行一次输入,那么我们就可以把flush操作省略掉,直接初始化FileWriter对象,调用write和close即可。关闭之后的流,是无法写入数据的,否则会出现IOException异常。

        对于IOException异常的处理,采取抛的策略,是不太合适的,容易导致程序不健壮。而从文件调取(或创建)开始,知道流关闭close结束,这过程中的每一步都有可能出现IO异常,那么,我们对流的IO异常处理就得用下面的模版中的分部处理思想来考虑了,来看: 

import java.io.*;class  FileWriter_IOExecptionTest{       public static void main(String[] args)       {              FileWriter File_w = null; //在外部建立声明,以便于close的调用              try              {                     //try内部建立的变量是属于该程序快的局部变量,因此,如果需要,需建立外部声明                     File_w = newFileWriter("test.txt");                     File_w.write("123");                }              catch (IOException e)              {                     System.out.println(e.toString());              }              finally              {                     if(File_w!=null)  //如果文件对象创建或选取的操作出现了异常,那么文件指向为空,就不需要关闭操作                     {                            try                            {                                   File_w.close();                            }                            catch (IOException e)                            {                                   System.out.println(e.toString());                            }                     }              }       }}


        如果文件成功创建或者调用,那么结尾处,不论其操作过程中是否出现异常,我们都得关闭对应的流,以保证资源的合理使用。所以,我们将对应的close放入finally的程序块中,并加上非空判断条件,来保证流被关闭。注意上例中的模块外声明。

 

        上面的文件调用方法,我们采用的是新建文件。如果想要对已经有了的文件进行续写。我们需要用下面这种格式:

        FileWriter File_w =new FileWriter("test.txt",true); //实现文件续写

        由于涉及文件的修改与录入,这里就有个细节需要注意。那就是,Windows无法识别转义字符"\n",如果想要换行,需使用"\r"。

 

 

字符流Reader

        读操作也是流操作,那么,就必然有关闭流的close函数。但是读操作的关闭流close,它与写操作不同的一点是,它不需要在关闭时调用flush刷新。只需要直接关闭流即可。这也说明了,读操作,不需要刷新。由此,我们可以看出,对应的读取流的整个操作过程。

        首先,读取流,想要读取数据,我们必须得有数据。那么,第一步就是将对应的文件,与读取流相关联。这一步,就是读取流的创建及初始化。然后,我们需要根据需求调用对应的流对象的read方法,来读取数据。具体的步骤,我们有下面这张图(创建省略):


图3.Reader字符流读操作顺序图

 

        读取数据用的是read系的函数,该函数系最大的特点就是以从前往后的顺序,来读取数据。注意,只有读取流才拥有mark、markSupport、ready、reset、skip方法。

        先来看read()方法。read( )方法读取的数据是挨个读取的,这也就是说,如果我们想要读取一个文件中的所有数据,我们必须得多次循环才行。而且,read()方法返回值类型为int型,这就是说,如果我们想要使他有意义的显示,则需要根据具体的需求,将其强制转换为我们需要的数据类型才可以。接下来,我们就他的使用举例说明:

import java.io.*;class  MyReaderTest{       public static void main(String[] args)       {              FileReader File_r = newFileReader("test.txt");                           //第一种读取文件中所有数据的方式:              while (ture)              {                     int buff = File_r.read();//read方法,无数据时会返回-1                     if (buff==-1)                     {                            break;                     }                     sop<char>(buff);              }                               /*当然,上面哪种方式,再循环判断条件上使用了死循环条件,               这样是不太科学的。编程中,我们不建议使用这种条件,哪怕               不会出现死循环。于是,我们有下面这种写法:              */               //第二种方式:              int n = 0;              while ((n = File_r.read())!=-1)              {                     sop<char>(n);              }              //这样的写法,既简便,又安全       }       public static <T> void sop(T obj)       {              System.out.print(obj);  //这里不使用println换行,是为了保证数据的视觉连续       }}

        当然,我们也可以将文件中的数据一次性,或者分段,直接传入到一个数组中。对应的使用的是read( char[] f)和read(char[] f, int b, int len ),后者是将数据读入到数组对应的字段中。具体事例:

import java.io.*;class MyReaderTest_2{       public static void main(String[] args)       {              FileReader File_r =newFileReader("test.txt");                           //读取数据入数组,这种read方法会返回读取的有效数据个数              char[] c = new char[4];  //一般缓冲区,在使用时会定义为1024的整数倍              int num = 0;              while ((num = File.read(c))!=-1)              {                     sop(new String(c,0,num));              }       }       public static <T> void sop(T obj)       {              System.out.print(obj);       }}

        上面的两个有关read的例子,都没有对读取流的异常进行处理,这是为了简便。正常写程序的时候,应该加上异常处理的语句。和写入流一样,最好不要抛出,而是内部处理。写处理语句的原则,和Writer一致。在读取时,没有读到文件抛出的异常是FileNoFoundException的IOExecption子类异常。

        现在,我们掌握了字符流的基本操作,那么我们就可以结合两者,来看一下他们在程序中的读写文字操作。这样的典型,就是复制操作。如下: 

/**@author:LZ*//*目标:将本Java程序在D盘做个备份(即,copy操作)  思路:1 创建读取流,读取源文件              2 创建写入流,将读取入缓冲的数据写入              3 关闭读写流*/import java.io.*;class CopyTest{       public static void main(String[] args)       {              FileWriter w = null;              FileReader r = null;              char[] buffer = new char[1024]; //创建缓冲              int num = 0;              try              {                     r = newFileReader("CopyTest.java");                     w = newFileWriter("D:\\CopyTest_backup.java");                                         while ((num =r.read(buffer))!=-1)  //copy操作                     {                            w.write(buffer,0,num);                     }                     sop("copysuccess!");              }              catch (IOException e)  //源文件读取及新文件创建异常处理              {                     throws newRuntimeException("process:copy_error ")                     sop(e.toString());              }              finally              {                     //关闭读写流                     myClose(r);                     myClose(w);              }       }        private static <T extends java.io>void myClose(T t)       {              if (t!=null)              {                     try                     {                            t.close();                     }                     catch (IOException e)                     {                            sop(e.toString());                     }              }       }        public static <T> void sop(T t)       {              System.out.println(t);       }}

        上面就是字符流的基本操作了。为了提高对数据的读写效率,我们设立了缓冲区,因此,我们有对应的类BufferedWriter和BufferedReader。缓冲区主要实现了对数据的暂存,同时在需要的时候,能够将对应的数据一次性写入到对应文档中。这种性质,实际上是通过在缓冲区类中,封装数组来实现的。因为缓冲区都是依托于流而存在的,所以,具体的缓冲区对象,在初始化的时候,必须为其指定对应的可操作流对象。

        但是需要在意的是,关闭缓冲区的close方法。该方法本质上其实是关闭该缓冲区所对应的流对象。所以,在调用了缓冲区close方法后,不必在用对应流调用流的close方法了。

 

写缓冲区 BufferedWriter

        来看写缓冲BufferedWriter。对于写缓冲区来说,每次通过缓冲区进行的write写操作,都应该让写缓冲刷新一次。这个flush方法,同样是对相应流的操作,只不过信息量叫直接的大。写缓冲类中新增加了插入分隔符的newLine功能方法。newLine能够在调用缓冲中写入一个分隔符,来存到对应文件中。新定义这个方法,主要是为了避免不同系统下的换行转义字符冲突,来提高兼容性。比如Linux和Windows的换行就分别是"\n"和"\r"。这个方法是缓冲区独有的。

import java.io.*;class  BufferedWriterTest{       public static void main(String[] args)       {              FileWriter w = newFileWriter("bftest.txt"); //创建测试文件              BufferedWriter bw = newBufferedWriter(w); //将缓冲与流建立关系              bf.write("1234adf");              bf.flush();              bf.writer("qe");              bf.close();  //关闭对应流,在此之前刷新       }}

 

读缓冲区 BufferedReader

        读缓存BufferedReader中主要新提供了一个更加有效的方法readLine( )。这个方法能够从对应的读取流中获得相应文件的一整行(按照"\n"、"\r"、"\r\n"来区分行)数据的内容,返回值类型为String。它的使用方式如下:

import java.io.*;class  BufferedReaderTest{       public static void main(String[] args)       {              //创建读取流,并关联相关文件              FileReader r = newFileReader("bftest.txt");              //创建缓冲区,并关联对应读取流,以提高读取效率              BufferedReader br = newBufferedReader(r); //关联读缓冲到对应流                           String line = null;              while ((line=br.readLine())!=null)//读取一行,如读到末尾则返回null              {                     sop(line);  //输出该行              }       }        public static <T> void sop(T t)       {              System.out.println(t);       }}

        readLine方法,主要是为了提高文件获取速率,并简化读取过程。注意其返回值和read方法的不同,末尾时返回null,返回值不包含换行转义字符。

        既然学了缓冲区,那么我们可以通过缓冲区来实现上文中的复制操作: 

/**@author:LZ*//*目标:将本Java程序在D盘做个备份(即,copy操作)  思路:1 创建读取流,关联读缓冲区,读取源文件              2 创建写入流,关联写缓冲区,将读取入缓冲的数据写入              3 关闭读写流*/ import java.io.*;class  BufferCopyTest{       public static void main(String[] args)       {              //变量外部声明              FileWriter w = null;              FileReader r = null;               BufferedReader br = null;              BufferedWriter bw = null;              try              {                     //建立读写流,并关联相应的缓冲                     r = newFileReader("BufferCopyTest.java");                     w = newFileWriter("D:\\TestCopy_backup.java");                     BufferedReader br = newBufferedReader(r);                     BufferedWriter bw = newBufferedWriter(w);                      String s = null;                     while ((s =br.readLine())!=null)                     {                            bw.write(s);                            bw.newLine();                     }              }              catch (IOException e)              {                     sop("reader &write_error");              }              finally              {                     MyClose(bw);                     MyClose(br);              }       }        //缓冲区关闭方法close及异常处理的封装函数       public static <C extends java.io>void MyClose(C c) throws RuntimeException       {              if(c!=null)              {                     try                     {                            c.close();                     }                     catch (IOException e)                     {                            throw newRuntimeException("cant get file");                     }              }        }        public static <T> void sop(T t)       {              System.out.println(t);       }}


装饰设计模式

        装饰类是对已有的类的装饰,即功能上的增强。基于被装饰类的功能,而提供在此基础上更强的功能。由此,我们可以看出,对应的BufferedReader和FileReader的关系就似如此。常见的定义方式,常常把被装饰的类的对象封装到对应的装饰类中。然后,通过装饰类的构造函数,来完成对象传递。随后再根据对象的功能,来改进提供更强的功能。

class A extends Object//原类{       public void properties() //原有方法       {              System.out.println("desk");       }}class B  //修饰类{       private A r;       B(A r )       {              this.r = new A(r);       }        public void moreProperties()  //修饰后的新方法       {              r. properties ();              System.out.println("adv_p_computer");       }}classTestForDecoration{       public static void main(String[] args)       {              //修饰前后比较:              A a = new A();              a.properties();              B b = new B(a);              b.moreProperties();       }}

        我们一般将修饰类与被修饰的单个或多个类归属到同一个父类之下。通常而言为了提高对应建立的修饰类的扩展性,我们一般通过多态的向上转型的方式,来实现对被修饰类的调用。这样做是为了避免每次需要修饰时,都需要新建立对应的用于修饰的子类。即,摆脱继承结构,使用组合结构。避免了继承结构体系的臃肿,更具有灵活的特性,同时,降低了类与类之间的相互联系。利于模块化程序设计思想。组合结构,就是内部包含对应被调用类的形式的一种类结构。

        在继承父类抽象方法的情况,如果是创建抽象类,那么我们可以通过其内部的父类多态,来调用对应被修饰类的已经覆写过的原抽象方法,来避免重写代码造成的代码冗余。

        上文中的两种Buffer缓冲区类型类就是修饰类的典型。

 

LineNumberReader类

        这是所谓的lineNumber行号类,该类继承自IO修饰类BufferedReader,并在原有的基础上,新增加了设置行号setLineNumber( int lineNumber)方法,和获取行号getLineNumber()。这两个方法主要就是设置起始行号,和获取每一行是第几行。简单的来说,就是加了个计数器而已。

 

 


------- android培训java培训、期待与您交流! ----------


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信转账到不了账也退不回是怎么办 求人办事微信发红包对方不收怎么办 微信上交了订金对方不退怎么办 交通事故对方伤员堵大门搂腿怎么办 电脑开机桌面文件都没了怎么办 qq飞车手游队长换了微信群怎么办 qq飞车手游登录授权失败怎么办 安装时提示安装包发现错误怎么办 苹果6p升级系统验证失败怎么办 w10开不了机无限重启怎么办 微信朋友圈里的表情图打不开怎么办 金立手机微信启动录音被拒绝怎么办 微信帐号解封后漂流瓶不能用怎么办 微信怎么在电脑上登不上去怎么办 玩旧版60级魔兽经常花屏怎么办? 我的世界手机版物品栏不见了怎么办 苹果手机掉进水里出现花屏该怎么办 球球大作战还没进去停止运行怎么办 ps3 e3硬破芯片坏了怎么办 电话打开后页面上没有东西怎么办 WPS在电脑安装后卸载不了怎么办 ps总要以管理员的身份打开怎么办 3d关的慢保存慢怎么办 无法与服务器建立可靠的连接怎么办 被抵押的房子开发商不解押怎么办 手机系统语言是英文没有中文怎么办 w7主机网插口灯不亮了没网怎么办 电脑用了5年变得很卡了怎么办 苹果6s系统占12g怎么办 百度网盘下载的压缩包打不开怎么办 三星手机微信安装包解析错误怎么办 下身流出来的东西有异味很重怎么办 鞋脚底总有一股酸臭的味道怎么办 腋窝总是有股酸臭的味道应该怎么办 房间里总是有灰尘的味道怎么办 香水喷多了衣服味道太呛怎么办 香水喷在衣服上变黄了怎么办 84喷在白衣服上变红怎么办 夏季高温多肉山地玫瑰砍头了怎么办 海水缸爆藻有些石头不出绿藻怎么办 不小心把b原来的壁纸删了怎么办