JavaSE01_Day04(中下)-缓冲输入输出流、对象流、字符流

一、缓冲输入流和缓冲输出流

       为了提高对于文件中的数据读写效率,可以采用缓冲流进行搭配文件流进行使用(流连接),使用缓冲流时千万要注意,不能直接对数据源进行读写操作,搭配文件流进行对文件数据的读写操作,可以简化读写的复杂处理流程。

1.1 BufferedInputStream缓冲字节输入流工作原理

       文件输入流对于文件中的数据进行读取时,是以字节为单位进行读写的,会因为频繁进行读取操作,而降低读取效率,这也就是读取效率慢的原因。如果搭配缓冲字节输入流,当进行读取数据时,仍然是以字节为单位进行读取数据,但是缓冲字节输入流中维护了一个缓冲区(字节数组),使用该流进行读取字节时,会尽可能的多的一次性读取若干字节,然后存储到缓冲区中,然后按照读取的需要的字节量进行字节的返回,直至缓冲区的数据全部被读取完毕,再进行读取若干字节到缓冲区中,这样操作会反复执行多次,就可以减少读取的次数,从而提高读取的效率。

1.2 BufferedOutputStream缓冲字节输出流工作原理

       缓冲字节输出流的工作原理和上面的缓冲字节输入流工作原理类似,在向磁盘中进行写出数据操作时,增加写出次数会降低写出的效率,所以我们可以采用缓冲字节输出流进行一次性批量的写出若干数据,进而减少读写的次数,提高读写效率。当搭配缓冲字节输出流进行使用后,内部维护了一个缓冲区(字节数组),可以一次性批量写出若干数据,从而减少写出次数,进而提高写出效率。当进行写出的时候,会先将数据存储到缓冲区中,当缓冲区满了以后,会一次性全部进行写出操作。如果希望缓冲区存储的数据没有满的时候就进行数据的全部写出操作,此时需要手动去调用flush()方法强制输出。

 package cn.tedu.io;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 
 /**
  * 使用缓冲字节流完成对音频文件的复制案例
  * @author cjn
  *
  */
 public class BOS_BIS_Copy {
 
     public static void main(String[] args) throws Exception {
         /*
          * 1.读取音频文件
          * 文件流是一个低级流,低级流往往对数据源明确,创建缓冲字节输入流(高级流)以后,
          * 需要在创建对象的时候,将低级流对象作为创建构造器的参数进行传递(流连接)
          */
         //创建文件字节输入流(低级流)对象
         FileInputStream fis = new FileInputStream("G_E_M_ 邓紫棋 - 句号.mp3");
         //创建缓冲字节输入流(高级流)对象
         BufferedInputStream bis = new BufferedInputStream(fis);
 
         /*
          * 2.写出音频文件
          */
         //创建文件字节输入流(低级流)对象
         FileOutputStream fos = new FileOutputStream("G_E_M_ 邓紫棋 - 句号copy.mp3");
         //创建缓冲字节输出流(低级流)对象
         BufferedOutputStream bos = new BufferedOutputStream(fos);
         
         //3.读写数据
         int len = 0;//实际读取字节量的大小
         while ((len=bis.read()) != -1) {//未读取到末尾
             bos.write(len);//写出数据
        }
         System.out.println("复制音频文件完成!!!");
         
         //4.关闭资源
         bis.close();
         bos.close();
         
    }
 
 }

1.3 BufferedOutputStream提供的flush方法

       使用缓冲字节输出流可以提高对于文件的读写效率,但是在写出数据的时候会存在数据的实时性相对较差的情况。在数据存储缓冲区的时候也是有时间的,并且需要将缓冲区的数据全部存满以后才会进行写出。如果是希望实时的去完成数据的写出,可以手动的调用flush方法。在close方法中已经提供了在关闭资源的时候,强制输出缓冲区的数据。

 package cn.tedu.io;
 
 import java.io.BufferedOutputStream;
 import java.io.FileOutputStream;
 
 /**
  * 缓冲字节输出流写出数据的缓冲区案例演示
  * @author cjn
  *
  */
 public class BOS_Flush {
 
     public static void main(String[] args) throws Exception {
         //1.使用流连接
         //创建文件字节输出流(低级流)对象
         FileOutputStream fos = new FileOutputStream("bao.txt");
         //创建缓冲字节输出流(高级流)对象
         BufferedOutputStream bos = new BufferedOutputStream(fos);
         
         //2.准备存储的字符串数据
         String str = "今天天气不错,风和日丽的!!!";
         
         //3.将字符串内容转化为字节序列
         byte[] data = str.getBytes("utf-8");
         
         //4.写出数据
         bos.write(data);
         //bos.flush();//此处写不写结果都一样,就人眼而言看不出来差别,但计算机能识别
         System.out.println("写出完毕!!!");
         
         //5.关闭资源
         bos.close();
 
    }
 
 }

 

二、对象流

2.1 定义:

       对象流是高级流,也可以称之为是处理流,可以对Java中任何的对象进行读写操作。

2.2 应用场景:

       原本Java对象是存储在内存中的,有时往往需要将Java对象保存到外部设备中,再有时我们也需要将对象传输到另一台计算机等这样的操作,可以使用对象流。

2.3 对象序列化

       将Java对象转换为字节序列,这个过程就称之为对象的序列化。相反而言,有字节序列需要进行转换为Java对象,这个过程就称之为反序列化

2.4 代码业务场景要求:

  • 定义一个Person类,类中包含姓名、性别、年龄、爱好属性(每个人的爱好都不止一个,这个爱好属性为字符串数组类型)。

  • 将Person对象进行序列化到文件中,文件名为person.obj。

  • 将Person对象从person.obj文件中反序列出来,并打印输出到程序的控制台中。

2.5 将对象进行序列化要求:

  • 该对象需要实现一个序列化接口Serializable,实现该接口以后,不需要添加任何该接口重写的方法。

  • 在类中需要提供一个serialVersionUID

Person:

 package cn.tedu.io;
 
 import java.io.Serializable;
 import java.util.Arrays;
 
 public class Person implements Serializable{
 
     /**
      * 1.Person类中添加姓名、性别、年龄、爱好:属性
      * 2.生成set和get方法:设置或获取属性
      * 3.添加构造器:初始化属性
      * 4.添加toString方法:重写该方法后,输出对象时直接输出对象内容
      * 5.添加equals方法:重写该方法后,比较时直接比较对象内容而非地址值
      * 6.实现序列化接口:Person类实现Serializable接口
      * 7.添加serialVersionUID
      */
     
     
     private static final long serialVersionUID = 1L;
 
     private String name;
     private char gender;
     private int age;
     private String[] hobby;
     
     public Person() {}
     
     public Person(String name, char gender, int age, String[] hobby) {
         super();
         this.name = name;
         this.gender = gender;
         this.age = age;
         this.hobby = hobby;
    }
     
     public String getName() {
         return name;
    }
     public void setName(String name) {
         this.name = name;
    }
     public char getGender() {
         return gender;
    }
     public void setGender(char gender) {
         this.gender = gender;
    }
     public int getAge() {
         return age;
    }
     public void setAge(int age) {
         this.age = age;
    }
     public String[] getHobby() {
         return hobby;
    }
     public void setHobby(String[] hobby) {
         this.hobby = hobby;
    }
 
     @Override
     public String toString() {
         return "Person [name=" + name + ", gender=" + gender + ", age=" + age + ", hobby=" + Arrays.toString(hobby) + "]";
    }
 
     @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
         result = prime * result + age;
         result = prime * result + gender;
         result = prime * result + Arrays.hashCode(hobby);
         result = prime * result + ((name == null) ? 0 : name.hashCode());
         return result;
    }
 
     @Override
     public boolean equals(Object obj) {
         if (this == obj)
             return true;
         if (obj == null)
             return false;
         if (getClass() != obj.getClass())
             return false;
         Person other = (Person) obj;
         if (age != other.age)
             return false;
         if (gender != other.gender)
             return false;
         if (!Arrays.equals(hobby, other.hobby))
             return false;
         if (name == null) {
             if (other.name != null)
                 return false;
        } else if (!name.equals(other.name))
             return false;
         return true;
    }
     
     
 }

2.6 ObjectOutputStream实现对象的序列化

       ObjectOutputStream提供了对对象进行序列化的API方法`writeObject(Object o)`,调用该方法可以将给定的对象转换为字节序列,然后进行写出。

 package cn.tedu.io;
 
 import java.io.FileOutputStream;
 import java.io.ObjectOutputStream;
 
 /**
  * 将对象进行序列化案例
  * @author cjn
  *
  */
 public class OOSDemo {
 
     public static void main(String[] args) throws Exception {
         //1.创建文件字节输出流(低级流)对象
         FileOutputStream fos = new FileOutputStream("person.obj");
         //2.创建对象输出流(高级流)对象
         ObjectOutputStream oos = new ObjectOutputStream(fos);
         
         //3.创建需要进行序列化的Person实例,注意:hobby是数组类型,需要新建字符串类型数组对象
         Person p = new Person("苍老师", '男', 50, new String[] {"健身","拍片","讲课"});
         
         //4.将person对象进行序列化
         oos.writeObject(p);
 
         System.out.println("人物的存档完毕");
         //5.关闭资源
         oos.close();
    }
 
 }

将对象写到文件中需要经历的两个操作

  1. 将给定版本的对象转换为一组字节--->对象的序列化

  2. 再将这组字节写到文件中--->数据的持久化

2.7 ObjectInputStream实现对象的反序列化

       ObjectInputStream提供了对对象进行反序列化的API方法:readObject();,调用该方法可以从流中将字节序列转换为对应的对象。

 package cn.tedu.io;
 
 import java.io.FileInputStream;
 import java.io.ObjectInputStream;
 
 /**
  * 将对象进行反序列化案例
  * @author cjn
  *
  */
 public class OSISDemo {
 
     public static void main(String[] args) throws Exception {
         //1.创建文件字节输入流(低级流)对象
         FileInputStream fis = new FileInputStream("person.obj");
         
         //2.创建对象输入流(高级流)对象
         ObjectInputStream ois = new ObjectInputStream(fis);
         
         //3.对person对象进行反序列化,注意:此处需要强制类型转换(Object对象--->Person对象)
         Person p = (Person)ois.readObject();
         System.out.println(p);
         
         //4.关闭资源
         ois.close();
    }
 
 }

2.8 transient关键字

       对象在进行序列化操作时,往往需要进行序列化的内容较多,如果说希望在进行对对象序列化操作时,忽略掉一些业务不需要的属性时,可以在属性的类型前面添加transient,从而可以达到序列化时瘦身的效果。

     private String name;
     private char gender;
     private transient int age;//输出对象属性时,该属性值为默认值
     private String[] hobby;

 

三、字符流

3.1 定义:

       所有的字符流都是高级流,字符流对数据进行读写操作的单位是以字符为单位进行读写的,所以只能对文本数据进行读写操作。底层源码中读写的单位仍然是按照字节进行读写操作的。

3.2 字符转换流(转换流/高级流/处理流)

  • OutputStreamWriter:字符输出流,该流在进行使用的时候,可以设置指定的字符集编码,并按照指定的字符集编码将字符转换为对应的字节,然后进行写出。

  • InputStreamReader:字符输入流,该流在使用的过程中也可以设置字符集编码,就会按照指定的字符集编码从流中将字节转换为字符,并进行读取。

注意

       所有的字符流都是高级流,大部分的字符流只能处理字符流,不能直接处理字节流(低级流)。所以提供了转换流,可以和字节流进行流连接,从而起到承上启下的作用。

3.3 使用字符输出流OutputStreamWriter向文件中写出数据

 package cn.tedu.io.two;
 
 import java.io.FileOutputStream;
 import java.io.OutputStreamWriter;
 
 /**
  * 字符输出转换流案例演示
  * @author cjn
  *
  */
 public class OSWDemo {
 
     public static void main(String[] args) throws Exception {
         //1.创建文件字节输出流(低级流)对象
         FileOutputStream fos = new FileOutputStream("osw.txt");
         
         //2.创建字符输出转换流(高级流)对象,并指定字符集编码
         OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
         
         //3.写出文本数据
         osw.write("同学们最近学习都很刻苦,");
         osw.write("革命尚未成功,通知仍需努力!!!");
         
         System.out.println("数据写出完毕");
         
         //4.关闭资源
         osw.close();
 
    }
 
 }

3.4 使用字符输入流向文件中读取数据

 package cn.tedu.io.two;
 
 import java.io.FileInputStream;
 import java.io.InputStreamReader;
 
 /**
  * 字符输入转换流案例演示
  * @author cjn
  *
  */
 public class ISRDemo {
 
    public static void main(String[] args) throws Exception {
        //1.创建文件字节输入流(低级流)对象
        FileInputStream fis = new FileInputStream("osw.txt");
         
        //2.创建字符输入流(高级流)对象,并指定字符集
        InputStreamReader isr = new InputStreamReader(fis, "utf-8");
         
        //3.读取osw.txt文本中的数据内容
        int len = 0;/实际读取的字节大小
        while ((len=isr.read()) != -1) {//未到达末尾
            //将整数int变量存储的字节转换为字符char类型
            System.out.print((char)len);//此处需要手动转换
        }
        System.out.println();
        System.out.println("读取数据完毕!!!");
        //4.关闭资源
        isr.close();
    }
 
 }

3.5 字符缓冲流

3.5.1 PrintWriter

    PrintWriter是一个高级流,内部维护着BufferedWriter,可以按照行进行写出字符串内容。PrintWriter是一个具备自动行刷新功能的缓冲字节输出流,虽然具备自动行刷新功能,但是需要手动进行开启,如果不开启则认为不使用自动行刷新功能。

 //Java中提供的PrintWriter方法
 public PrintWriter (Writer out)
 //支持自动行刷新的使用,第二个参数传递true
 public PrintWriter(Writer out,boolean autoFlush)
 public PrintWriter(OutputStream out)
 //支持自动行刷新的使用,第二个参数传
 public PrintWriter(OutputStream out, boolean autoFlush)
 public PrintWriter(String fileName)
 public PrintWriter(String fileName,String csn)
 public PrintWriter(File file)
 ......

       当没有进行行刷新前,程序员写出的数据并没有真正的写到文件中,而是只保存在内容中,刷新以后才会写出到文件中。如果程序中没有进行调用行刷新方法,当程序都执行完以后,会自动进行刷新。也就是只有到程序中的代码全部执行完以后一次性写出到文件中,如果数据量较大的情况,程序的执行性能会有影响。

3.5.2 PrintWriter提供的写出方法

       使用PrintWriter写出字符串时,调用的方法不再是write()方法,而是使用printprintlnprintln方法是在写出数据后自动添加一个换行符,并且支持行刷新的API方法,而print不支持行刷新并且不会添加一个换行符。

 package cn.tedu.io.two;
 
 import java.io.PrintWriter;
 
 /**
  * PrintWriter字符缓冲输出流案例
  * @author cjn
  *
  */
 public class PWDemo {
 
     public static void main(String[] args) throws Exception {
         //1.创建字符缓冲输出流(高级流)对象,并指定字符集
         PrintWriter pw = new PrintWriter("pw.txt","utf-8");
         
         //2.写出字符串内容
         pw.println("这个案例是字符缓冲输出流案例");
         pw.println("这个案例写着真简单,效率还高");
         System.out.println("写出数据完毕!!!");
         
         //3.关闭资源
         pw.close();
 
    }
 
 }

3.5.3 PrintWriter 底层源码实现

流连接示意图:

 

3.5.4 PrintWriter 开启自动行刷新功能

 package cn.tedu.io.two;
 
 import java.io.BufferedWriter;
 import java.io.FileOutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 
 /**
  * 使用PrintWriter开启自动行刷新功能
  * @author cjn
  *
  */
 public class PWDemo02 {
 
     public static void main(String[] args) throws Exception {
         //1.创建文件字节输出流(低级流)对象
         FileOutputStream fos = new FileOutputStream("pw2.txt");
         
         //2.创建字符输出转换流(高级流)对象
         OutputStreamWriter osw = new OutputStreamWriter(fos);
         
         //3.创建字符缓冲输出流(高级流)对象
         BufferedWriter bw = new BufferedWriter(osw);
         
         //4.创建字符缓冲输出流(高级流+自动行刷新)对象,true为开启自动行刷新
         PrintWriter pw = new PrintWriter(bw,true);
         
         //5.写出字符串内容
         pw.println("同学们再坚持一会,");
         pw.println("明天就休息了,可以自己消化一下");
         
         System.out.println("写出数据完毕!!!");
         
         //6.关闭资源
         pw.close();
    }
 
 }

3.5.5 BufferedReader字符缓冲输入流

       BufferedReader是一个高级流,内部维护了一个缓冲区,可以提高读取数据的效率,需要搭配转换流和低级流进行使用,使用该流可以对文本中的数据进行按行读取。

       BufferedReader提供了一个可以按行进行读取的API方法:readLine()方法,读取以后返回的字符串内容不包含换行符,也就意味着将换行符前面的所有字符以字符串的形式进行返回,如果读取到了文件末尾,返回值为null。

 package cn.tedu.io.two;
 
 import java.io.BufferedReader;
 import java.io.FileInputStream;
 import java.io.InputStreamReader;
 
 /**
  * 缓冲字符输入流案例
  * @author cjn
  *
  */
 public class BRDemo {
 
     public static void main(String[] args) throws Exception {
         //1.创建文件字节输入流(低级流)对象
         FileInputStream fis = new FileInputStream("pw2.txt");
         
         //2.创建字符输入转换流(高级流)对象
         InputStreamReader isr = new InputStreamReader(fis);
         
         //3.创建缓冲字符输入流(高级流)对象
         BufferedReader br = new BufferedReader(isr);
         
         //4.按行进行读取数据
         /*
              int len = 0;
              while ((len=输入流.read()) != -1){
  读取数据
              }
          */
         String line = null;//存放实际读取的字符串内容
         while ((line=br.readLine()) != null) {//未到达末尾
             System.out.println(line);
        }
         
         //5.关闭资源
         br.close();
    }
 
 }

 

posted @ 2021-06-20 19:42  Coder_Cui  阅读(98)  评论(0编辑  收藏  举报