JAVA笔记整理-File文件类-包装流

一、包装流

定义: 在原始字节流或字符流的基础性,为了提高读写效率进行再次处理的流, 称为包装流/处理流

1、缓存字节流 BufferedInputStream 、BufferedOutputStream

​ 由于原始流在文件读写时 效率比较低(操作文件本身占用资源较多),可以通过创建缓冲区的方式提高读写效率, 将读取/写出的数据线放入缓冲区,到达一定数量后再次冲缓冲区读取/写出

​ mark(readLimit) 与 reset()用法

其中reset不能单独使用,必须mark(readLimit) ,readLimit表示标记后最多读取的上限,但是这里标记后读取的内容与BufferedInputStream的缓冲大小有关,比由上限决定,也就是说读取的内容超出上限可以继续重置到mark的位置。

public static void main(String[] args) throws IOException {
        //创建缓冲流
        InputStream is = new FileInputStream("d:/myfile.txt");
        BufferedInputStream bis = new BufferedInputStream(is);
        //是否支持mark  或 reset
        System.out.println(bis.markSupported());
        System.out.println((char)bis.read());//97
        //重置
        bis.mark(3);  // pos标记往后退三个  最多可以读取字节上限

        System.out.println("再次读取:"+(char)bis.read());
        System.out.println("再次读取:"+(char)bis.read());
        System.out.println("再次读取:"+(char)bis.read());
        System.out.println("再次读取:"+(char)bis.read());

       bis.reset(); // 这里 重置后 退回到3个以前的位置

        // 重置后输出
        int n =0;
        while( (n = bis.read()) !=-1){
            System.out.println("重置后;"+(char)n);
        }
        //关闭流
        bis.close();
        is.close();

    }

2、缓存字符流 (BufferedReader 、BufferedWriter)

  public static void main(String[] args) throws IOException {
        // 缓冲字符流 可以一行一行读取   、写出
        BufferedReader br = new BufferedReader(new FileReader("d:/小众网站.txt"));
        //读一行
//        System.out.println(br.readLine());
//        System.out.println(br.readLine());
//        System.out.println(br.readLine());
        String s = null;  //读的数据为空 则不需要读
        while( (s = br.readLine()) !=null){
            System.out.println(s);
        }

        br.close();

        //缓冲写出流
        FileOutputStream   pw =  new FileOutputStream("d:/abcd.txt");
        //由于字节流不能直接放入 字符缓冲区,需要将它转成字符流  使用转换流并可以指定编码格式
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(pw));
        bw.newLine();// 开启新一行(换行)
        bw.write("这是测试转换流的方式");

        bw.close();
    }

3、打印流(输出流) PrintWriter 、PrintStream

 public static void main(String[] args) throws FileNotFoundException {
        // 打印流 ,提供一些打印输出方法

        PrintWriter pw = new PrintWriter("d:/abcd.txt");
        pw.print(100);
        pw.println('a');//换行打印
        pw.print("hello");


        pw.close();

        //System.out   字节打印流 PrintStream

4、数据字节流DataInputStream、DataOutputStream

它们用于读入写出Java基本数据类型的数据到文件或其他设备端,它们也属于包装流

DataOutputStream 常用方法

  • ​ writerByte(byte):写一个字节到设备或文件
  • ​ writerChar(char):写一个字符到设备或文件
  • ​ writerInt(int):写一个4个字节的int到设备或文件
  • ​ writer(boolean):写一个boolean类型到设备或文件
  • ​ writerDouble(double):写一个double类型到设备或文件
  • ​ writerFloat(float):写一个float类型到设备或文件
  • ​ writerLong(long):写一个long类型到设备或文件
  • ​ writerShort(short):写一个short类型到设备或文件
  • ​ writerUTF(String):写一个字符串类型到设备或文件

DataInputStream: 读指定文件的数据,可以读数据类型

  • ​ int readInt() :读一个int类型
  • ​ short readShort():读一个short类型
  • readByte():读一个字节类型
  • ​ read():读一个字节类型
  • ​ readDouble(): 读一个double类型
  • ​ readFloat():读一个float类型
  • ​ readChar():读一个字符类型
  • ​ readBoolean():读一个boolean类型
  • ​ readLong() :读一个long类型
 public static void main(String[] args) throws IOException {
        //创建数据写出流
        DataOutputStream dos = new DataOutputStream(
                new FileOutputStream("d:/data.txt"));
        //写一个int类型 依次写出4个字节
        dos.writeInt(100);
        dos.writeBoolean(true);
        //关闭
        dos.close();
     
     //读取文件 创建数据读入流  ,需要按写的顺序读进来
        DataInputStream dis = new DataInputStream(
                new FileInputStream("d:/data.txt"));
        //读一个int类型 (依次读4个字节)
        int num = dis.readInt();
        System.out.println("读取的数据:"+ num);
        System.out.println("读的数据:"+dis.readBoolean());
        dis.close();


    }

5、转换流

​ 转换流是将字节流转成字符流的桥梁, 也可以在转换时指定编码格式。 InputStreamReader 和 OutputStreamWriter

   public static void main(String[] args) throws IOException {
         // 字节流转成字符流
         InputStream is = new FileInputStream("d://小众网站.txt");
         InputStreamReader isr = new InputStreamReader(is);
         //缓冲流 读取数据
         BufferedReader br = new BufferedReader(isr);
         //读一行
        String str =null;
        while( (str= br.readLine()) !=null){
            System.out.println(str);
        }
        //关闭流
        br.close();
        isr.close();
        is.close();

    }
 public static void main(String[] args) throws IOException {
        // 创建 字节转成字符的 写出流  FileOutputStream os  =
        FileOutputStream fos = new FileOutputStream("d://data.txt");
        //指定编码  GBK 格式一个汉字占2个字节   UTF-8 格式一个汉字占3个字节
        OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
        //缓冲形式的
        BufferedWriter bw = new BufferedWriter(osw);

        bw.write("你好");
        bw.newLine();
        bw.write("我不好");

        bw.close();


    }

6、随机字节流

RandomAccessFile 是随机字节流,它是一个可读可写的流 ,在文件操作时指定该对象的模式(model)后,可以读数据或写数据

实现 DataInputStream和DataOutputStream类

构造器:

RandomAccessFile rm = new RandomAccessFile(File ,mode);

​ RandomAccessFile rm = new RandomAccessFile(String ,mode);

mode表示对象的模式

r: 表示该对象只能读 不能写

rw/rws/rwd :表示该 对象是可读可写的;

public static void main(String[] args) throws IOException {
        //创建可读 的流
        RandomAccessFile reader = new RandomAccessFile("d://data.txt","r");
        //创建可读可写的 的流
        RandomAccessFile writer = new RandomAccessFile("d://data-1.txt","rw");
        // 读和写的过程和之前一样
        byte [] b= new byte[10];
        int len=0;
        while( (len = reader.read(b)) !=-1){
            writer.write(b , 0 , len);
        }
        System.out.println("复制成功");
        //关闭流
        writer.close();
        reader.close();

    }

skipByte 和 seek的区别

      // 跳字节读取
        RandomAccessFile raf = new RandomAccessFile("d:/data.txt","rw");
        // 跳过2个字节
        raf.skipBytes(2);
        System.out.println((char)raf.readByte()); //3
        System.out.println("当前偏移量:"+raf.getFilePointer());//3
        // 又丢弃1个字节   从当前位置 往后偏移1位
        raf.skipBytes(1);
        System.out.println("修改后的偏移量"+raf.getFilePointer());//4
        System.out.println("偏移后的读取数据:"+(char)raf.readByte()); //5
        raf.close();


        // seek用法
        RandomAccessFile raf2 = new RandomAccessFile("d:/data.txt","rw");
        //  设置当前读取的位置  ,从0开始计算  ,指定n ,就从n的下一个字节 读取
        raf2.seek(                                 );
        System.out.println("seek后的数据:"+(char)raf2.readByte());//3
        raf2.seek(1); // 又从0开始 设置偏移量为1  
 		System.out.println("修改后的偏移量"+raf.getFilePointer());//1
        System.out.println("seek后的数据:"+(char)raf2.readByte())//2
        raf2.close();

7、对象序列化流

​ 对象流也称为序列化流,用于存储对象和读取对象的字节流,也是属于包装流

序列化和反序列化

​ 将内存中的对象(Object,集合类等)保存到磁盘、网络介质、其他设置的过程,并在合适的时间能获取磁盘文件/网络的数据 ,这个过程就是对象的序列化和反序列化。

为什么需要序列化和反序列化呢?

​ 在之前文件中存储的文本信息,这样不便于对数据的分类和操作,如果可以做到直接对对象的读和写这样可大大提高编程效率,并最大程度保证对象的完整性。

Java-IO中实现对象序列化的两种方式:

  1. 实现Serializable接口
  2. 实现Externalizable接口

Serializable接口

对象需要实现该接口,但是它没有任何需要实现的方法,只有一个用于标记该类可序列化的唯一标识。 任何类需要序列化都必须标记该变量

   public class User implements Serializable {
       // 对于能区分其他类的唯一表示
       private static final long serialVersionUID = 1L;
   
        private int uid;
        private String name;
        private String password;
        //  有一部分属性不能序列化
   
       public int getUid() {
           return uid;
       }
   
       public void setUid(int uid) {
           this.uid = uid;
       }
   
       public String getName() {
           return name;
       }
   
       public void setName(String name) {
           this.name = name;
       }
   
       public String getPassword() {
           return password;
       }
   
       public void setPassword(String password) {
           this.password = password;
       }
   
       @Override
       public String toString() {
           return "User{" +
                   "uid=" + uid +
                   ", name='" + name + '\'' +
                   ", password='" + password + '\'' +
                   '}';
       }
   }
      //创建序列化的对象流  从内存到文件
           ObjectOutputStream oos = new ObjectOutputStream(
                   new FileOutputStream("d:/user.txt"));
           User user= new User();
           user.setUid(1001);
           user.setName("admin");
           user.setPassword("123456");
           //序列化对象
           oos.writeObject(user);
           //关闭流
           oos.close();
   
           // 反序列化:  将文件中的数据 再读入到内存中 ,需要一个读的流 ObjectInputStream
           ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d://user.txt"));
           //  反序列化尽量只读一次 (也可以读多次, 如何写出就如何读入)
           Object obj = ois.readObject();
           if(obj instanceof  User){
               User u = (User)obj;
               System.out.println("反序列化的结果:"+u);
           }
           //关闭流
           ois.close();
   
public interface Externalizable extends java.io.Serializable

Externalizable接口

public class Student implements Externalizable {
    private  int id;
    private String name;
    private String sex;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    // 自定义可序列化的属性
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(this.id);
        out.writeUTF(this.name);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.name = in.readUTF();
    }

    public Student(int id, String name, String sex) {
        this.id = id;
        this.name = name;
        this.sex = sex;
    }

    public Student( ) {

    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

 public static void main(String[] args) throws IOException, ClassNotFoundException {
         // 创建序列化类
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("d:/stu.txt"));
        //创建学生
        List<Student> list = new ArrayList<>();
        list.add(new Student(1001,"张飞","男"));
        list.add(new Student(1002,"刘备","男"));
        list.add(new Student(1003,"小乔","女"));
        // 将集合序列化
        oos.writeObject(list);
        //关闭
        oos.close();

        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("d:/stu.txt"));
        //读
        Object obj = ois.readObject();
        if(obj instanceof  List){
            List<Student> list2 = (List<Student>)obj;
            for(Student s : list2){
                System.out.println(s);
            }
        }
        //关闭流
        ois.close();



    }

问题: 能否自定义序列化的属性 :

这里可以采用方式二,实现Externalizable,
并重写两个方法 接口继承而来,
在其基础上新增了两个未实现方法:readExternal(ObjectInputStream)和 writeExternal(ObjectOutputStreawm)
自定义需要序列化的属性

问题: 哪些属性不能实现序列化 ?

1、类中的static修饰的属性不能序列化

2、类中属性被transient修饰的不能序列化 例如 transient private Integer age = null;

3、实现Externalizable接口的类的属性不能全部序列化,必须手动写可序列化的属性。

posted @ 2020-11-08 15:31  落雨♡̶初晴  阅读(186)  评论(0编辑  收藏  举报