[Java learning notes] IO

Summarize the tutorial from Liao Xuefeng

Java Tutorial - Liao Xuefeng's official website (liaoxuefeng.com)https://www.liaoxuefeng.com/wiki/1252599548343744

catalogue

File

Exercise: printing the contents of all files and subfolders

InputStream/OutputStream

Filter pattern (a small number of class implementation function combinations)

File

The java.io.File object of the Java standard library represents a file or directory:

Creating a File object itself does not involve IO operations;

  • You can obtain path / absolute path / canonical path: getPath()/getAbsolutePath()/getCanonicalPath();
  • You can get the files and subdirectories of the directory: list()/listFiles();
  • You can create or delete files and directories.

The File object can represent either a File or a directory. In particular, when constructing a File object, even if the incoming File or directory does not exist, the code will not make an error, because constructing a File object will not lead to any disk operation. Only when we call some methods of the File object can we really perform disk operation.

For example, call isFile() to determine whether the File object is an existing File, and call isDirectory() to determine whether the File object is an existing directory:

When obtaining a File with the File object, you can further judge the permission and size of the File:

  • boolean canRead(): whether it is readable;
  • boolean canWrite(): whether it is writable;
  • boolean canExecute(): executable;
  • long length(): File byte size.

For a directory, executable indicates whether the files and subdirectories it contains can be listed.

Sometimes, the program needs to read and write some temporary files. The File object provides createTempFile() to create a temporary File, and deleteOnExit() automatically deletes the File when the JVM exits.

public class Main {

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

        File f = File.createTempFile("tmp-", ".txt"); // Provide prefix and suffix for temporary files

        f.deleteOnExit(); // Automatically delete when JVM exits

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

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

    }

}

true

/tmp/tmp-17585469961555042196.txt

Exercise: printing the contents of all files and subfolders

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) {
        // Print the contents of all files and subfolders
        File[] files = dir.listFiles();
        if(files != null){
            for(File f : files){
                System.out.println(f.getName());
            }
        }
    }
}

 

InputStream/OutputStream

The java.io.InputStream/java.io.OutputStream of the Java standard library defines the superclasses of all input / output streams:

  • FileInputStream implements file stream input, and FileOutputStream implements file stream output;

  • ByteArrayInputStream simulates a byte stream input in memory,   ByteArrayOutputStream simulates a byte stream output in memory.

  • In some cases, you need to manually call the flush() method of OutputStream to force the output buffer.

  • Always use try(resource) to ensure proper shutdown.

InputStream is not an interface, but an abstract class, which is the superclass of all input streams. One of the most important methods defined by this abstract class is int read(). The signature is as follows:

public abstract int read() throws IOException;

Try... Finally is required to ensure that InputStream can be shut down correctly no matter whether IO error occurs:

public void readFile() throws IOException {
    InputStream input = null;
    try {
         // Define a 1000 byte buffer:
        byte[] buffer = new byte[1000];
        int n;
        while ((n = input.read(buffer)) != -1) { // Read buffer
            System.out.println("read " + n + " bytes.");
        }
    } finally {
        if (input != null) { input.close(); }
    }
}

Writing the above code with try... Finally will feel more complex. A better way is to use the new try(resource) syntax introduced by Java 7. You only need to write a try statement to let the compiler automatically close the resources for us. The recommended wording is as follows:

public void readFile() throws IOException {
    try (InputStream input = new FileInputStream("src/readme.txt")) {
         // Define a 1000 byte buffer:
        byte[] buffer = new byte[1000];
        int n;
        while ((n = input.read(buffer)) != -1) { // Read buffer
            System.out.println("read " + n + " bytes.");
        }
    } // Here the compiler automatically writes finally for us and calls close()
}

In fact, the compiler does not specifically add auto shutdown for InputStream. The compiler only depends on whether the object in try(resource =...) implements the java.lang.autoclosable interface. If so, it will automatically add a finally statement and call the close() method. Both InputStream and OutputStream implement this interface, so they can be used in try(resource)

When the read() method of InputStream is called to read data, the read() method is blocked

int n;
n = input.read(); // You must wait for the read() method to return before executing the next line of code
int m = n;

Filter pattern (a small number of class implementation function combinations)

When we need to add various functions to a "basic" InputStream, we first determine the InputStream that can provide data source, because the data we need always comes from somewhere, for example, FileInputStream, and the data comes from files:

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

Next, we hope that FileInputStream can provide buffering function to improve reading efficiency. Therefore, we wrap this InputStream with BufferedInputStream. The obtained packaging type is BufferedInputStream, but it is still regarded as an InputStream:

InputStream buffered = new BufferedInputStream(file);

Finally, assuming that the file has been compressed with gzip, we want to directly read the extracted content, and then we can package a GZIPInputStream:

InputStream gzip = new GZIPInputStream(buffered);

No matter how many times we wrap, the object we get is always InputStream. We can reference it directly with InputStream and read it normally:

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

The above mode of superimposing various "additional" functional components through a "basic" component is called Filter mode (or Decorator mode). It allows us to realize the combination of various functions through a small number of classes:

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

 

Tags: Java Back-end

Posted on Mon, 08 Nov 2021 07:35:24 -0500 by jazappi