IO stream

Published 2026-07-29 09:12 Updated 2026-10-13 17:14 2277 words 12 min read ... Page views AI-reviewed An AI proof-read this post, but it still deserves a careful read. AI-rewritten Someone else wrote the original; AI helped me rewrite and polish it.

AI-reviewed An AI proof-read this post, but it still deserves a careful read.

The article systematically introduces the content related to file operations and IO streams in Java, covering the path processing of File classes, file and directory creation, recursive operations, and the use of byte streams and character streams. It focuses on the classification of IO streams, the combined use of node streams and processing streams, the functions and considerations of common stream classes (such as FileInputStream, BufferedInputStream, FileWriter, BufferedWriter, DataInputStream, ObjectOutputStream), and introduces the configuration file format (Properties, XML, YAML) and the core points of Apache POI to realize reading and writing Excel files.

IO stream

File class

File Is an abstract representation of a file or directory path. create File Object just creates a path object in memory and does not mean that files or directories on disk must exist.

constructor

File file1 = new File("d:/lesson/java2601/a.txt");
File file2 = new File("d:/lesson/java2601", "a.txt");
File parent = new File("d:/lesson/java2601");
File file3 = new File(parent, "a.txt");

You can use escaped backslashes or forward slashes in Windows path strings.

create files and directories

create a file

File file = new File("d:/lesson/java2601/test/a.txt");

try {
    boolean created = file.createNewFile();
    System.out.println(created ? "创建成功" : "文件已存在");
} catch (IOException e) {
    e.printStackTrace();
}

createNewFile() Parent directories that do not exist are not automatically created.

File extensions are often used to help operating systems and applications identify file types, but the extensions themselves do not change file content.

create a directory

mkdir() A single-level directory can only be created when the parent directory already exists.

File directory = new File("d:/lesson/java2601/test/a");
directory.mkdir();

mkdirs() You can create multiple levels of missing parent directories at the same time.

File directory = new File("d:/lesson/java2601/test/a/b/c");
directory.mkdirs();

create temporary files

File directory = new File("d:/lesson/java2601/test");
try {
    File tempFile = File.createTempFile("temp", ".txt", directory);
    System.out.println(tempFile);
} catch (IOException e) {
    e.printStackTrace();
}

The temporary file name prefix requires at least three characters.

common methods

methodrole
exists()Determine whether a path exists
isFile()Determine whether it is an ordinary document
isDirectory()Determine whether it is a directory
delete()Delete files or empty directories
getName()Get file or directory name
getParentFile()Gets the corresponding to the parent path File object
getPath()Gets the path form used when constructing the object
getAbsolutePath()Gets the absolute path string
length()Gets the length of the ordinary file in bytes
listFiles()Get direct subitems in the directory

listFiles() May be returned when the path is not a directory, an IO error occurs, or no access rights are available null, should be checked before use.

File directory = new File("d:/lesson/java2601");
File[] files = directory.listFiles();

if (files != null) {
    for (File file : files) {
        System.out.println(file);
    }
}

File.equals() Comparing whether abstract path names are equal does not automatically add relative paths, symbolic links or include .. All paths are standardized. When you need to compare actual specification paths, you can use the getCanonicalFile(), and process IOException。

recursive

Recursion is when a method calls itself directly or indirectly. Recursion must have clear termination conditions and ensure that each call approaches the termination conditions gradually.

factorial

public static long factorial(int number) {
    if (number < 0) {
        throw new IllegalArgumentException("阶乘参数不能为负数");
    }
    if (number <= 1) {
        return 1;
    }
    return number * factorial(number - 1);
}

Fibonacci sequence

public static long fibonacci(int number) {
    if (number < 0) {
        throw new IllegalArgumentException("参数不能为负数");
    }
    if (number <= 1) {
        return number;
    }
    return fibonacci(number - 1) + fibonacci(number - 2);
}

This direct recursion will repeatedly compute a large number of subproblems and is only suitable for demonstrating recursion. Use circular loops or cached results when processing large data.

Recursively find files

public static void findPdfFiles(File path, List<File> result) {
    if (path == null || !path.exists()) {
        return;
    }

    if (path.isFile()) {
        if (path.getName().toLowerCase().endsWith(".pdf")) {
            result.add(path);
        }
        return;
    }

    File[] children = path.listFiles();
    if (children == null) {
        return;
    }

    for (File child : children) {
        findPdfFiles(child, result);
    }
}

Recursively delete directory

public static boolean deleteRecursively(File path) {
    if (path == null || !path.exists()) {
        return true;
    }

    if (path.isDirectory()) {
        File[] children = path.listFiles();
        if (children == null) {
            return false;
        }

        for (File child : children) {
            if (!deleteRecursively(child)) {
                return false;
            }
        }
    }

    return path.delete();
}

When recursively processing directories, you should also be aware of access permissions, symbolic links, and possible stack overflows caused by excessively deep directories. Modern Java projects can also use java.nio.file.Files Provided file tree API.

IO Flow Overview

IO represents input and output. Using program memory as a reference:

  • The input stream reads external data into the program.
  • The output stream writes program data to external devices.

Classification by data unit

  • InputStream and OutputStream The system is processed by bytes and is suitable for pictures, audio, video and other binary data.
  • Reader and Writer The system processes characters and is suitable for text data.

Character stream requires decoding bytes into characters according to character encoding, or encoding characters into bytes.

Classified by connection target

  • Node streams directly connect data sources or targets such as files, memory, and networks, such as FileInputStream。
  • Processing streams wrap other streams and add functions such as buffering, reading and writing data types, and object serialization, such as BufferedInputStream and ObjectOutputStream。

Processing flows are not necessarily “more advanced” or “faster” than node flows and should be used in combination based on the required functionality.

using step

  1. Determine whether input or output.

  2. Determine whether to process bytes or characters.

  3. Select the node flow corresponding to the actual data source.

  4. Increase buffering, transcoding, or object processing streams as needed.

  5. use try-with-resources Close resources in a timely manner.

FileInputStream and FileOutputStream

read bytes

FileInputStream common read() The method is as follows:

  • read(): Read one byte, return 0 to 255, return to the end -1。
  • read(byte[] buffer): The maximum length of the array can be read in bytes, and the actual number of reads is returned.
  • read(byte[] buffer, int offset, int length): Read the data to the specified range of the array.

When reading in batches, only the actual length read this time can be processed.

try (FileInputStream input = new FileInputStream(
        "d:/lesson/java2601/test/a.txt")) {

    byte[] buffer = new byte[1024];
    int length;

    while ((length = input.read(buffer)) != -1) {
        for (int i = 0; i < length; i++) {
            System.out.println(buffer[i]);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

When outputting text content, you should not force any bytes to characters one by one. You should use a character stream and specify correct encoding.

write byte

FileOutputStream Overwrites the original file by default, and the second parameter of the constructor is true When it means adding.

try (FileOutputStream output = new FileOutputStream(
        "d:/lesson/java2601/test/a.txt", true)) {

    byte[] data = "ABCDEFG".getBytes(StandardCharsets.UTF_8);
    output.write(data);
} catch (IOException e) {
    e.printStackTrace();
}

copy files

public static void copyFile(String source, String target) {
    try (FileInputStream input = new FileInputStream(source);
         FileOutputStream output = new FileOutputStream(target)) {

        byte[] buffer = new byte[8192];
        int length;

        while ((length = input.read(buffer)) != -1) {
            output.write(buffer, 0, length);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Copying binary files must use a byte stream.

BufferedInputStream and BufferedOutputStream

Buffered byte streams maintain buffers in memory and reduce the number of underlying read and write calls.

public static void copyFileWithBuffer(String source, String target) {
    try (BufferedInputStream input = new BufferedInputStream(
            new FileInputStream(source));
         BufferedOutputStream output = new BufferedOutputStream(
            new FileOutputStream(target))) {

        byte[] buffer = new byte[8192];
        int length;

        while ((length = input.read(buffer)) != -1) {
            output.write(buffer, 0, length);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

The buffer is flushed first when the output stream is turned off. When you need to submit data to the underlying target immediately while the stream remains open, you can call flush()。

FileReader and FileWriter

FileReader and FileWriter Used to read and write text, but traditional constructors usually use the platform’s default character set. When the file code is fixed, it is recommended to use InputStreamReader and OutputStreamWriter Explicitly specify the character set.

try (FileReader reader = new FileReader(
        "d:/lesson/java2601/test/a.txt")) {

    char[] buffer = new char[1024];
    int length;

    while ((length = reader.read(buffer)) != -1) {
        System.out.print(new String(buffer, 0, length));
    }
} catch (IOException e) {
    e.printStackTrace();
}

FileReader itself is not BufferedReader, cannot be described as naturally having line buffering functions.

try (FileWriter writer = new FileWriter(
        "d:/lesson/java2601/test/a.txt")) {
    writer.write("1234567890");
} catch (IOException e) {
    e.printStackTrace();
}

BufferedReader and BufferedWriter

BufferedReader provide readLine(), you can read text per line. The returned string does not contain a line terminator and is returned when the end of the file is reached null。

try (BufferedReader reader = new BufferedReader(
        new FileReader("d:/lesson/java2601/test/a.txt"))) {

    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

BufferedWriter provide newLine() Write the line separator corresponding to the platform.

try (BufferedWriter writer = new BufferedWriter(
        new FileWriter("d:/lesson/java2601/test/a.txt"))) {

    writer.write("第一行");
    writer.newLine();
    writer.write("第二行");
} catch (IOException e) {
    e.printStackTrace();
}

InputStreamReader and OutputStreamWriter

These two conversion streams connect the byte stream and the character stream and are responsible for character encoding conversions.

try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(
                new FileInputStream("d:/lesson/java2601/test/a.txt"),
                StandardCharsets.UTF_8))) {

    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}
try (BufferedWriter writer = new BufferedWriter(
        new OutputStreamWriter(
                new FileOutputStream("d:/lesson/java2601/test/a.txt"),
                StandardCharsets.UTF_8))) {

    writer.write("使用 UTF-8 写入文本");
} catch (IOException e) {
    e.printStackTrace();
}

DataInputStream and DataOutputStream

The data stream can read and write data in binary format of Java’s basic type.

try (DataOutputStream output = new DataOutputStream(
        new FileOutputStream("d:/lesson/java2601/test/data.bin"))) {

    output.writeInt(1024);
    output.writeDouble(100.0);
    output.writeUTF("Java");
} catch (IOException e) {
    e.printStackTrace();
}

You must use the same order and type when reading, otherwise you will read wrong results or throw exceptions.

try (DataInputStream input = new DataInputStream(
        new FileInputStream("d:/lesson/java2601/test/data.bin"))) {

    int number = input.readInt();
    double price = input.readDouble();
    String text = input.readUTF();

    System.out.println(number);
    System.out.println(price);
    System.out.println(text);
} catch (IOException e) {
    e.printStackTrace();
}

Object serialization and deserialization

Serialization converts an object and its serializable object graph into a byte sequence, and deserialization restores the object based on the byte sequence.

Class needs to be implemented Serializable Label the interface.

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private transient String password;
}

transient Fields do not participate in default serialization.serialVersionUID Used to determine whether serialized data is compatible with the current class version.

Serialize to byte array

public static byte[] serialize(Object object) throws IOException {
    try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
         ObjectOutputStream output = new ObjectOutputStream(bytes)) {

        output.writeObject(object);
        return bytes.toByteArray();
    }
}

deserialize from byte array

public static Object deserialize(byte[] data)
        throws IOException, ClassNotFoundException {

    try (ByteArrayInputStream bytes = new ByteArrayInputStream(data);
         ObjectInputStream input = new ObjectInputStream(bytes)) {

        return input.readObject();
    }
}

Serialization data does not require the JDK minor versions to be completely consistent, but class names, field structures,serialVersionUID and related classes must meet compatibility conditions.

deserializing untrusted data may trigger dangerous object behavior. Bytestreams from untrusted sources must not be directly deserialized in real projects.

PrintWriter

PrintWriter provide print()、println() and printf() and other convenient text output methods.

try (PrintWriter writer = new PrintWriter(
        "d:/lesson/java2601/test/a.txt",
        StandardCharsets.UTF_8.name())) {

    writer.println("你好");
    writer.println("中午吃什么");
} catch (FileNotFoundException | UnsupportedEncodingException e) {
    e.printStackTrace();
}

PrintWriter Some write errors will not be directly reported IOException Throw, which can be called when needed checkError() Check status. Closing the stream automatically flushes the buffer.

Properties Class

Properties Configuration used to save string key values, inherited from Hashtable<Object, Object>。Should be used first when reading configuration getProperty(), used when writing setProperty()。

Configuration files are suitable for holding data that can be adjusted by the deployment environment but should not be hard-coded into the program.

Common configuration formats

  • XML has a clear structure, but it is relatively complex to write and parse.
  • Properties use simple key value forms and have limited hierarchical expression capabilities.
  • YAML has strong hierarchical expression capabilities and is often used in frameworks such as Spring Boot.

read configuration

p1.name=Tom
p1.age=20
p2.name=Jerry
p2.age=22
Properties properties = new Properties();

try (Reader reader = new InputStreamReader(
        new FileInputStream("src/myinfo.properties"),
        StandardCharsets.UTF_8)) {

    properties.load(reader);

    Person person1 = new Person();
    person1.name = properties.getProperty("p1.name");
    person1.age = Integer.parseInt(properties.getProperty("p1.age"));

    Person person2 = new Person();
    person2.name = properties.getProperty("p2.name");
    person2.age = Integer.parseInt(properties.getProperty("p2.age"));
} catch (IOException e) {
    e.printStackTrace();
}

use Reader You can explicitly select a character set. Direct use in Java 8 InputStream When reading Properties, traditional formats press ISO-8859-1 Rule processing, non-ASCII characters usually require Unicode escape.

writing configuration

properties.setProperty("p3.name", "Tom");
properties.setProperty("p3.age", "22");

try (Writer writer = new OutputStreamWriter(
        new FileOutputStream("src/myinfo.properties"),
        StandardCharsets.UTF_8)) {

    properties.store(writer, "第三个人的信息");
} catch (IOException e) {
    e.printStackTrace();
}
image-001
image-001

Apache POI

Apache POI is a Java project of the Apache Software Foundation that can read and write Microsoft Office formats such as Excel and Word.

A typical exercise is to read data from a student’s achievement workbook, calculate the average, and write the results to another worksheet. When processing Excel, you need to select the corresponding model according to the file format:

  • .xls HSSF is usually used.
  • .xlsx XSSF is usually used.

When using POI to read and write workbooks, input streams, and output streams, you should also use the try-with-resources Turn off resources correctly and pay attention to the handling of cell types, empty cells, and formula cells.

Written by AI-rewritten

If you enjoyed this, leave a comment~

Loading...