ZetCode

Java FileDescriptor 类

最后修改时间:2025 年 4 月 16 日

java.io.FileDescriptor 类表示一个打开的文件、套接字或其他 I/O 资源。它充当底层特定于机器的结构的句柄。FileDescriptor 实例通常由 I/O 流创建。

FileDescriptor 提供了对操作系统使用的本机文件描述符的访问。它包含三个标准流:in、out 和 err。这些分别对应于标准输入、输出和错误流。

FileDescriptor 类概述

FileDescriptor 是一个简单的类,只有几个方法。它的主要目的是提供对本机文件句柄的访问。该类在 Java 的 I/O 类中内部使用,很少由应用程序代码直接使用。

public final class FileDescriptor {
    public static final FileDescriptor in;
    public static final FileDescriptor out;
    public static final FileDescriptor err;
    public FileDescriptor();
    public boolean valid();
    public native void sync() throws SyncFailedException;
}

上面的代码显示了 FileDescriptor 的结构。该类包含三个用于标准流的静态描述符。sync 方法强制所有系统缓冲区与底层设备同步。

标准流 FileDescriptor

Java 为系统 I/O 提供了三个标准的 FileDescriptor 对象。 这些可以通过 System.in、System.out 和 System.err 访问。 它们分别代表标准输入、输出和错误流。

Main.java
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Main {

    public static void main(String[] args) {
        // Access standard stream FileDescriptors
        FileDescriptor inFd = FileDescriptor.in;
        FileDescriptor outFd = FileDescriptor.out;
        FileDescriptor errFd = FileDescriptor.err;
        
        System.out.println("Standard input valid: " + inFd.valid());
        System.out.println("Standard output valid: " + outFd.valid());
        System.out.println("Standard error valid: " + errFd.valid());
        
        // Demonstrate using FileDescriptor with FileOutputStream
        try (FileOutputStream fos = new FileOutputStream(outFd)) {
            PrintStream ps = new PrintStream(fos);
            ps.println("This goes to standard output via FileDescriptor");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例演示了如何访问标准流 FileDescriptor。 valid 方法检查描述符是否打开。 我们还展示了如何将 FileDescriptor 与 FileOutputStream 一起使用以写入标准输出。 try-with-resources 确保正确的资源清理。

创建和验证 FileDescriptor

FileDescriptor 对象通常从 I/O 流获取,而不是直接创建。 valid 方法检查描述符是否仍然打开且有效。 这对于检查流状态很有用。

Main.java
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) {
        try {
            // Create FileInputStream and get its FileDescriptor
            FileInputStream fis = new FileInputStream("test.txt");
            FileDescriptor fd1 = fis.getFD();
            System.out.println("Input FileDescriptor valid: " + fd1.valid());
            
            // Create FileOutputStream and get its FileDescriptor
            FileOutputStream fos = new FileOutputStream("output.txt");
            FileDescriptor fd2 = fos.getFD();
            System.out.println("Output FileDescriptor valid: " + fd2.valid());
            
            // Close streams and check validity
            fis.close();
            fos.close();
            System.out.println("After closing:");
            System.out.println("Input FD valid: " + fd1.valid());
            System.out.println("Output FD valid: " + fd2.valid());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了如何从流中获取 FileDescriptor 对象。 我们创建输入和输出流并获取它们的描述符。 关闭流后,我们检查描述符的有效性。 关闭的流会导致无效的描述符。

使用 sync() 将数据强制写入磁盘

sync 方法强制所有系统缓冲区将数据写入物理存储设备。 这确保数据实际上已写入磁盘而不是缓存。 如果操作失败,sync 会抛出 SyncFailedException。

Main.java
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SyncFailedException;

public class Main {

    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("important.dat")) {
            // Write critical data
            fos.write("Critical system data".getBytes());
            
            // Get FileDescriptor and sync
            FileDescriptor fd = fos.getFD();
            System.out.println("Before sync - valid: " + fd.valid());
            
            try {
                fd.sync(); // Force data to disk
                System.out.println("Data successfully synced to disk");
            } catch (SyncFailedException e) {
                System.err.println("Failed to sync data: " + e.getMessage());
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了如何使用 sync 来确保将数据写入磁盘。 我们将关键数据写入文件,然后调用 sync。 如果设备无法同步,则操作可能会失败。 使用此方法时,始终处理 SyncFailedException。

比较 FileDescriptor

可以比较 FileDescriptor 对象以检查它们是否引用相同的底层系统资源。 这是使用标准对象比较完成的,因为 FileDescriptor 没有覆盖 equals。 只有对同一对象的引用才被认为是相等的。

Main.java
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) {
        try {
            // Create two input streams for the same file
            FileInputStream fis1 = new FileInputStream("test.txt");
            FileInputStream fis2 = new FileInputStream("test.txt");
            
            FileDescriptor fd1 = fis1.getFD();
            FileDescriptor fd2 = fis2.getFD();
            FileDescriptor fd3 = fd1;
            
            System.out.println("fd1 == fd2: " + (fd1 == fd2));
            System.out.println("fd1 == fd3: " + (fd1 == fd3));
            
            // Standard streams comparison
            FileDescriptor stdOut1 = FileDescriptor.out;
            FileDescriptor stdOut2 = FileDescriptor.out;
            System.out.println("stdOut1 == stdOut2: " + (stdOut1 == stdOut2));
            
            fis1.close();
            fis2.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了 FileDescriptor 比较的工作方式。 同一文件的两个描述符是不同的对象。 标准流描述符是单例。 FileDescriptor 不提供基于内容的比较,仅提供引用相等性。

将 FileDescriptor 与 RandomAccessFile 一起使用

RandomAccessFile 还可以提供对 FileDescriptor 对象的访问。 这允许对以随机访问模式打开的文件进行低级操作。 该描述符可以与来自其他 I/O 类的描述符类似地使用。

Main.java
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Main {

    public static void main(String[] args) {
        try (RandomAccessFile raf = new RandomAccessFile("data.bin", "rw")) {
            // Get FileDescriptor from RandomAccessFile
            FileDescriptor fd = raf.getFD();
            System.out.println("RandomAccessFile descriptor valid: " + fd.valid());
            
            // Perform operations
            raf.writeInt(42);
            raf.writeDouble(3.14159);
            
            // Force data to disk
            fd.sync();
            System.out.println("Data written and synced");
            
            // Verify descriptor remains valid
            System.out.println("Descriptor still valid: " + fd.valid());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了如何将 FileDescriptor 与 RandomAccessFile 一起使用。 我们以读写模式打开一个文件并获取其描述符。 写入数据后,我们使用 sync 来确保将其写入磁盘。 描述符在整个操作过程中保持有效。

来源

Java FileDescriptor 类文档

在本文中,我们介绍了 Java FileDescriptor 类的基本方法和特性。 理解这些概念对于在 Java 应用程序中使用低级 I/O 操作至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员。 我于 2007 年开始撰写编程文章,至今已创作超过 1,400 篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程