跳至主要內容

Java - IO2

codejava约 1086 字大约 4 分钟

IO流2

缓冲流

虽然普通的文件流读取文件数据非常便捷,但是每次都需要从外部I/O设备去获取数据,由于外部I/O设备的速度一般都达不到内存的读取速度,很有可能造成程序反应迟钝,因此性能还不够高,而缓冲流正如其名称一样,它能够提供一个缓冲,**提前将部分内容存入内存(缓冲区)**在下次读取时,如果缓冲区中存在此数据,则无需再去请求外部设备。同理,当向外部设备写入数据时,也是由缓冲区处理,而不是直接向外部设备写入。

20250225181159
20250225181159

缓冲字节流

缓冲字节读取流 BufferedInputStream

要创建一个缓冲字节流,只需要将原本的流作为构造参数传入BufferedInputStream即可:

public static void main(String[] args) {
    try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"))){   
        //传入FileInputStream
        System.out.println((char) bufferedInputStream.read());   //操作和原来的流是一样的
    }catch (IOException e){
        e.printStackTrace();
    }
}

缓冲流特性

1. 装饰着模式

实际上进行I/O操作的并不是BufferedInputStream,而是我们传入的FileInputStream,而BufferedInputStream虽然有着同样的方法,但是进行了一些额外的处理然后再调用FileInputStream的同名方法,这样的写法称为装饰者模式

对应缓冲流的close源码:

public void close() throws IOException {
    byte[] buffer;
    while ( (buffer = buf) != null) {
        if (bufUpdater.compareAndSet(this, buffer, null)) {  
            //CAS无锁算法,并发会用到,暂时不需要了解
            InputStream input = in;
            in = null;
            if (input != null)
                input.close();
            return;
        }
        // Else retry in case a new buf was CASed in fill()
    }
}

实际上这种模式是父类FilterInputStream提供的规范

2. 缓冲机制

I/O操作一般不能重复读取内容(比如键盘发送的信号,主机接收了就没了),而缓冲流提供了缓冲机制,一部分内容可以被暂时保存
BufferedInputStream 支持 reset()mark() 操作
即通过 mark 标记位置, reset 可以返回之前标记过的位置。

当调用 mark(readlimit) 之后,输入流会以某种方式保留之后读取的readlimit 数量的内容,当读取的内容数量超过 readlimit 则之后的内容不会被保留,当调用 reset() 之后,会使得当前的读取位置回到 mark() 调用时的位置。

public static void main(String[] args) {
    try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"))){
        bufferedInputStream.mark(1);   //只保留之后的1个字符
        System.out.println((char) bufferedInputStream.read());
        System.out.println((char) bufferedInputStream.read());
        bufferedInputStream.reset();   //回到mark时的位置
        System.out.println((char) bufferedInputStream.read());
        System.out.println((char) bufferedInputStream.read());
    }catch (IOException e) {
        e.printStackTrace();
    }
}

我们发现虽然后面的部分没有保存,但是依然能够正常读取,其实mark()后保存的读取内容是取readlimitBufferedInputStream类的缓冲区大小两者中的最大值,而并非完全由readlimit确定。

public static void main(String[] args) {
    try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"), 1)){  
        //将缓冲区大小设置为1
        bufferedInputStream.mark(1);   
        //只保留之后的1个字符
        System.out.println((char) bufferedInputStream.read());
        System.out.println((char) bufferedInputStream.read());   
        //已经超过了readlimit,继续读取会导致mark失效
        bufferedInputStream.reset();   
        //mark已经失效,无法reset()
        System.out.println((char) bufferedInputStream.read());
        System.out.println((char) bufferedInputStream.read());
    }catch (IOException e) {
        e.printStackTrace();
    }
}
3. 缓冲流可以叠加

即可以进行套娃:
BufferedInputStream stream = new BufferedInputStream(new BufferedInputStream(new FileInputStream("test.txt")))

缓冲字节输出流 BufferedOutputStream

其实和BufferedInputStream原理差不多,只是反向操作

try (BufferedOutputStream stream = new BufferedOutputStream(Files.newOutputStream(Paths.get("src/1.txt")))){
            stream.write("Hello Penguin!".getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

缓冲字符流

BufferedReader | BufferedWriter

缓冲字符读取流 BufferedReader

public static void main(String[] args) {
    try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))){
        System.out.println((char) reader.read());
    }catch (IOException e) {
        e.printStackTrace();
    }
}

相比Reader更方便的是,它支持按行读取

public static void main(String[] args) {
    try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))){
        System.out.println(reader.readLine());   //按行读取
    }catch (IOException e) {
        e.printStackTrace();
    }
}

读取后直接得到一个字符串,当然,它还能把每一行内容依次转换为集合类提到的Stream流

public static void main(String[] args) {
    try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))){
        reader
                .lines()
                .limit(2)
                .distinct()
                .sorted()
                .forEach(System.out::println);
    }catch (IOException e) {
        e.printStackTrace();
    }
}

它同样也支持mark()reset()操作

缓冲字符输出流 BufferedWriter

public static void main(String[] args) {
    try (BufferedWriter reader = new BufferedWriter(new FileWriter("output.txt"))){
        reader.newLine();   //使用newLine进行换行
        reader.write("汉堡做滴彳亍不彳亍");   //可以直接写入一个字符串
        reader.flush();   //清空缓冲区
    }catch (IOException e) {
        e.printStackTrace();
    }
}
上次编辑于: