javaIO (1): OutputStream and FileOutputStream source code analysis

Keywords: Java

Preface

From now on, we'll go into detail about the classes in the java.io package that relate to input and output. As you know from this package, the class inheritance relationships are very complicated. It's enough to understand the class inheritance relationships. To tell you the truth, I don't have any good way to figure out these classes in a minute, just a brief description of the general structure. The categories are roughly divided into five categories:

1. File class. There is a separate File class in the package, which is an abstract representation of file and directory pathnames. This class contains many methods related to files or paths, such as creating and deleting files or paths, obtaining attributes of files or paths, and judging whether files or paths have some properties. Although there are many input and output devices, the most frequently operated is the hard disk, and the data on the hard disk is represented in the form of files, that is File.

2. OutputStream and its subclasses: OutputStream. The system writes data in byte format to the output device (hard disk or screen, etc.).

3. InputStream and its subclasses: input byte stream. This system reads byte data sources of different input devices (keyboard, hard disk, etc.).

4. Writer and its subclasses: Output character stream. The system writes data to the output device (hard disk or screen, etc.) in character format.

5. Reader and its subclasses: input character stream. The system writes data to the output device (hard disk or screen, etc.) in character format.

text

The use of File classes is very simple and will not be repeated. This article describes the use of OutputStream byte output stream and some source code.

First, Output Stream source code

package java.io;

// OutputStream is a superclass of all byte output streams and implements two interfaces, one for close() and the other for flush().
public abstract class OutputStream implements Closeable, Flushable {

    /* 
    Write the specified byte to the output stream of this file, and subclasses need to implement this method. The conventional protocol for write is to write a byte to the output stream. The bytes to be written are eight low bits of parameter b. The 24 highs of b will be ignored.
    */
    public abstract void write(int b) throws IOException;

    // Writes b.length bytes from the specified byte array to the file output stream. 
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    // Writes len bytes from offset off in the specified byte array to the file output stream.
    public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);  // To call the wirte(int b) method one by one
        }
    }

    /* Refresh this output stream and force all buffered output bytes to be written. The convention of flush is that if the implementation of this output stream has buffered any bytes previously written, calling this method indicates that these bytes should be written to their intended target immediately.*/
    public void flush() throws IOException {
    }

    /*
     Close this output stream and release all system resources associated with it. The conventional protocol of close is that the method closes the output stream. Closed streams cannot perform output operations, nor can they be reopened.
     */
    public void close() throws IOException {
    }

}

Second, FileOutputStream source code

package java.io;

import java.nio.channels.FileChannel;
import sun.nio.ch.FileChannelImpl;


/**
 * The file output stream is used to write byte data to the file.
 *
 * The stream can be used to write image data. To write characters, consider FileWriter
 *
 * Is a direct subclass of OutputStream
 */
public class FileOutputStream extends OutputStream
{
    /**
     * Open File Handle
     */
    private final FileDescriptor fd;

    /**
     * Whether to append at the end of the file
     */
    private final boolean append;

    /**
     * Reference object
     */
    private FileChannel channel;

    /**
     * File path
     */
    private final String path;

    private final Object closeLock = new Object();
    private volatile boolean closed = false;

    // Constructor 1, passes in a string representing the file path, initializes the string as a File object, and passes it to constructor 4
    public FileOutputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null, false);
    }

    // Constructor 2, passes in a string representing the file path and an additional identifier, initializes the string as a File object, and passes it to constructor 4.
    public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, append);
    }

    // Construct method 3, pass in File object, call construct method 4
    public FileOutputStream(File file) throws FileNotFoundException {
        this(file, false);
    }

    // Constructor 4, which is called by all the first three constructors
    public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        this.fd = new FileDescriptor();
        fd.attach(this);
        this.append = append;
        this.path = name;

        open(name, append); // Open calls the native method to open the specified file
    }

    // Create an output file stream that writes data to the specified file descriptor
    public FileOutputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkWrite(fdObj);
        }
        this.fd = fdObj;
        this.append = false;
        this.path = null;

        fd.attach(this);
    }

    // write Method for Implementing Parent Class
    public void write(int b) throws IOException {
        write(b, append);  // Call the native method and write one byte at a time.
    }

    // Override the parent class write(byte []) method
    public void write(byte b[]) throws IOException {
        writeBytes(b, 0, b.length, append); // Call the native method to write an array of sections
    }

    // Override the parent class write method
    public void write(byte b[], int off, int len) throws IOException {
        writeBytes(b, off, len, append); // Call the native method to write part of the section array
    }

    // Override the close() method of the parent class, and also call the native method to close the resource
    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }

        if (channel != null) {
            channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

    // Flush inherits from the parent class. The parent's flush method does nothing, and the natural FileOutputStream's flush method does nothing.

Posted by nemethpeter on Wed, 26 Jun 2019 14:14:24 -0700