Java-IO之FileDescriptor

时间:2023-03-09 01:18:08
Java-IO之FileDescriptor

FileDescriptor是文件描述符,可以被用来表示开放文件,开放套接字等,FileDescriptor可以被看成某个文件,但无法对该文件进行操作,需要新创建FileDescriptor对应的FileOutputStream再对文件进行操作。

FileDescriptor示例代码:

public class FileDescriptorTest {

    private static final String FileName = "file.txt";
    private static final String OutText = "Hi FileDescriptor";
    public static void main(String[] args) {
        testWrite();
        testRead();
        testStandFD() ;
        //System.out.println(OutText);
    }
    /**
     * FileDescriptor.out 的测试程序
     * 该程序的效果 等价于 System.out.println(OutText);
     */
    private static void testStandFD() {
        // 创建FileDescriptor.out 对应的PrintStream
        PrintStream out = new PrintStream(new FileOutputStream(FileDescriptor.out));
        // 在屏幕上输出“Hi FileDescriptor”
        out.println(OutText);
        out.close();
    }
    /**
     * FileDescriptor写入示例程序
     * (01) 为了说明,"通过文件名创建FileOutputStream"与“通过文件描述符创建FileOutputStream”对象是等效的
    * (02) 该程序会在“该源文件”所在目录新建文件"file.txt",并且文件内容是"Aa"。
    */
    private static void testWrite() {
        try {
            // 新建文件“file.txt”对应的FileOutputStream对象
            FileOutputStream out1 = new FileOutputStream(FileName);
            // 获取文件“file.txt”对应的“文件描述符”
            FileDescriptor fdout = out1.getFD();
            // 根据“文件描述符”创建“FileOutputStream”对象
            FileOutputStream out2 = new FileOutputStream(fdout);
            out1.write('A');    // 通过out1向“file.txt”中写入'A'
            out2.write('a');    // 通过out2向“file.txt”中写入'A'

            if (fdout!=null)
                System.out.printf("fdout(%s) is %s\n",fdout, fdout.valid());

                out1.close();
                out2.close();

            } catch(IOException e) {
            e.printStackTrace();
            }
    }

    /**
     * FileDescriptor读取示例程序
     *
     * 为了说明,"通过文件名创建FileInputStream"与“通过文件描述符创建FileInputStream”对象是等效的
    */
    private static void testRead() {
        try {
            // 新建文件“file.txt”对应的FileInputStream对象
            FileInputStream in1 = new FileInputStream(FileName);
            // 获取文件“file.txt”对应的“文件描述符”
            FileDescriptor fdin = in1.getFD();
            // 根据“文件描述符”创建“FileInputStream”对象
            FileInputStream in2 = new FileInputStream(fdin);

            System.out.println("in1.read():"+(char)in1.read());
            System.out.println("in2.read():"+(char)in2.read());

            if (fdin!=null)
                System.out.printf("fdin(%s) is %s\n", fdin, fdin.valid());

                in1.close();
                in2.close();
            } catch(IOException e) {
            e.printStackTrace();
            }
    }
}
运行结果:
fdout(java.io.FileDescriptor@1ea21c07) is true

in1.read():A

in2.read():a

fdin(java.io.FileDescriptor@24de1f47) is true

Hi FileDescriptor

基于JDK8的FileDescriptor的源码:

public final class FileDescriptor {

    private int fd;

    private long handle;

    private Closeable parent;
    private List<Closeable> otherParents;
    private boolean closed;

    /**
     * Constructs an (invalid) FileDescriptor
     * object.
     */
    public /**/ FileDescriptor() {
        fd = -1;
        handle = -1;
    }

    static {
        initIDs();
    }

    // Set up JavaIOFileDescriptorAccess in SharedSecrets
    static {
        sun.misc.SharedSecrets.setJavaIOFileDescriptorAccess(
        new sun.misc.JavaIOFileDescriptorAccess() {
            public void set(FileDescriptor obj, int fd) {
                        obj.fd = fd;
            }

            public int get(FileDescriptor obj) {
                return obj.fd;
            }

            public void setHandle(FileDescriptor obj, long handle) {
                        obj.handle = handle;
            }

            public long getHandle(FileDescriptor obj) {
                return obj.handle;
            }
                }
        );
    }

    /**
     * A handle to the standard input stream. Usually, this file
     * descriptor is not used directly, but rather via the input stream
     * known as {@code System.in}.
     *
     * @see     java.lang.System#in
     */
    //标准输入流
    public static final FileDescriptor in = standardStream(0);

    /**
     * A handle to the standard output stream. Usually, this file
     * descriptor is not used directly, but rather via the output stream
     * known as {@code System.out}.
     * @see     java.lang.System#out
     */
    //标准输出流
    public static final FileDescriptor out = standardStream(1);

    /**
     * A handle to the standard error stream. Usually, this file
     * descriptor is not used directly, but rather via the output stream
     * known as {@code System.err}.
     *
     * @see     java.lang.System#err
     */
    //标准错误
    public static final FileDescriptor err = standardStream(2);

    /**
     * Tests if this file descriptor object is valid.
     *
     * @return  {@code true} if the file descriptor object represents a
     *          valid, open file, socket, or other active I/O connection;
     *          {@code false} otherwise.
     */
    //文件描述对象是否有效
    public boolean valid() {
        return ((handle != -1) || (fd != -1));
    }

    /**
     * Force all system buffers to synchronize with the underlying
     * device.  This method returns after all modified data and
     * attributes of this FileDescriptor have been written to the
     * relevant device(s).  In particular, if this FileDescriptor
     * refers to a physical storage medium, such as a file in a file
     * system, sync will not return until all in-memory modified copies
     * of buffers associated with this FileDesecriptor have been
     * written to the physical medium.
     *
     * sync is meant to be used by code that requires physical
     * storage (such as a file) to be in a known state  For
     * example, a class that provided a simple transaction facility
     * might use sync to ensure that all changes to a file caused
     * by a given transaction were recorded on a storage medium.
     *
     * sync only affects buffers downstream of this FileDescriptor.  If
     * any in-memory buffering is being done by the application (for
     * example, by a BufferedOutputStream object), those buffers must
     * be flushed into the FileDescriptor (for example, by invoking
     * OutputStream.flush) before that data will be affected by sync.
     *
     * @exception SyncFailedException
     *        Thrown when the buffers cannot be flushed,
     *        or because the system cannot guarantee that all the
     *        buffers have been synchronized with physical media.
     * @since     JDK1.1
     */
    public native void sync() throws SyncFailedException;

    /* This routine initializes JNI field offsets for the class */
    private static native void initIDs();

    private static native long set(int d);

    private static FileDescriptor standardStream(int fd) {
        FileDescriptor desc = new FileDescriptor();
        desc.handle = set(fd);
        return desc;
    }

    /*
     * Package private methods to track referents.
     * If multiple streams point to the same FileDescriptor, we cycle
     * through the list of all referents and call close()
     */

    /**
     * Attach a Closeable to this FD for tracking.
     * parent reference is added to otherParents when
     * needed to make closeAll simpler.
     */
    synchronized void attach(Closeable c) {
        if (parent == null) {
            // first caller gets to do this
            parent = c;
        } else if (otherParents == null) {
            otherParents = new ArrayList<>();
            otherParents.add(parent);
            otherParents.add(c);
        } else {
            otherParents.add(c);
        }
    }

    /**
     * Cycle through all Closeables sharing this FD and call
     * close() on each one.
     *
     * The caller closeable gets to call close0().
     */
    @SuppressWarnings("try")
    synchronized void closeAll(Closeable releaser) throws IOException {
        if (!closed) {
            closed = true;
            IOException ioe = null;
            try (Closeable c = releaser) {
                if (otherParents != null) {
                for (Closeable referent : otherParents) {
                    try {
                            referent.close();
                        } catch(IOException x) {
                            if (ioe == null) {
                                ioe = x;
                            } else {
                                ioe.addSuppressed(x);
                            }
                        }
                    }
                }
            } catch(IOException ex) {
                /*
                 * If releaser close() throws IOException
                 * add other exceptions as suppressed.
                 */
                if (ioe != null)
                    ex.addSuppressed(ioe);
                    ioe = ex;
                } finally {
                    if (ioe != null)
                        throw ioe;
                }
        }
    }
}