【Java原理系列】 FileDescriptor原理用法源码分析

Java FileDescriptor原理用法源码分析

原理

FileDescriptor 是 Java 中用于表示文件或文件描述符的抽象类。它提供了对底层操作系统文件句柄的访问和控制。

原理实现:

  • FileDescriptor 类是一个封装了底层文件句柄的对象,用于在 Java 程序中进行文件和 I/O 操作。
  • 它通过整数值来表示底层操作系统的文件描述符(file descriptor)。
  • 在 Java 中,每个打开的文件都会被赋予一个唯一的文件描述符,它作为文件在操作系统中的标识符。

用法示例

FileDescriptor 主要用于以下情况:

  • 调用本地方法或使用低级别 I/O API 进行文件和 I/O 操作时,需要将底层文件描述符传递给相应的方法。
  • 可以使用 FileDescriptor 来创建自定义的输入流或输出流,并在这些流中直接操作底层文件描述符。

以下是一个使用FileDescriptor的简单示例,演示如何使用它来创建自定义的输入流和输出流,并在这些流中直接操作底层文件描述符:

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        // 自定义的输出流,基于底层文件描述符进行操作
        class MyCustomOutputStream extends FileOutputStream {
            public MyCustomOutputStream(FileDescriptor fd) {
                super(fd);
            }

            // 可以在这里添加自定义的写入方法或重写父类的写入方法
            // ...
        }

        // 自定义的输入流,基于底层文件描述符进行操作
        class MyCustomInputStream extends FileInputStream {
            public MyCustomInputStream(FileDescriptor fd) {
                super(fd);
            }

            // 可以在这里添加自定义的读取方法或重写父类的读取方法
            // ...
        }
        
        
        try {
            // 创建文件输出流并获取其底层文件描述符
            FileOutputStream fos = new FileOutputStream("example.txt");
            FileDescriptor fdOut = fos.getFD();

            // 使用底层文件描述符创建自定义的输出流
            MyCustomOutputStream customOutputStream = new MyCustomOutputStream(fdOut);

            // 写入数据到文件
            String data = "Hello, World!";
            byte[] bytes = data.getBytes();
            customOutputStream.write(bytes);

            customOutputStream.close();

            // 创建文件输入流并获取其底层文件描述符
            FileInputStream fis = new FileInputStream("example.txt");
            FileDescriptor fdIn = fis.getFD();

            // 使用底层文件描述符创建自定义的输入流
            MyCustomInputStream customInputStream = new MyCustomInputStream(fdIn);

            // 读取文件内容
            int dataByte;
            while ((dataByte = customInputStream.read()) != -1) {
                System.out.print((char) dataByte);
            }

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

在这个示例中,我们首先创建了一个FileOutputStream来打开文件并获取其底层文件描述符。然后,我们使用该文件描述符创建了自定义的输出流MyCustomOutputStream,在其中可以添加自定义的写入方法或重写父类的写入方法。我们将数据写入到文件中,并关闭输出流。

接下来,我们创建了一个FileInputStream来打开同一个文件,并获取其底层文件描述符。然后,我们使用该文件描述符创建了自定义的输入流MyCustomInputStream,在其中可以添加自定义的读取方法或重写父类的读取方法。我们读取文件内容,并在控制台上打印出来,最后关闭输入流。

请注意,在实际使用时,您需要根据具体需求和文件操作,添加适当的异常处理、缓冲区管理等。

方法总结

FileDescriptor 是一个文件描述符类,它作为对底层机器特定结构的不透明句柄,表示一个打开的文件、打开的套接字或其他字节源或接收器。主要实际用途是创建 FileInputStreamFileOutputStream

应用程序不应该自己创建文件描述符。

该类具有以下主要成员和方法:

  • int fd:文件描述符的整数值。
  • long handle:文件描述符的长整型值。
  • Closeable parent:关联的 Closeable 对象,用于跟踪引用此文件描述符的其他对象。
  • List<Closeable> otherParents:与此文件描述符共享的其他 Closeable 对象列表。
  • boolean closed:标记文件描述符是否已关闭。

构造函数:

  • FileDescriptor():构造一个(无效的)FileDescriptor 对象。

静态成员:

  • FileDescriptor in:标准输入流的文件描述符。
  • FileDescriptor out:标准输出流的文件描述符。
  • FileDescriptor err:标准错误流的文件描述符。

方法:

  • boolean valid():测试此文件描述符对象是否有效。
  • void sync():强制系统缓冲区与底层设备同步。
  • void attach(Closeable c):将一个 Closeable 对象附加到此文件描述符以进行跟踪。
  • void closeAll(Closeable releaser):循环遍历所有共享此文件描述符的 Closeable 对象,并调用它们的 close() 方法。

FileDescriptor 类的具体用法可以参考 Java API 文档中的说明。

中文源码

/**
 * FileDescriptor 类的实例用作底层机器特定结构(如打开的文件、打开的套接字等)的不透明句柄。
 * 主要实际用途是创建 FileInputStream 或 FileOutputStream 对象来包含它。
 *
 * <p>应用程序不应创建自己的文件描述符。
 *
 * @author Pavani Diwanji
 * @since JDK1.0
 */
public final class FileDescriptor {

    private int fd; // 文件描述符的整数值
    private long handle; // 文件描述符的长整型值

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

    /**
     * 构造一个(无效的)FileDescriptor对象。
     */
    public /**/ FileDescriptor() {
        fd = -1;
        handle = -1;
    }

    static {
        initIDs();
    }

    // 在SharedSecrets中设置JavaIOFileDescriptorAccess
    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;
                }
            }
        );
    }

    /**
     * 标准输入流的文件描述符。
     *
     * @see java.lang.System#in
     */
    public static final FileDescriptor in = standardStream(0);

    /**
     * 标准输出流的文件描述符。
     *
     * @see java.lang.System#out
     */
    public static final FileDescriptor out = standardStream(1);

    /**
     * 标准错误流的文件描述符。
     *
     * @see java.lang.System#err
     */
    public static final FileDescriptor err = standardStream(2);

    /**
     * 测试此文件描述符对象是否有效。
     *
     * @return 如果文件描述符对象表示一个有效的、打开的文件、套接字或其他活动 I/O 连接,则返回 true;否则返回 false。
     */
    public boolean valid() {
        return ((handle != -1) || (fd != -1));
    }

    /**
     * 强制所有系统缓冲区与底层设备同步。
     * 在所有修改的数据和属性都已写入相关设备后,此方法返回。
     * 特别地,如果此FileDescriptor引用物理存储介质(如文件系统中的文件),则sync将一直阻塞,直到所有内存中已修改的缓冲区的副本都已写入物理介质。
     *
     * sync用于需要物理存储(如文件)处于已知状态的代码。
     * 例如,提供简单事务功能的类可以使用sync来确保由给定事务导致的对文件的所有更改都记录在存储介质上。
     *
     * sync仅影响此FileDescriptor之下的缓冲区。如果应用程序正在执行任何内存中的缓冲(例如,通过BufferedOutputStream对象),那些缓冲区必须在受sync影响之前刷新到FileDescriptor(例如,通过调用OutputStream.flush)。
     *
     * @exception SyncFailedException
     *        当无法刷新缓冲区时抛出,或者因为系统无法保证所有缓冲区已与物理介质同步。
     * @since JDK1.1
     */
    public native void sync() throws SyncFailedException;

    /* 此例程初始化类的JNI字段偏移量 */
    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;
    }

    /*
     * 包私有方法以跟踪引用。
     * 如果多个流指向相同的FileDescriptor,我们会循环遍历所有引用,并调用close()。
     */

    /**
     * 附加一个Closeable对象到此FD以进行跟踪。
     * 当需要时,将parent引用添加到otherParents中,以使closeAll更简单。
     */
    synchronized void attach(Closeable c) {
        if (parent == null) {
            // 第一个调用者执行此操作
            parent = c;
        } else if (otherParents == null) {
            otherParents = new ArrayList<>();
            otherParents.add(parent);
            otherParents.add(c);
        } else {
            otherParents.add(c);
        }
    }

    /**
     * 循环遍历所有共享此FD的Closeable对象,并对每个对象调用close()。
     * 调用closeable的对象可以调用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) {
                /*
                 * 如果releaser的close()抛出IOException,
                 * 将其他异常作为被抑制异常添加。
                 */
                if (ioe != null)
                    ex.addSuppressed(ioe);
                ioe = ex;
            } finally {
                if (ioe != null)
                    throw ioe;
            }
        }
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值