Page Content

Tutorials

What is File Handling in Java? How do I Create a File?

File Handling in Java

Because streams are abstractions that either produce (input stream) or consume (output stream) information, they constitute the foundation of Java’s I/O system. Through the Java I/O system, these streams are connected to actual devices and exhibit uniform behaviour across all devices. Two primary I/O stream types are supported by Java: character streams for 16-bit Unicode characters (optimised for internationalisation) and byte streams for 8-bit bytes (helpful for binary data).

The Class

When working with files and the file system directly instead of through streams, the java.io.File class is essential. The features of a file or directory itself are described by a File object, which lets you access or change data like permissions, time, date, and directory paths without dictating how data is read or written.

You can use a variety of constructors to build File objects, including one that requires a pathname string. The File class’s primary methods are as follows:

  • getName(): Returns the name of the file or directory.
  • getPath(): Converts the abstract pathname into a string.
  • getAbsolutePath(): Returns the absolute pathname string.
  • getParent(): Returns the parent directory’s pathname string.
  • exists(): Tests whether the file or directory exists.
  • canRead() / canWrite(): Checks if the application can read or modify the file.
  • isDirectory() / isFile(): Tests if the pathname denotes a directory or a normal file, respectively.
  • length(): Returns the length of the file in bytes.

For more modern file system operations, especially from JDK 7 onwards, the Path interface and Files class in the java.nio.file package offer a powerful alternative to the File class. The File class includes a toPath() method to convert a File object into a Path object, bridging the older and newer APIs.

Creating a File

There are multiple methods for programmatically creating a new, empty file:

  1. File.createNewFile(): Atomically creates a new, empty file if one with the same name doesn’t already exist.
  2. FileOutputStream: Creating a FileOutputStream object will create the file if it doesn’t exist before opening it for output.
  3. FileWriter: Similar to FileOutputStream, creating a FileWriter object will create the file if it doesn’t exist.
  4. Files.createFile(Path) (NIO.2): This static method creates a new, empty file.

Here’s an example using FileWriter to create a new file:

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class CreateFileDemo {
    public static void main(String[] args) {
        String fileName = "myNewFile.txt";
        try {
            File file = new File(fileName);
            // FileWriter will create the file if it doesn't exist
            FileWriter writer = new FileWriter(file);
            System.out.println("File created successfully: " + file.getAbsolutePath());
            writer.close(); // Close the writer even if nothing is written
        } catch (IOException e) {
            System.err.println("Error creating file: " + e.getMessage());
        }
    }
}

Code Output:

File created successfully: /path/to/your/project/myNewFile.txt

(The exact path will vary based on your system.)

Writing to File

In order to write data to a file, you usually use Writer subclasses for characters or OutputStream subclasses for bytes.

  • Byte Streams: To write 8-bit bytes to a file, utilise the FileOutputStream. Instead of writing an array of bytes, the write(byte[] buffer) function writes a single byte.
  • Character Streams: It is possible to write character streams to a file using FileWriter. It offers write(char[] cbuf) for character arrays, write(int c) for single characters, and write(String str) for strings among other functions.
  • Buffered Streams: BufferedWriter has been utilised to wrap a FileWriter for efficiency. It minimises the quantity of physical writes to the device by buffering the output.

Using the close() method to end an output stream or writer is essential for freeing up system resources and guaranteeing that any buffered data is sent to the physical device. If you don’t, you risk data loss or “memory leaks”

Even in the event of an exception, streams are immediately terminated when the try block is exited thanks to the try-with-resources statement, which has been an automated resource management tool since JDK 7.

Here’s an example of writing to a file using FileWriter wrapped in a BufferedWriter with try-with-resources:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class WriteFileDemo {
    public static void main(String[] args) {
        String fileName = "output.txt";
        String content = "Hello, world!\nThis is a test line.\nAnother line of text.";
        // Use try-with-resources to ensure the writer is closed automatically
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
            writer.write(content);
            System.out.println("Content successfully written to " + fileName);
        } catch (IOException e) {
            System.err.println("Error writing to file: " + e.getMessage());
        }
    }
}

Code Output:

Content successfully written to output.txt

(A file named output.txt will be created in the current directory with the specified content.)

You can also specify an append mode in the FileWriter constructor (e.g., new FileWriter(fileName, true)) to add content to the end of an existing file rather than overwriting it.

Reading Files

Reader subclasses for characters and InputStream subclasses for bytes are commonly used to read data from a file.

  1. Byte Streams: In order to read 8-bit bytes from a file, FileInputStream is utilised. The read(byte[] buffer) method reads bytes into an array, while the read() method reads a single byte (returning -1 at end-of-file).
  2. Character Streams: Character streams can be read from a file using FileReader. It has the same read() and read(char[] cbuf) methods as FileInputStream.
  3. Buffered Readers: BufferedReaders are frequently used to surround a FileReader or InputStreamReader in order to increase performance and provide access to readLine(), which reads a line of text.
  4. Scanner Class: The java.util.Scanner class provides a convenient way to read formatted input (e.g., line by line, word by word, or specific data types) from files.

Closing readers and input streams is essential to freeing up resources, much like when writing. Additionally, the try-with-resources declaration is strongly advised in this case.

Here’s an example of reading from a file using FileReader wrapped in a BufferedReader with try-with-resources:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFileDemo {
    public static void main(String[] args) {
        String fileName = "output.txt"; // Assuming output.txt exists from previous example
        // Use try-with-resources to ensure the reader is closed automatically
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            System.out.println("Reading content from " + fileName + ":");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("Error reading from file: " + e.getMessage());
        }
    }
}

Code Output:

Reading content from output.txt:
Hello, world!
This is a test line.
Another line of text.

Deleting Files

There are ways to remove files or empty directories using the File class:

  1. File.delete(): This function removes the directory or file that the abstract pathname represents. If it is a directory, it cannot be removed until it is empty.
  2. File.deleteOnExit(): In the event that the Java Virtual Machine ends, File.deleteOnExit() requests that the file or directory be erased.
  3. Files.deleteIfExists(Path) (NIO.2): A more reliable method of deleting files or directories that won’t raise an error if the file doesn’t exist.
import java.io.File;
import java.io.IOException;
public class DeleteFileDemo {
    public static void main(String[] args) {
        String fileName = "myNewFile.txt"; // Assuming myNewFile.txt exists
        File fileToDelete = new File(fileName);
        if (fileToDelete.exists()) {
            if (fileToDelete.delete()) {
                System.out.println(fileName + " deleted successfully.");
            } else {
                System.err.println("Failed to delete " + fileName);
            }
        } else {
            System.out.println(fileName + " does not exist.");
        }
    }
}

Code Output (if myNewFile.txt exists):

myNewFile.txt deleted successfully.

Code Output (if myNewFile.txt does not exist):

myNewFile.txt does not exist.

Directories

Directories are treated as a unique kind of File object in Java, which can hold a list of additional files and directories.

  • Creating Directories:
    • File.mkdir(): One directory is created by using File.mkdir(). It returns true if the directory is successful and false if it cannot be formed (for example, if parent directories are absent) or if the directory already exists.
    • File.mkdirs(): Constructs the directory with the abstract pathname, encompassing any parent directories that are required but no longer exist. Making nested directory structures is much easier using this.
  • Listing Directories:
    • File.list(): The function File.list() yields a string array containing the names of the files and directories within the directory.
    • File.list(FilenameFilter filter): Provides a list of strings that meet a given filter request.
    • Files.newDirectoryStream(Path) (NIO.2): This function opens a directory and returns a DirectoryStream, on which the contents can be iterated.

Here’s an example demonstrating creating and listing directories:

import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class DirectoryDemo {
    public static void main(String[] args) {
        String parentDirName = "myParentDir";
        String nestedDirName = "myParentDir/nestedDir";
        
        File parentDir = new File(parentDirName);
        File nestedDir = new File(nestedDirName);
        // 1. Create directories
        if (parentDir.mkdir()) {
            System.out.println("Parent directory '" + parentDirName + "' created.");
        } else {
            System.out.println("Parent directory '" + parentDirName + "' already exists or failed to create.");
        }
        if (nestedDir.mkdirs()) { // Using mkdirs to create parent and nested
            System.out.println("Nested directory '" + nestedDirName + "' created.");
        } else {
            System.out.println("Nested directory '" + nestedDirName + "' already exists or failed to create.");
        }
        // 2. Listing contents of parentDir using File.list()
        System.out.println("\nContents of '" + parentDirName + "':");
        String[] contents = parentDir.list();
        if (contents != null) {
            for (String item : contents) {
                File itemPath = new File(parentDir, item);
                System.out.println(item + (itemPath.isDirectory() ? " (Directory)" : " (File)"));
            }
        } else {
            System.out.println("Could not list contents of '" + parentDirName + "'.");
        }
        // 3. Listing contents using NIO.2 DirectoryStream
        Path dirPath = Paths.get(parentDirName);
        System.out.println("\nContents of '" + parentDirName + "' (NIO.2):");
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dirPath)) {
            for (Path entry : stream) {
                System.out.println(entry.getFileName() + (Files.isDirectory(entry) ? " (Directory)" : " (File)"));
            }
        } catch (IOException e) {
            System.err.println("Error listing directory (NIO.2): " + e.getMessage());
        }
        // Clean up: delete directories (must be empty first for File.delete())
        System.out.println("\nCleaning up directories...");
        if (nestedDir.delete()) {
            System.out.println("Nested directory '" + nestedDirName + "' deleted.");
        } else {
            System.err.println("Failed to delete nested directory '" + nestedDirName + "'.");
        }
        if (parentDir.delete()) {
            System.out.println("Parent directory '" + parentDirName + "' deleted.");
        } else {
            System.err.println("Failed to delete parent directory '" + parentDirName + "'.");
        }
    }
}

Code Output:

Parent directory 'myParentDir' created.
Nested directory 'myParentDir/nestedDir' created.
Contents of 'myParentDir':
nestedDir (Directory)
Contents of 'myParentDir' (NIO.2):
nestedDir (Directory)
Cleaning up directories...
Nested directory 'myParentDir/nestedDir' deleted.
Parent directory 'myParentDir' deleted.

Java provides comprehensive support for file handling and I/O (Input/Output) operations, which are crucial for programs to interact with external data like disk files. The primary packages involved in Java’s I/O system are java.io and java.nio.file.

Index