黑马程序员——IO第二天

来源:互联网 发布:查理布朗经典语录 知乎 编辑:程序博客网 时间:2024/06/10 04:50

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

IO

一、字符流的简介

1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。

        2、字符流只用于处理文字数据,而字节流可以处理媒体数据。

        3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter   后缀是父类名。前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。

二、字符流的读写

1、写入字符流步骤

        a、创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。且该目录下如果已有同名文件,则同名文件将被覆盖。其实该步就是在明确数据要存放的目的地。

        b、调用write(String s)方法,将字符串写入到流中。

        c、调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。

        d、调用close()方法,关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。

close()flush()区别:

        flush()刷新后,流可以继续使用;

       而close()刷新后,将会关闭流,不可再写入字符流。

注意:

        1、其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。

        2、文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。(windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)

        3、由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。

 示例:

import java.io.*;class FileWriterDemo {public static void main(String[] args) {writerDate("abcde");//写入数据writerFrom("zheshi:\r\nshenma");//续写数据,并附换行操作}//在硬盘上创建一个文件并写入指定数据public static void writerDate(String s){FileWriter fw=null;try{fw=new FileWriter("demo.txt");//创建文件fw.write(s);//将数据写入流}catch (IOException e){throw new RuntimeException("写入失败");}finally{if(fw!=null)try{fw.close();//将写入流的数据刷到指定文件内,并关闭流资源}catch (IOException e){}}}//对已有文件的数据续写指定数据public static void writerFrom(String s){FileWriter fw=null;try{fw=new FileWriter("demo.txt",true);//传递一个true参数,代表不覆盖已有的文件。fw.write(s);}catch (IOException e){throw new RuntimeException("写入失败");}finally{try{if(fw!=null)fw.close();}catch (IOException e){}}}}

2、读取字符流步骤

        1)创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException

        2)调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。

              第一种方式:读取单个字符。第二种方式:通过字符数组进行读取。

        3)读取后要调用close方法将流资源关闭。

示例:

import java.io.*;class FileReaderDemo{public static void main(String[] args) {//第一种读取方式singleCharReader();//第二种读取方式arrayCharReader();}//第一种读取方式,单个字符读取public static void singleCharReader(){FileReader fr=null;try{fr=new FileReader("demo.txt");//读取文件位置和文件名for (int ch=0;(ch=fr.read())!=-1 ; ){System.out.print((char)ch);}}catch (IOException e){throw new RuntimeException("读取失败");}finally{try{if(fr!=null)fr.close();//关闭读取流资源}catch (IOException e){}}}//第二种读取方式,通过字符数组进行读取public static void arrayCharReader(){FileReader fr=null;char[] arr=new char[1024];//定义一个字符数组,用于临时存储读取的字符try{//读取一个.java文件,并打印在控制台上。fr=new FileReader("FileReaderDemo.java");for (int len=0;(len=fr.read(arr))!=-1 ; ){//显示字符数组的字符个数System.out.print(new String(arr,0,len));}}catch (IOException e){throw new RuntimeException("读取失败");}finally{if(fr!=null)try{fr.close();}catch (IOException e){}}}}

注意:

        1、定义文件路径时,可以用“/”或者“\\”。

        2、在创建一个文件时,如果目录下有同名文件将被覆盖。

        3、在读取文件时,必须保证该文件已存在,否则出异常。

练习:文本文件的拷贝,文本文件修改

* ①将text1文件中的每一行倒序排列输出到text3中。比如:第一行是:a b c d e 处理后第一行是:e d c b a。 ②找出text1和text2中不同的字符,并把这些字符输出到text4文件中,要求每5个字符换行一次,字符之间用空格分开。 @author 刘建华 */import java.io.*;import java.util.*;public class IOtest {public static void main(String[] args){//调用方法getDifferent();reverseCopy();}/* * 步骤: * 1.创建流分别读取text1和text2。 * 2.创建流写出数据text4. * 3.新建3个集合。分别存入text1.与text2和null的数据。 * 4.使第3个集合存入text1与text2不同的数据。 * 5.将其写入text4中。 * */public static void getDifferent(){//创建3个流BufferedReader br1=null;BufferedReader br2=null;BufferedWriter bw=null;try{//使流与文件相关联br1=new BufferedReader(new FileReader("d:\\text1.txt"));br2=new BufferedReader(new FileReader("d:\\text2.txt"));bw=new BufferedWriter(new FileWriter("d:\\text4.txt"));int len1=0;int len2=0;int count=0;//穿件3个集合ArrayList al1=new ArrayList();ArrayList al2=new ArrayList();ArrayList temp=new ArrayList();//在第一个集合中存入text1的数据while((len1=br1.read())!=-1){if(" ".equals(String.valueOf((char)len1)))continue;else if("\r".equals(String.valueOf((char)len1)))continue;else if("\n".equals(String.valueOf((char)len1)))continue;elseal1.add(String.valueOf((char)len1));}//在第二个集合中存入text2的数据while((len2=br2.read())!=-1){if(" ".equals(String.valueOf((char)len2)))continue;else if("\r".equals(String.valueOf((char)len2)))continue;else if("\n".equals(String.valueOf((char)len2)))continue;elseal2.add(String.valueOf((char)len2));}//使第3个集合中存入text1与text2不听的数据temp.addAll(al1);al1.removeAll(al2);al2.removeAll(temp);temp=al2;temp.addAll(al1);//把第3个集合的数据写入writer流中for(String str:temp){count++;bw.write(str+" ");if(count==5){count=0;bw.newLine();}}}catch(IOException e){throw new RuntimeException("操作失败");}finally{//关闭所有流。try{if(br1!=null)br1.close();}catch(IOException e){throw new RuntimeException("读取流1关闭失败");}try{if(br2!=null)br2.close();}catch(IOException e){throw new RuntimeException("读取流2关闭失败");}try{if(bw!=null)bw.close();}catch(IOException e){throw new RuntimeException("写入流关闭失败");}}}/* * 步骤: * 1.创建一个Read流。将text1的数据读入流中。 * 2.创建一个StringBuffer。将流中的数据存入StringBuffer中。 * 3.使用reverse功能。将其反转。 * 4.创建一个写出流,将反转后的数据写入流中。然后写出在text3中*/public static void reverseCopy(){//创建读取和写出流BufferedReader br=null;BufferedWriter bw=null;try{//使流与text1,text2想关联。br=new BufferedReader(new FileReader("d:\\text1.txt"));bw=new BufferedWriter(new FileWriter("d:\\text3.txt"));String line=null;//读取text1中的数据,存入StringBuffer中,并使其反转后写出流中。while((line=br.readLine())!=null){StringBuffer sb=new StringBuffer();StringBuffer reversesb=new StringBuffer();sb.append(line);reversesb=sb.reverse();bw.write(reversesb.toString());bw.newLine();}}catch(IOException e){throw new RuntimeException("文件复制失败");}finally{//关闭流try{if(br!=null)br.close();}catch(IOException e){throw new RuntimeException("读取流关闭失败");}try{if(bw!=null)bw.close();}catch(IOException e){throw new RuntimeException("写入流关闭失败");}}}}

三、字符流的缓冲区——BufferedReaderBufferedWriter

1、缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象。即先将流对象初始化到构造函数中。 

2、缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。

3、写入流缓冲区BufferedWriter的步骤:

        1)创建一个字符写入流对象。

         如:FileWriter fw=newFileWriter("buf.txt");

         2)为了提高字符写入流效率。加入缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

         如: BufferedWriter bufw =new BufferedWriter(fw);

         3)调用write方法写入数据到指定文件

         如:bufw.write("adfg");

         记住,只要用到缓冲区,就要记得刷新。(关闭流同样会刷新,但为了排除意外事故,保证数据存在,建议写入一次就刷新一次)

         如:bufw.flush();

         4)其实关闭缓冲区,就是在关闭缓冲区中的流对象。

        如: bufw.close();

小知识:BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。

        如:bufw.newLine();

4、读取流缓冲区BufferedReader

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

readLine方法原理:

        无论是读一行。或者读取多个字符。其实最终都是在在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。

步骤:

        1)创建一个读取流对象和文件相关联

        如: FileReader fr=newFileReader("buf.txt");

        2)为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。

        如: BufferedReader bufr=new BufferedReader(fr);

        3)调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null

        如: String s=bufr.readLine();

        4)关闭流资源

        如: bufr.close();

示例:通过缓冲区复制一个文本文件。

/*需求:使用缓冲技术copy一个文本文件*/import java.io.*;class BufferedCopyDemo {public static void main(String[] args) {BufferedWriter bfw=null;BufferedReader bfr=null;try{//创建写缓冲对象bfw=new BufferedWriter(new FileWriter("ReaderWriterTest_copy.txt"));//创建读缓冲对象bfr=new BufferedReader(new FileReader("ReaderWriterTest.java"));//利用BufferedReader提供的readLine方法获取整行的有效字符。直到全部获取for (String line=null; (line=bfr.readLine())!=null; ){bfw.write(line);//写入指定文件中bfw.newLine();//换行bfw.flush();//将缓冲区数据刷到指定文件中}}catch (IOException e){throw new RuntimeException("文件copy失败");}finally{if(bfw!=null)try{bfw.close();//关闭写入流}catch (IOException e){throw new RuntimeException("写入流关闭失败");}if(bfr!=null)try{bfr.close();//关闭读取流}catch (IOException e){throw new RuntimeException("读取流关闭失败");}}}}

5、自定义BufferedReader

原理:

        可根据BufferedReader类中特有一行一行读取方法readLine()的原理,自定义一个类中包含相同功能的方法

步骤:

        a、初始化自定义的类,加入流对象。

        b、定义一个临时容器,原BufferedReader封装的是字符数组,此类中可定义一个StringBuilder的容器,最终可实现字符串的提取。

代码:

/*需求:根据readLine方法原理,模拟BufferedReader写一个自己的MyBufferedReader*/import java.io.*;//自定义缓冲类class MyBufferedReader extends Reader{private Reader r;//定义接收的流对象MyBufferedReader(Reader r){this.r=r;}//自定义整行读取public String myReadLine()throws IOException{//创建一个容器,用来存储一行的字符StringBuilder sb =new StringBuilder();//一个字符一个字符读取for (int ch=0;(ch=r.read())!=-1 ; ){if(ch=='\r')//如果遇到换行符,则继续continue;if(ch=='\n')//如果遇到回车符,表示该行读取完毕return sb.toString();elsesb.append((char)ch);//将该行的字符添加到容器}if(sb.length()!=0)//如果读取结束,容器中还有字符,则返回元素return sb.toString();return null;}//复写父类中的抽象方法public int read(char[] cbuf, int off, int len) throws IOException{return r.read(cbuf,off,len);}//复写父类的close方法public void close()throws IOException{r.close();}}//测试MyBufferedReaderclass MyBufferedReaderDemo{public static void main(String[] args) {MyBufferedReader mbr=null;try{mbr=new MyBufferedReader(new FileReader("BufferedCopyDemo.java"));for (String line=null;(line=mbr.myReadLine())!=null ; ){System.out.println(line);//显示效果}}catch (IOException e){throw new RuntimeException("读取数据失败");}finally{try{if(mbr!=null)mbr.close();}catch (IOException e){throw new RuntimeException("读取流关闭失败");}}}}

6LineNumberReader

        在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:

         setLineNumber();//设置初始行号

         getLineNumber();//获取行号

示例:

import java.io.*;public class MyLineNumberReader {public static void main(String[] args){MyLineNumber my=null;try{my=new MyLineNumber(new FileReader("G:\\java\\MyEclipse 10\\exam\\src\\练习\\hhh.java"));String line=null;while((line=my.MyLineRead())!=null){System.out.println(my.MyGetLineNumber()+line);}}catch(IOException e){System.out.println(" IOException");}finally{try{if(my!=null)my.close();}catch(IOException e){System.out.println("IOException");}}}}class MyLineNumber extends Reader{ private int linenumber=0;private Reader r;MyLineNumber(Reader r){this.r=r;}public void close()throws IOException{r.close();}public int read(char[] cbuf, int off, int len)throws IOException{return r.read(cbuf,off,len);}public void MySetLineNumber(int linenumber){this.linenumber=linenumber;}public int MyGetLineNumber(){return linenumber;}public String MyLineRead()throws IOException{++linenumber;StringBuffer sb=new StringBuffer();int num=0;while((num=r.read())!=-1){if(num=='\r')continue;if(num=='\n'){return sb.toString();}sb.append((char)num);}if(sb.length()!=0){return sb.toString();}return null;}}

四、装饰设计模式

1、简述

        当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。

2、特点

        装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

3、装饰和继承的区别:

        1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。

        2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。

        3)从继承结构转为组合结构。

注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。


0 0
原创粉丝点击