Java Lecture 16 块读写、节点流、处理流、高级流、缓冲流、对象流和序列化

本文介绍了如何通过块读写技术提高Java文件读写效率,包括使用块读写优化、节点流与处理流的区别、缓冲流的应用以及对象流的序列化和反序列化。通过实例演示了如何在Java中利用这些技术进行文件操作和对象传输。
摘要由CSDN通过智能技术生成

一、块读写

解决拷贝速度,提高每次读写量,减少读取次数,就可以提高读写效率。

因为原先的读写方式(单字节读写)是一种随机读写的形式。

块读写是每次读取一组字节的形式,每次多读一些字节内容,减少读取次数。

无法确定一个文件有多少个字节,所以无法确定设定的块包含多少字节能够刚好读完。

package io;
​
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
​
/**
 * 拷贝demo02 -------:使用块读写的方式提高读写效率
 */
public class CopyDemo02 {
​
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("./神里.jpeg");//建立输入流管道链接源文件
        FileOutputStream fos= new FileOutputStream("./神里3.jpeg");//建立输出流体管道 链接目标写出的文件
        块读写的思路:
         *  java.io.inputStream 超类 中定义 read
         *  int read(byte[] data); 因为有方法重载
  所以读取时也可以传入字节数组,一次读取的字节数,取决于给定的字节数组的容量
         *  假设源文件 有 7个 字节
         *  神里.jpeg ----  11100010  00110101 11001100   01010101  01010101  01110101  01011111
         *  byte[] data = new byte[4];                             ^^^^^^^^   第二次只能读取3个字节
         *  int length = fis.read(data)   length代表实际读取到的字节数。
         *  data : [11100010  00110101 11001100    01010101 ]
         *  length 此时为 4
         *  length = fis.read(date);
         *   data : [01010101  01110101  01011111    01010101 ]
         *          |--------新读取的3个字节-------|   |--旧数据--|
         *  length 此时为 3
         
​
        int length;
        /**
         *  1b  ---- 8位
         *   b  ---字节
         *  1024b  ---- 1kb
         *  1024kb ---- 1Mb
         *  1024MB ---- 1GB
         *  1024GB ---- 1TB
         * */
​
        byte[] data = new byte[1024*10];//10kb
        long start = System.currentTimeMillis();
        while ((length =fis.read(data))!= -1){//读取后看下 如果不等于-1  那么就不是文件末尾
            fos.write(data);//将读取到的data数组中的字节 一次行写入到文件中。
        }
        long end = System.currentTimeMillis();
        System.out.println("拷贝完毕,总共耗时:"+(end-start)+"ms");//1ms
        fis.close();//释放输入流体
        fos.close();//释放输出流体
​
    }
}
​

注意,fis.read()和fis.read(data)是不一样的概念。fis.read()输出的是下一个字节的int值,但fis.read(data)是指fis在data数组里实际读取到的字节数量,这就是为什么上面的length = 4。

复制之后,目标文件的大小可能会比原有的文件大小大一些,因为多读了一部分倒数第二个字节的数据,但是不影响使用。具体看下面的过程就明白了:

倒数第二次写入时:

 最后一次写入时:

 

所以目前的问题:通过快读写的方式,效率提高了,但是写出时,会把多余的旧数据一并写出到目标文件中,导致目标文件与源文件大小不一样。

解决方法:写出时,使用当次读取到的实际字节长度即可

write有三个重载的方法:

write(int b )写入传入的字节

write(byte[] byte)写入传入的字节数组

write(byte[] byte, int off, int len)这个就是解决此问题的方法

第一个参数:需要写入的字节数组

第二个参数:从字节数组的哪个地方开始写入(从头开始就写0)

第三个参数:实际要写入文件的字节数量

所以这里直接length = fis.read(data),然后fos.write(data,0,length)就能解决问题了。

这里就相当于,每次按实际读取到的length长度写入data数组的字节数

      byte[] data = new byte[1024*10];//10kb
        long start = System.currentTimeMillis();
        while ((length =fis.read(data))!= -1){//读取后看下 如果不等于-1  那么就不是文件末尾
//            fos.write(data);//将读取到的data数组中的字节 一次行写入到文件中。
            /**
             * write(int b)  代表传入字节 来写出
             * write(byte[] byte)  代表传入一个字节数组 来写出
             * write(byte[] byte,int off ,int len) 1.需要写出的字节数组 2. 0   3.length 传入实际读取的字节长度
             */
            fos.write(data,0,length); //使用含有三个参数的write方法来实现 copy 解决:目标文件多出字节的问题。
        }
        long end = System.currentTimeMillis();
        System.out.println("拷贝完毕,总共耗时:"+(end-start)+"ms");
        fis.close();//释放输入流体
        fos.close();//释放输出流体
​
    }
}
​

如何在当前项目路径下在文件中写入字符串的内容?(很常用)

先读取demo.txt文件作为输出文件,写出要写入的字符串,因为要把它转成字节数组才能传入,所以我们要先进行转换。

注意:每个汉字在内存中都是占2个字节的,我国用的叫GBK编码表

为防止编码格式不同,我们使用国际统一标准Unicode(万国码),基本上用的都是utf-8

所以在写入时,要使用

byte[] data = str.getBytes(standardCharsets.UTF-8)

f.write(data)

这样就写入成功了,具体代码如下:

 接下来,把这个文件的内容再读取到程序中:

fis.avaiable()方法可以获取当前文本内容的大致长度

package io;
​
​
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
​
/**
 * 读取项目路径下demo.txt文件的内容到程序中
 */
public class ReadStringDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("./demo.txt");
        int len = fis.available(); 
        //available方法,可以获取到当前文本内容的长度 (可以估算长度)
        byte[] data = new byte[len];//开辟估算长度的空间
        fis.read(data);//一次读取到data数组中。
        String line = new String(data, StandardCharsets.UTF_8);
        //将data数组中读到的字节转换为字符串 ,转换的编码格式UTF8
        System.out.println(line);//打印转换后的内容
        fis.close();//释放流体
​
​
    }
}
​

FileOutStream类还提供的追加的构造方法,在操作现文件基础上追加内容。

   FileOutputStream fos = new FileOutputStream("./demo.txt",true);

equalsIgnoreCase可以在判断时,忽略输入的内容的大小写。

二、Java流分类: 节点流和处理流

除了文件流之外,还有其他的流。我们学的文件流就是节点流里的一种。

Java里的流分为节点流和处理流

(1)节点流是低级流:

定义:用来真实连接程序与另一端(可以理解为文件)的管道。读写一定是建立在节点流的基础上进行的,例如:文件流就是节点流.

生动形象理解一下低级流(就是至少要有个管道),但是自来水我们不能直接喝,所以需要在这个管道上加一些东西,比如净水器来实现我们的需求,那这就是高级流。

低级流(比较基础):

处理流是高级流,且是不能够独立存在的,必须链接在其它流(不一定是低级流,但肯定得有流)基础上,

目的:当数据流经过它时,可以对数据做一些加工处理,简化我们的同等操作。

(2)高级流:

实际在开发过程,我们经常会串联一组高级流最终连接到低级流上,让读写的数据以流水线的方式加工处理,这个操作称之为流的链接。

处理流和高级流的关系可以用以下方式描述:

  1. 处理流是对基本流进行包装或装饰,为基本流添加了额外的功能,提供了更高级别的操作接口。
  2. 高级流是基于处理流构建的,通过串联多个处理流的方式,将多个功能组合在一起,形成更高级别的流操作接口。

高级流:

1. 缓冲流:

缓冲流是一对高级流(高级流的一种),作用是加快读写效率。

缓冲流有两种:

缓冲输入流 -->java.io.BufferedInputStream

缓冲输出流--->java.io.BufferedOutputStream

就是把原先创建的输入流作为参数,放入到缓冲输入流中作为参数,创建高级缓冲输入流

输出流也是一样的。

然后,原先后面写的fis和fos都可以换成bis和bos了。

最后,直接释放高级流就好了,内部也会把对应的低级流自动释放掉。

package io;
​
import java.io.*;
​
/**
 * 缓冲流的使用演示类:
 * 缓冲流是高级流,功能:加快读写效率
 */
public class BOSDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("./神里.jpeg");//建立输入流管道链接源文件
        BufferedInputStream bis = new BufferedInputStream(fis);//创建高级缓冲输入流
​
        FileOutputStream fos= new FileOutputStream("./神里6.jpeg");//建立输出流体管道 链接目标写出的文件
        BufferedOutputStream bos = new BufferedOutputStream(fos);//创建高级缓冲写出流
        int length;
        long start = System.currentTimeMillis();
        while ((length =bis.read())!= -1){//读取后看下 如果不等于-1  那么就不是文件末尾
            bos.write(length);//将读到的字节length内容 写出到目标文件的环节
        }
        long end = System.currentTimeMillis();
        System.out.println("拷贝完毕,总共耗时:"+(end-start)+"ms");
        bis.close();//释放高级流 内部也会把对应低级流释放
        bos.close();//释放高级流 内部也会把对应低级流释放
    }
}

生动形象理解一下bis(缓冲输入流)和bos (缓冲输出流):

 缓冲字节输出流,缓冲区会存在一些问题。

缓冲流本质上是块读写, 内部做的块容量8kb, 如果内容较少,而且没有刷新的话,系统是不会立刻写出到文件的,导致有可能不能及时存入数据,可以通过手动打点调用 flush刷新。close()方法里本身就带有这个刷新方法。

package io;
​
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
​
/**
 * 缓冲输出流:
 *          在当前项目路径下写出一个内容到文件中。
 */
public class BOSDemo02 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("./bos.txt",true);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        String line = "同学们";
        bos.write(line.getBytes(StandardCharsets.UTF_8));
        System.out.println("写出完毕!");
        //缓冲流是块读块写的方式,如果写入的内容较少(不满足内部声明的数组容量),则有可能不会立刻写出
        bos.flush();//手动刷新。
        bos.close();// 关闭缓冲流时,内部会执行以flush刷新操作。
    }
}

2. 对象流(也是一种高级流)

对象流作用:进行对象的序列化和反序列化的操作。

对象的序列化:将一个对象按照其结构转换成为一组字节的过程。

对象的反序列化:将一组字节(必须是序列化的一组字节)还原为一个对象的过程。

好处:有了对象流,我们可以很方便区读写任何Java对象

对象输入流:java.io.ObjectInputStream

对象输出流:java.io.ObjectOutputStream

对象如果需要序列化是需要为当前对象的模板类添加实现Serializable接口。

用一个例子来说明(待续):

人的模板类:

package io;
​
import java.io.Serializable;
import java.util.Arrays;
​
/**
 * 人类模板
 */
public class Person implements Serializable {
    private String name;
    private int age;
    private String gender;
    private String[] otherInfo;//其它信息
​
    //1.在当前空白区域 alt + insert 或 右键 找到 Generate 选中
    //选择 get and set 方法 全选生成
    //重复操作1.  选择 toString
    //重复操作1.  选择 第一个选择 生成构造方法 全选 生成。
    public Person(String name, int age, String gender, String[] otherInfo) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.otherInfo = otherInfo;
    }
​
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", otherInfo=" + Arrays.toString(otherInfo) +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public String getGender() {
        return gender;
    }
​
    public void setGender(String gender) {
        this.gender = gender;
    }
​
    public String[] getOtherInfo() {
        return otherInfo;
    }
​
    public void setOtherInfo(String[] otherInfo) {
        this.otherInfo = otherInfo;
    }
}
​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq030928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值