A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始




8、IO

8.2 IO流

    8.2.2 IO流常用基类-字符流

    练习:
    将d盘的一个文本文件复制到d盘。

    分析:
    读取d盘demo.txt文件中的数据,将这些数据写入到d盘copyText_1.txt文件当中。既然是操作文本数据,使用字符流。


    方式1:使用read()读取文本文件数据。

    代码:
  1. import java.io.FileReader;
  2. import java.io.FileWriter;
  3. import java.io.IOException;

  4. public class CopyTextTest{
  5.        public static void main(String[] args) throws IOException {
  6.              //1、读取一个已有的文本文件,使用字符读取流和文件相关联
  7.             FileReader fr = new FileReader("demo.txt" );

  8.              //2、创建一个目的,用于存储读到数据。
  9.             FileWriter fw = new FileWriter("copyText_1.txt" );

  10.              //3、频繁的读写操作
  11.              int ch = 0;
  12.              while((ch = fr.read()) != -1){
  13.                   fw.write(ch);
  14.             }
  15.             
  16.              //4、关闭字节流
  17.             fw.close();
  18.             fr.close();
  19.       }
  20. }
复制代码
   运行结果:





    方式2:使用read(char[])读取文本文件数据。

    代码:
  1. import java.io.FileReader;
  2. import java.io.FileWriter;
  3. import java.io.IOException;

  4. public class CopyTextTest{
  5.        private static final int BUFFER_SIZE = 1024;

  6.        public static void main(String[] args){
  7.             FileReader fr = null;
  8.             FileWriter fw = null;
  9.             
  10.              try{
  11.                   fr = new FileReader("demo.txt" );
  12.                   fw = new FileWriter("copytest_2.txt" );
  13.                   
  14.                    //创建一个临时容器,用于缓存读取到的字符
  15.                    char[] buf = new char[BUFFER_SIZE];

  16.                    //定义一个变量记录读取到的字符数(其实就是往数组里装的字符个数)
  17.                    int len = 0;

  18.                    while((len = fr.read(buf)) != -1){
  19.                         fw.write(buf,0,len);
  20.                   }
  21.             } catch(Exception e){
  22.                    throw new RuntimeException("读写失败!");
  23.             } finally{
  24.                    if(fw != null){
  25.                          try{
  26.                               fw.close();
  27.                         } catch(IOException e){
  28.                               System.out.println(e.toString());
  29.                         }
  30.                   }
  31.                    if(fr != null){
  32.                          try{
  33.                               fw.close();
  34.                         } catch(IOException e){
  35.                               System.out.println(e.toString());
  36.                         }
  37.                   }
  38.             }
  39.       }
  40. }
复制代码
   运行结果:





    字符流的缓冲区
    缓冲区的出现提高了对数据的读写效率。

    对应类:
   BufferedWriter
   BufferedReader


    P.S.
    缓冲区要结合流才可以使用。

    作用:在流的基础上对流的功能进行了增强。


    示例6:提高写入效率,使用缓冲区。
  1. import java.io.BufferedWriter;
  2. import java.io.FileWriter;
  3. import java.io.IOException;

  4. public class BufferedWriterDemo{
  5.        public static void main(String[] args) throws IOException{
  6.             FileWriter fw = new FileWriter("buf.txt" );

  7.              //为了提高写入的效率,使用了字符流的缓冲区
  8.              //创建了一个字符写入流的缓冲区对象,并且指定与要被缓冲的流对象相关联
  9.             BufferedWriter bufw = new BufferedWriter(fw);

  10.              for(int x = 1; x <= 4; x++){
  11.                    //使用缓冲区的写入方法将数据先写入到缓冲区中
  12.                   bufw.write( "abcdef" + x);
  13.                    //写入内容换行方法:newLine();
  14.                   bufw.newLine();
  15.                   bufw.flush();
  16.             }

  17.              //使用缓冲区的刷新方法将数据刷目的地中
  18.             bufw.flush();

  19.              //关闭缓冲区,其实关闭的就是被缓冲的流对象
  20.             fw.close();
  21.       }
  22. }
复制代码
   运行结果:





    示例7:提高读取效率,使用缓冲区。
  1. import java.io.BufferedReader;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileReader;

  4. public class BufferedReaderDemo{
  5.        public static void main(String[] args) throws Exception{
  6.             FileReader fr = new FileReader("buf.txt" );
  7.             
  8.             BufferedReader bufr = new BufferedReader(fr);

  9.             String line = null;

  10.              while((line = bufr.readLine()) != null){
  11.                   System.out.println(line);           
  12.             }

  13.             bufr.close();
  14.       }
  15. }
复制代码
   运行结果:

    字符流缓冲区:

    写入换行使用BufferedWriter类中的newLine()方法。
    读取一行数据使用BufferedReader类中的readLine()方法。

    bufr.read():这个read方法是从缓冲区中读取字符数据,所以覆盖了父类中的read方法。
    bufr.readLine():另外开辟了一个缓冲区,存储的是原缓冲区一行的数据,不包含换行符。


    原理:使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓冲数据变成字符串返回。

    示例8:
  1. import java.io.BufferedReader;
  2. import java.io.BufferedWriter;
  3. import java.io.FileReader;
  4. import java.io.FileWriter;

  5. public class CopyTextBufTest{
  6.        public static void main(String[] args) throws Exception {
  7.             FileReader fr = new FileReader("buf.txt" );
  8.             BufferedReader bufr = new BufferedReader(fr);

  9.             FileWriter fw = new FileWriter("buf_copy.txt" );
  10.             BufferedWriter bufw = new BufferedWriter(fw);
  11.             
  12.             String line = null;

  13.              //方式一
  14.              while((line = bufr.readLine()) != null){
  15.                   bufw.write(line);
  16.                   bufw.newLine();
  17.                   bufw.flush();
  18.             }

  19.              //方式二
  20.              /*
  21.             int ch = 0;

  22.             while((ch = bufr.read()) != -1){
  23.                   bufw.write(ch);
  24.             }
  25.             */

  26.             bufr.close();
  27.             bufw.close();
  28.       }
  29. }
复制代码
   运行结果:




    LineNumberReader
    跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。

    示例:
  1. import java.io.FileReader;
  2. import java.io.IOException;
  3. import java.io.LineNumberReader;

  4. public class LineNumberReaderDemo{
  5.        public static void main(String[] args) throws IOException {
  6.             FileReader fr = new FileReader("LineNumberReaderDemo.java" );
  7.             LineNumberReader lnr = new LineNumberReader(fr);
  8.       
  9.             String line = null;
  10.             
  11.             lnr.setLineNumber(100);

  12.              while((line = lnr.readLine()) != null){
  13.                   System.out.println(lnr.getLineNumber() + ":" + line);
  14.             }

  15.             lnr.close();
  16.       }
  17. }
复制代码
   运行结果:

    8.2.3 装饰设计模式
    对原有类进行了功能的改变,增强。
    示例:
  1. class Person{
  2.        void chifan(){
  3.             System.out.println( "吃饭");
  4.       }
  5. }

  6. //采用装饰的方式增强Person类
  7. //这个类的出现是为了增强Person而出现的
  8. class NewPerson{
  9.        private Person p;

  10.       NewPerson(Person p){
  11.              this.p = p;
  12.       }

  13.        public void chifan(){
  14.             System.out.println( "开胃酒");
  15.             p.chifan();
  16.             System.out.println( "甜点");
  17.       }
  18. }

  19. //采用继承的方式增强Person类
  20. class NewPerson2 extends Person{
  21.        public void chifan(){
  22.             System.out.println( "开胃酒");
  23.              super.chifan();
  24.             System.out.println( "甜点");
  25.       }
  26. }

  27. public class PersonDemo{
  28.        public static void main(String[] args){
  29.             Person p = new Person();
  30.             NewPerson np1 = new NewPerson(p);
  31.             np1.chifan();

  32.             System.out.println( "---------------");

  33.             NewPerson2 np2 = new NewPerson2();
  34.             np2.chifan();
  35.       }
  36. }
复制代码
   运行结果:

    装饰和继承都能实现一样的特点:进行功能的扩展增强。有什么区别呢?

    首先有一个继承体系:
    Writer
          |--TextWriter:用于操作文本
          |--MediaWriter:用于操作媒体
    如果想要对操作的动作进行效率的提高,按照面向对象,可以通过继承的方式对具体的对象进行功能的扩展,那么就需要加入缓冲技术。
    Writer
          |--TextWriter:用于操作文本
               |--BufferTextWriter:加入了缓冲技术的操作文本的对象
          |--MediaWriter:用于操作媒体
               |--BufferMediaWriter:加入了缓冲技术的操作媒体的对象
    以上方式并不理想,如果这个体系需要再进行功能扩展,又多了更多流对象。
    这样就会发现只为提高功能,导致继承体系越来越臃肿,不够灵活。

    重新思考问题:
    既然加入的都是同一种技术--缓冲。
    前一种是让缓冲和自己的流对象相结合。
    可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
  1. class Buffer {
  2.      Buffer(TextWriter w){}
  3.      Buffer(MediaWriter w){}
  4. }
复制代码
   简化为:

  1. class BufferedWriter extends Writer{
  2.      BufferedWriter(Writer w){}
  3. }
复制代码

    Writer
         |--TextWriter:用于操作文本
         |--MediaWriter:用于操作媒体
         |--BufferedWriter:用于提高效率

    可见:装饰比继承灵活。
    特点:装饰类和被装饰类都必须所属同一个接口或者父类。

    练习:
    自定义一个读取缓冲区类,模拟一个BufferedReader。

    分析:
    缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问,其实这些方法最终操作的都是数组的角标。

    缓冲的原理:
    其实就是从源中获取一批数据到缓冲区中,再从缓冲区中不断地取出一个一个数据。
    在此次取完后,再从源中继续取一批数据进缓冲区,当源中的数据取完时,用-1作为结束标记。

    代码:
  1. import java.io.FileReader;
  2. import java.io.IOException;
  3. import java.io.Reader;

  4. class MyBufferedReader{
  5.       
  6.       private Reader r;
  7.       
  8.       //定义一个数组作为缓冲区
  9.       private char[] buf = new char[1024];

  10.       //定义一个指针用于操作这个数组中的元素,当操作到最后一个元素后,指针应该归零
  11.       private int pos = 0;

  12.       //定义一个计数器用于记录缓冲区中的数据个数,当该数据减到0,就从源中继续获取数据到缓冲区中
  13.       private int count = 0;

  14.       MyBufferedReader(Reader r){
  15.              this .r = r;
  16.       }
  17.       
  18.       //该方法从缓冲区中一次取一个字符
  19.       public int myRead() throws IOException {
  20.              //从源中获取一批数据到缓冲区中,需要先做判断,只有计数器为0时,才需要从源中获取数据
  21.              if (count == 0){
  22.                   count = r.read(buf);
  23.       
  24.                   //每次获取数据到缓冲区后,角标归零
  25.                   pos = 0;
  26.              }

  27.              if (count < 0)
  28.                    return -1;

  29.              char ch = buf[pos];

  30.              pos++;
  31.              count--;

  32.              return ch;
  33.       }

  34.        public String myReadLine() throws IOException {
  35.             StringBuilder sb = new StringBuilder();

  36.              int ch = 0;
  37.              while ((ch = myRead()) != -1){
  38.                    if (ch == '\r' )
  39.                          continue ;
  40.                    if (ch == '\n' )
  41.                          return sb.toString();
  42.                    //将从缓冲区读到的字符,存储到缓存行数据的缓冲区中
  43.                   sb.append(( char )ch);
  44.             }
  45.             
  46.              if (sb.length() != 0){
  47.                    return sb.toString();
  48.             }
  49.              return null ;
  50.       }

  51.        public void myClose() throws IOException {
  52.             r.close();
  53.       }
  54. }

  55. public class MyBufferedReaderDemo{
  56.        public static void main(String[] args) throws IOException {
  57.             FileReader fr = new FileReader("buf.txt" );
  58.             
  59.             MyBufferedReader bufr = new MyBufferedReader(fr);

  60.             String line = null ;

  61.              while ((line = bufr.myReadLine()) != null){
  62.                   System.out.println(line);           
  63.             }

  64.             bufr.myClose();
  65.       }
  66. }
复制代码
    运行结果:

    8.2.4 IO流常用基类-字节流

    基本操作与字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。

    示例1:
  1. import java.io.FileOutputStream;
  2. import java.io.IOException;

  3. public class ByteStreamDemo{
  4.        public static void main(String[] args) throws IOException {
  5.             demo_write();
  6.       }

  7.        public static void demo_write() throws IOException {
  8.              //1、创建字节输出流对象,用于操作文件
  9.             FileOutputStream fos = new FileOutputStream( "bytedemo.txt");

  10.              //2、写数据,直接写入到了目的地中
  11.             fos.write( "abcdefg".getBytes());
  12.       
  13.              //关闭资源动作要完成
  14.             fos.close();
  15.       }
  16. }
复制代码
   运行结果:





    示例2:
  1. import java.io.FileInputStream;
  2. import java.io.IOException;

  3. public class ByteStreamDemo{
  4.        public static void main(String[] args) throws IOException {
  5.             demo_read1();
  6.             System.out.println( "---------------");
  7.             demo_read2();
  8.             System.out.println( "---------------");
  9.             demo_read3();
  10.       }

  11.        //读取方式一
  12.        public static void demo_read1() throws IOException {
  13.              //1、创建一个读取流对象,和指定文件关联
  14.             FileInputStream fis = new FileInputStream("bytedemo.txt" );
  15.             
  16.              //打印字符字节大小,不过要少用,文件太大,可能内存溢出
  17.              byte[] buf = new byte[fis.available()];
  18.             fis.read(buf);
  19.             System.out.println( new String(buf));
  20.             
  21.             fis.close();
  22.       }
  23.       
  24.        //读取方式二
  25.        public static void demo_read2() throws IOException {
  26.             
  27.              FileInputStream fis = new FileInputStream("bytedemo.txt" );
  28.             
  29.              //建议使用这种读取数据的方式
  30.              byte[] buf = new byte[1024];

  31.              int len = 0;

  32.              while((len = fis.read(buf)) != -1){
  33.                   System.out.println( new String(buf,0,len));
  34.             }

  35.             fis.close();
  36.       }
  37.       
  38.        //读取方式三
  39.        public static void demo_read3() throws IOException {
  40.             
  41.             FileInputStream fis = new FileInputStream("bytedemo.txt" );
  42.             
  43.              //一次读取一个字节
  44.              int ch = 0;
  45.              while((ch = fis.read()) != -1){
  46.                   System.out.print(( char)ch);
  47.             }
  48.             
  49.             fis.close();
  50.       }
  51. }
复制代码
    运行结果:

    P.S.
    FileOutputStream、FileInputStream的flush方法内容为空,没有任何实现,调用没有意义。

    字节流的缓冲区:同样是提高了字节流的读写效率。

    练习:
    通过几种方式对MP3的进行拷贝,比较它们的效率。

    代码:
  1. import java.io.BufferedInputStream;
  2. import java.io.BufferedOutputStream;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;

  6. public class CopyMp3Test{
  7.        public static void main(String[] args) throws IOException {
  8.             copy_1();
  9.             copy_2();
  10.       }

  11.        public static void copy_1() throws IOException {
  12.             FileInputStream fis = new FileInputStream("0.mp3" );
  13.             FileOutputStream fos = new FileOutputStream("1.mp3" );
  14.       
  15.              byte[] buf = new byte[1024];

  16.              int len = 0;

  17.              while((len = fis.read(buf)) != -1){
  18.                   fos.write(buf,0,len);
  19.             }

  20.             fis.close();
  21.             fos.close();
  22.       }

  23.        public static void copy_2() throws IOException {
  24.             FileInputStream fis = new FileInputStream("0.mp3" );
  25.             BufferedInputStream bufis = new BufferedInputStream(fis);

  26.             FileOutputStream fos = new FileOutputStream("2.mp3" );
  27.             BufferedOutputStream bufos = new BufferedOutputStream(fos);

  28.              int ch = 0;

  29.              while((ch = bufis.read()) != -1){
  30.                   bufos.write(ch);
  31.             }

  32.             bufis.close();
  33.             bufos.close();
  34.       }
  35. }
复制代码
   运行结果:



~END~



19 个回复

正序浏览
支持一下!!
回复 使用道具 举报
yqlbd 中级黑马 2015-11-17 09:21:22
19#
非常感谢啊。
回复 使用道具 举报
我夜良辰顶起!!
回复 使用道具 举报
呵呵,有看完了,越来越喜欢充满挑战的任务!
回复 使用道具 举报
还没学到,先下载下来,学到看!!!!!!!!!
回复 使用道具 举报
新的一天,签完到去睡了
回复 使用道具 举报
超赞的说——————!正好需要!!!!!!!!!!!!!!!!!
回复 使用道具 举报
zan  zan  zan zan !!!
回复 使用道具 举报
IO流方法好多啊:D
回复 使用道具 举报
马上就要开始学了,先看看,谢楼主
回复 使用道具 举报
坚持学习,支持群主
回复 使用道具 举报
真的的每次都能学到一点新的收货
回复 使用道具 举报
赞一个!!!
回复 使用道具 举报
坚持学习,支持群主
回复 使用道具 举报
学习学习
回复 使用道具 举报
更新很快!膜拜
回复 使用道具 举报
继续支持!!!
回复 使用道具 举报
学习学习
回复 使用道具 举报
占个沙发,支持楼主
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马