File handling is a critical part of almost any application. Whether you need to read configuration data, write log files, or process user-uploaded content, interacting with the file system is a fundamental skill for a Java developer.
Java provides a rich set of APIs for file I/O (Input/Output).
java.io.File ClassThe cornerstone of file handling in older Java versions is the java.io.File class. It's important to understand that an object of the File class does not represent the content of a file itself, but rather the path to a file or directory on the file system.
You can use a File object to perform operations like:
import java.io.File;public class Main { public static void main(String[] args) { // Create a File object representing a path File myFile = new File("my-first-file.txt"); if (myFile.exists()) { System.out.println("File name: " + myFile.getName()); System.out.println("Absolute path: " + myFile.getAbsolutePath()); System.out.println("Writeable: " + myFile.canWrite()); System.out.println("Readable: " + myFile.canRead()); System.out.println("File size in bytes: " + myFile.length()); } else { System.out.println("The file does not exist."); } } }
To read or write the content of a file, Java uses streams. A stream is a sequence of data.
InputStream: An abstract class for reading a stream of bytes. FileInputStream is a common implementation for reading from files.OutputStream: An abstract class for writing a stream of bytes. FileOutputStream is a common implementation for writing to files.For handling text files, Java provides Reader and Writer classes, which work with characters instead of bytes.
Reader: Abstract class for reading character streams. FileReader is a common implementation.Writer: Abstract class for writing character streams. FileWriter is a common implementation.java.nio.fileSince Java 7, a new, more powerful file I/O library was introduced: the NIO.2 (New I/O) API, located in the java.nio.file package. It was designed to overcome many of the limitations of the original java.io.File class.
Key classes in the new API include:
Path: Represents a path on the file system (the modern replacement for File).Paths: A factory class for creating Path objects.Files: A utility class with static methods for performing operations on files and directories (e.g., Files.createFile(), Files.readAllLines(), Files.delete()).For new projects, it is generally recommended to use the java.nio.file API as it provides better error handling and more features. In the following chapters, we will show examples using both the traditional java.io and the modern java.nio approaches.
While standard Streams read data byte-by-byte or character-by-character, the Java NIO (New I/O) API uses Channels and Buffers for higher performance.
A FileChannel reads data into a Buffer, and writes data from a Buffer. This approach allows for faster bulk data transfers and advanced features like memory-mapped files and file locking.
import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public class Main { public static void main(String[] args) { try (RandomAccessFile file = new RandomAccessFile("data.txt", "r"); FileChannel channel = file.getChannel()) { // Create a buffer with a capacity of 48 bytes ByteBuffer buffer = ByteBuffer.allocate(48); // Read data from the channel into the buffer int bytesRead = channel.read(buffer); while (bytesRead != -1) { System.out.println("\nRead " + bytesRead + " bytes"); // Flip the buffer to prepare for reading from it buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); // Read 1 byte at a time } // Clear the buffer to prepare for the next read buffer.clear(); bytesRead = channel.read(buffer); } } catch (Exception e) { e.printStackTrace(); } } }
In highly scalable applications, blocking the main thread while waiting for disk I/O can be a bottleneck. The NIO.2 API provides AsynchronousFileChannel, which allows you to read or write files asynchronously.
You can use a Future object to check if the operation is done, or provide a CompletionHandler callback that executes when the I/O operation finishes.
import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.CompletionHandler; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption;public class Main { public static void main(String[] args) { Path path = Paths.get("data.txt"); try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ)) { ByteBuffer buffer = ByteBuffer.allocate(1024); // Read asynchronously (position 0) fileChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { System.out.println("Bytes read: " + result); attachment.flip(); byte[] data = new byte[attachment.limit()]; attachment.get(data); System.out.println("Content: " + new String(data)); } @Override public void failed(Throwable exc, ByteBuffer attachment) { System.err.println("Failed to read file"); exc.printStackTrace(); } }); // Do other tasks while the file is reading... System.out.println("Reading file in the background..."); Thread.sleep(1000); // Give the async task time to finish before exiting } catch (Exception e) { e.printStackTrace(); } } }
What does an object of the `java.io.File` class represent?