[Java学习笔记] IO

总结自廖雪峰大佬的教程

Java教程 - 廖雪峰的官方网站 (liaoxuefeng.com)icon-default.png?t=LA46https://www.liaoxuefeng.com/wiki/1252599548343744

目录

File

练习:打印所有文件和子文件夹的内容

InputStream/OutputStream

Filter模式(少量的类实现功能组合)



File

Java标准库的java.io.File对象表示一个文件或者目录:

创建File对象本身不涉及IO操作

  • 可以获取路径/绝对路径/规范路径:getPath()/getAbsolutePath()/getCanonicalPath()
  • 可以获取目录的文件和子目录:list()/listFiles()
  • 可以创建或删除文件和目录。

File对象既可以表示文件,也可以表示目录。特别要注意的是,构造一个File对象,即使传入的文件或目录不存在,代码也不会出错,因为构造一个File对象,并不会导致任何磁盘操作。只有当我们调用File对象的某些方法的时候,才真正进行磁盘操作。

例如,调用isFile(),判断该File对象是否是一个已存在的文件,调用isDirectory(),判断该File对象是否是一个已存在的目录:

用File对象获取到一个文件时,还可以进一步判断文件的权限和大小:

  • boolean canRead():是否可读;
  • boolean canWrite():是否可写;
  • boolean canExecute():是否可执行;
  • long length():文件字节大小。

对目录而言,是否可执行表示能否列出它包含的文件和子目录。

有些时候,程序需要读写一些临时文件,File对象提供了createTempFile()来创建一个临时文件,以及deleteOnExit()在JVM退出时自动删除该文件。

public class Main {

    public static void main(String[] args) throws IOException {

        File f = File.createTempFile("tmp-", ".txt"); // 提供临时文件的前缀和后缀

        f.deleteOnExit(); // JVM退出时自动删除

        System.out.println(f.isFile());

        System.out.println(f.getAbsolutePath());

    }

}

true

/tmp/tmp-17585469961555042196.txt

练习:打印所有文件和子文件夹的内容


import java.io.File;
import java.io.IOException;


public class demo {

    public static void main(String[] args) throws IOException {
        File currentDir = new File(".");
        printAllFile(currentDir.getCanonicalFile());
    }

    static void printAllFile(File dir) {
        // 打印所有文件和子文件夹的内容
        File[] files = dir.listFiles();
        if(files != null){
            for(File f : files){
                System.out.println(f.getName());
            }
        }
    }
}

 

InputStream/OutputStream

Java标准库的java.io.InputStream/java.io.OutputStream定义了所有输入/输出流的超类:

  • FileInputStream实现了文件流输入,FileOutputStream实现了文件流输出;

  • ByteArrayInputStream在内存中模拟一个字节流输入, ByteArrayOutputStream在内存中模拟一个字节流输出。

  • 某些情况下需要手动调用OutputStreamflush()方法来强制输出缓冲区。

  • 总是使用try(resource)来保证正确关闭。

InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read(),签名如下:

public abstract int read() throws IOException;

需要用try ... finally来保证InputStream在无论是否发生IO错误的时候都能够正确地关闭:

public void readFile() throws IOException {
    InputStream input = null;
    try {
         // 定义1000个字节大小的缓冲区:
        byte[] buffer = new byte[1000];
        int n;
        while ((n = input.read(buffer)) != -1) { // 读取到缓冲区
            System.out.println("read " + n + " bytes.");
        }
    } finally {
        if (input != null) { input.close(); }
    }
}

try ... finally来编写上述代码会感觉比较复杂,更好的写法是利用Java 7引入的新的try(resource)的语法,只需要编写try语句,让编译器自动为我们关闭资源。推荐的写法如下:

public void readFile() throws IOException {
    try (InputStream input = new FileInputStream("src/readme.txt")) {
         // 定义1000个字节大小的缓冲区:
        byte[] buffer = new byte[1000];
        int n;
        while ((n = input.read(buffer)) != -1) { // 读取到缓冲区
            System.out.println("read " + n + " bytes.");
        }
    } // 编译器在此自动为我们写入finally并调用close()
}

实际上,编译器并不会特别地为InputStream加上自动关闭。编译器只看try(resource = ...)中的对象是否实现了java.lang.AutoCloseable接口,如果实现了,就自动加上finally语句并调用close()方法。InputStreamOutputStream都实现了这个接口,因此,都可以用在try(resource)

在调用InputStreamread()方法读取数据时,read()方法是阻塞的

int n;
n = input.read(); // 必须等待read()方法返回才能执行下一行代码
int m = n;

Filter模式(少量的类实现功能组合)

当我们需要给一个“基础”InputStream附加各种功能时,我们先确定这个能提供数据源的InputStream,因为我们需要的数据总得来自某个地方,例如,FileInputStream,数据来源自文件:

InputStream file = new FileInputStream("test.gz");

紧接着,我们希望FileInputStream能提供缓冲的功能来提高读取的效率,因此我们用BufferedInputStream包装这个InputStream,得到的包装类型是BufferedInputStream,但它仍然被视为一个InputStream

InputStream buffered = new BufferedInputStream(file);

最后,假设该文件已经用gzip压缩了,我们希望直接读取解压缩的内容,就可以再包装一个GZIPInputStream

InputStream gzip = new GZIPInputStream(buffered);

无论我们包装多少次,得到的对象始终是InputStream,我们直接用InputStream来引用它,就可以正常读取:

┌─────────────────────────┐
│GZIPInputStream          │
│┌───────────────────────┐│
││BufferedFileInputStream││
││┌─────────────────────┐││
│││   FileInputStream   │││
││└─────────────────────┘││
│└───────────────────────┘│
└─────────────────────────┘

上述这种通过一个“基础”组件再叠加各种“附加”功能组件的模式,称之为Filter模式(或者装饰器模式:Decorator)。它可以让我们通过少量的类来实现各种功能的组合:

                 ┌─────────────┐
                 │ InputStream │
                 └─────────────┘
                       ▲ ▲
┌────────────────────┐ │ │ ┌─────────────────┐
│  FileInputStream   │─┤ └─│FilterInputStream│
└────────────────────┘ │   └─────────────────┘
┌────────────────────┐ │     ▲ ┌───────────────────┐
│ByteArrayInputStream│─┤     ├─│BufferedInputStream│
└────────────────────┘ │     │ └───────────────────┘
┌────────────────────┐ │     │ ┌───────────────────┐
│ ServletInputStream │─┘     ├─│  DataInputStream  │
└────────────────────┘       │ └───────────────────┘
                             │ ┌───────────────────┐
                             └─│CheckedInputStream │
                               └───────────────────┘

 

posted @ 2021-11-08 18:36  泥烟  阅读(22)  评论(0编辑  收藏  举报