ZetCode

Java FileDescriptor Class

Last modified: April 16, 2025

The java.io.FileDescriptor class represents an open file, socket, or other I/O resource. It serves as a handle to the underlying machine-specific structure. FileDescriptor instances are typically created by I/O streams.

FileDescriptor provides access to native file descriptors used by the operating system. It contains three standard streams: in, out, and err. These correspond to standard input, output, and error streams respectively.

FileDescriptor Class Overview

FileDescriptor is a simple class with few methods. Its main purpose is to provide access to native file handles. The class is used internally by Java's I/O classes and is rarely used directly by application code.

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;
}

The code above shows the structure of FileDescriptor. The class contains three static descriptors for standard streams. The sync method forces all system buffers to synchronize with the underlying device.

Standard Stream FileDescriptors

Java provides three standard FileDescriptor objects for system I/O. These are accessible through System.in, System.out, and System.err. They represent the standard input, output, and error streams respectively.

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();
        }
    }
}

This example demonstrates accessing standard stream FileDescriptors. The valid method checks if the descriptor is open. We also show how to use FileDescriptor with FileOutputStream to write to standard output. The try-with-resources ensures proper resource cleanup.

Creating and Validating FileDescriptors

FileDescriptor objects are typically obtained from I/O streams rather than created directly. The valid method checks if the descriptor is still open and valid. This is useful for checking stream status.

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();
        }
    }
}

This example shows how to obtain FileDescriptor objects from streams. We create input and output streams and get their descriptors. After closing the streams, we check descriptor validity. Closed streams result in invalid descriptors.

Forcing Data to Disk with sync()

The sync method forces all system buffers to write data to the physical storage device. This ensures data is actually written to disk rather than cached. sync throws SyncFailedException if the operation fails.

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();
        }
    }
}

This example demonstrates using sync to ensure data is written to disk. We write critical data to a file and then call sync. The operation may fail if the device cannot be synchronized. Always handle SyncFailedException when using this method.

Comparing FileDescriptors

FileDescriptor objects can be compared to check if they reference the same underlying system resource. This is done using standard object comparison since FileDescriptor doesn't override equals. Only references to the same object are considered equal.

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();
        }
    }
}

This example shows how FileDescriptor comparison works. Two descriptors for the same file are different objects. The standard stream descriptors are singletons. FileDescriptor doesn't provide content-based comparison, only reference equality.

Using FileDescriptor with RandomAccessFile

RandomAccessFile can also provide access to FileDescriptor objects. This allows for low-level operations on files opened in random access mode. The descriptor can be used similarly to those from other I/O classes.

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();
        }
    }
}

This example demonstrates using FileDescriptor with RandomAccessFile. We open a file in read-write mode and get its descriptor. After writing data, we use sync to ensure it's written to disk. The descriptor remains valid throughout the operation.

Source

Java FileDescriptor Class Documentation

In this article, we've covered the essential methods and features of the Java FileDescriptor class. Understanding these concepts is crucial for working with low-level I/O operations in Java applications.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.