1. Introduction to File Handling in Java
1.1 What is File Handling?
File handling refers to the process of performing operations such as creating, reading, writing, updating, and deleting files from within a Java program. It allows a Java application to store persistent data on the local file system, read from external sources, or generate logs and reports.
In any software application, managing data beyond runtime (i.e., permanently storing it) is essential. File handling provides the necessary APIs and tools to interact with files and directories in a systematic and controlled manner.
1.2 Why is File Handling Important?
- Data Persistence: Data can be saved to a file and retrieved even after the application terminates.
- Configuration Management: Reading settings or configurations from a .properties or .json file.
- Logging: Generating logs or audit trails.
- Data Exchange: Interfacing with files in formats like CSV, TXT, JSON, and XML.
- User-Generated Content: Handling files uploaded by users or storing generated content.
1.3 Real-World Use Cases of File Handling
- Text Editors: Saving and loading files from disk.
- Media Players: Reading playlists and metadata.
- Web Applications: Managing file uploads or storing cached responses.
- Data Analysis Tools: Reading large datasets from CSV or JSON files.
- Backup Systems: Copying and organizing files for redundancy.
1.4 Java’s Support for File Handling
Java offers a rich set of APIs for file handling across two major packages:
- java.io – Legacy package that provides input and output streams, readers, writers, and the File class.
- java.nio.file – Introduced in Java 7, this package provides more modern, efficient, and flexible file I/O operations using paths and streams.
Java also allows integration with third-party libraries such as Apache Commons IO and Google Guava for enhanced functionality, but this series will focus on core Java capabilities.
1.5 Key Concepts in File Handling
Concept | Description |
---|---|
File | Represents a file or directory in the file system. |
Stream | A flow of data from a source to a destination (e.g., file input/output streams). |
Reader/Writer | Character-based classes for handling text files. |
InputStream/OutputStream | Byte-based classes for handling binary files. |
Path | Represents a file system path introduced in java.nio.file. |
Files | A utility class introduced in Java 7 to perform file operations like copy, move, delete, and read/write. |
2. Java File Handling APIs Overview
Java provides multiple APIs to handle file operations, categorized mainly into two packages: java.io and java.nio.file. Understanding the structure and purpose of each will help in selecting the appropriate API for specific file operations.
2.1 java.io Package (Legacy I/O)
The java.io package was the original API for file input and output in Java. It includes classes for working with files, directories, and stream-based I/O.
Key Classes:
- File: Represents a file or directory path.
- FileInputStream / FileOutputStream: For reading and writing binary files.
- FileReader / FileWriter: For reading and writing character-based files.
- BufferedReader / BufferedWriter: For efficient reading/writing with buffering.
- PrintWriter: For formatted text output to a file.
Characteristics:
- Stream-based (byte or character).
- Requires manual handling of buffers and exceptions.
- More verbose in syntax and less flexible for complex tasks.
2.2 java.nio.file Package (New I/O - NIO)
Introduced in Java 7 and enhanced in Java 8, java.nio.file provides a modern, efficient, and extensible way of handling files and directories. It uses channels, buffers, and paths for better performance and control.
Key Classes:
- Path: Represents a file path in the file system (replaces much of File class).
- Paths: Factory class to obtain Path instances.
- Files: Utility class with static methods for file operations like copy, move, read, write, and more.
- DirectoryStream: Interface for iterating over directory contents.
- StandardOpenOption: Enum to define how files are opened (e.g., APPEND, CREATE, TRUNCATE_EXISTING).
Java 8 Additions:
- Stream support with Files.lines(Path) for lazy reading of large files.
- Lambda support for filtering files using predicates.
- Files.walk(Path) to recursively traverse file trees with stream operations.
Advantages:
- Less boilerplate code.
- Better exception handling.
- High performance for large files.
- Flexible file filtering and directory traversal with streams.
2.3 java.util.Scanner (Text File Reading)
The Scanner class from java.util can be used to read content from a file line-by-line or token-by-token.
Scanner sc = new Scanner(new File("data.txt"));
while(sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
sc.close();
Pros:
- Easy to use.
- Useful for reading formatted data.
- Ideal for text parsing, CSVs, or logs.
Cons:
- Less efficient for large files.
- Limited support for encoding and complex file operations.
2.4 Comparing java.io vs java.nio.file
Feature | java.io | java.nio.file |
---|---|---|
File Representation | File class | Path interface |
Binary File I/O | FileInputStream, FileOutputStream | Files.newInputStream(), Files.newOutputStream() |
Character File I/O | FileReader, BufferedReader | Files.newBufferedReader() |
File Copy/Move/Delete | Manual or using Apache Commons | Files.copy(), Files.move(), Files.delete() |
Directory Traversal | Manual recursion | Files.walk(), Files.list() (Java 8) |
Performance | Less efficient | More efficient |
Syntax | Verbose | Cleaner with lambdas and streams |
2.5 When to Use What?
Use Case | Recommended API |
---|---|
Simple read/write operations | java.io or Scanner |
Large files, high performance | java.nio.file |
Directory traversal and filtering | java.nio.file with Streams (Java 8) |
Structured or formatted input | Scanner |
Binary file manipulation | java.io streams or Files with InputStream |
This overview provides the foundational knowledge to help you decide which API to use for various file operations. In the following sections, we will explore each API in detail, starting with working with the File class.
3. Working with the File Class (with Explanation and Output)
The File class in the java.io package allows you to interact with files and directories—check if they exist, create them, delete them, and get their properties. Let’s go through each feature with examples.
3.2 Creating a File Object
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File file = new File("example.txt");
}
}
Explanation:
This creates a File object pointing to example.txt. It does not create the physical file—it only represents the pathname in memory.
Output:
No output. File is not created yet.
3.3 Checking if File Exists
File file = new File("example.txt");
if (file.exists()) {
System.out.println("File exists.");
} else {
System.out.println("File does not exist.");
}
Explanation:
Checks whether the file physically exists in the specified location.
Possible Output:
File does not exist.
(If the file hasn't been created yet.)
3.4 Creating a New File
import java.io.*;
public class FileCreation {
public static void main(String[] args) {
File file = new File("example.txt");
try {
boolean created = file.createNewFile();
if (created) {
System.out.println("File created successfully.");
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- The method createNewFile() creates an empty file on disk.
- Returns true if the file was created, false if it already exists.
- Throws IOException if something goes wrong (e.g., lack of permission or invalid path).
Output (first run):
File created successfully.
Output (second run):
File already exists.
3.5 Creating a Directory
File dir = new File("myDirectory");
boolean created = dir.mkdir();
if (created) {
System.out.println("Directory created.");
} else {
System.out.println("Directory not created.");
}
Explanation:
- mkdir() creates a single directory.
- It fails if the parent directory doesn't exist.
- Use mkdirs() to create parent directories as well.
Possible Output:
Directory created.
3.6 Deleting a File
File file = new File("example.txt");
boolean deleted = file.delete();
if (deleted) {
System.out.println("File deleted successfully.");
} else {
System.out.println("Failed to delete file.");
}
Explanation:
- Deletes the file from the file system.
- Returns true if deleted, false if file didn't exist or deletion failed.
Output:
File deleted successfully.
Or:
Failed to delete file.
3.7 Checking File or Directory Type
File file = new File("example.txt");
if (file.isFile()) {
System.out.println("It is a file.");
} else if (file.isDirectory()) {
System.out.println("It is a directory.");
}
Explanation:
- isFile() returns true if it's a regular file.
- isDirectory() returns true if it's a directory.
Output:
It is a file.
3.8 Retrieving File Properties
File file = new File("example.txt");
System.out.println("Name: " + file.getName());
System.out.println("Absolute Path: " + file.getAbsolutePath());
System.out.println("Parent: " + file.getParent());
System.out.println("Readable: " + file.canRead());
System.out.println("Writable: " + file.canWrite());
System.out.println("Executable: " + file.canExecute());
System.out.println("Size (bytes): " + file.length());
Explanation:
- These methods provide metadata about the file: its name, full path, permissions, and size.
- file.length() returns size in bytes.
Output Example:
Name: example.txt
Absolute Path: C:\Users\Saumya\example.txt
Parent: C:\Users\Saumya
Readable: true
Writable: true
Executable: false
Size (bytes): 0
3.9 Listing Files in a Directory
File directory = new File("myDirectory");
String[] files = directory.list();
if (files != null) {
for (String fileName : files) {
System.out.println(fileName);
}
}
Explanation:
- list() returns an array of file names in the directory.
- Returns null if the directory doesn't exist or isn't a directory.
Output Example:
file1.txt
file2.txt
notes.docx
3.10 Renaming a File
File oldFile = new File("oldName.txt");
File newFile = new File("newName.txt");
boolean renamed = oldFile.renameTo(newFile);
System.out.println("File renamed: " + renamed);
Explanation:
- Renames oldName.txt to newName.txt.
- Can also move the file to a new directory.
Possible Output:
File renamed: true
3.11 Summary Table of File Class Methods
Method | Description |
---|---|
createNewFile() | Creates a new file |
mkdir() / mkdirs() | Creates single or multiple directories |
exists() | Checks existence of file/directory |
isFile() / isDirectory() | Type check |
delete() | Deletes file or empty directory |
getName() / getAbsolutePath() | Metadata |
length() | File size in bytes |
canRead() / canWrite() / canExecute() | Permission check |
list() / listFiles() | List directory contents |
renameTo(File) | Rename or move a file |
4. Reading from Files
Reading data from a file is one of the most common file handling operations. Java provides multiple ways to read files depending on whether the content is text or binary, and whether you want to read it line-by-line, all at once, or using modern Stream APIs.
4.1 Using FileReader and BufferedReader (Character Stream)
Code:
import java.io.*;
public class ReadWithBufferedReader {
public static void main(String[] args) {
File file = new File("example.txt");
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- FileReader reads character files.
- BufferedReader adds buffering, making reading more efficient especially for large files.
- readLine() reads one line at a time until it reaches the end of the file.
Example Input (example.txt):
Hello Java
Welcome to File Handling
Output:
Hello Java
Welcome to File Handling
4.2 Using Scanner
Code:
import java.io.*;
import java.util.Scanner;
public class ReadWithScanner {
public static void main(String[] args) {
File file = new File("example.txt");
try (Scanner sc = new Scanner(file)) {
while (sc.hasNextLine()) {
String line = sc.nextLine();
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Explanation:
- Scanner is useful for reading files with structured input (like space-separated or line-by-line).
- hasNextLine() checks if there's another line.
- It’s easier for beginners and supports token-based reading.
Output:
Same as previous example.
4.3 Using Files.readAllLines (Java 8)
Code:
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class ReadWithFilesReadAllLines {
public static void main(String[] args) {
Path path = Paths.get("example.txt");
try {
List lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Files.readAllLines(Path) reads the entire file content into a List.
- Easy and compact but not suitable for very large files.
Output:
Hello Java
Welcome to File Handling
4.4 Using Files.lines() and Stream API (Java 8)
Code:
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class ReadWithFilesLines {
public static void main(String[] args) {
Path path = Paths.get("example.txt");
try (Stream stream = Files.lines(path)) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Files.lines(Path) returns a Stream to lazily read lines.
- Efficient for large files.
- Supports stream operations like filter(), map(), etc.
Output:
Hello Java
Welcome to File Handling
4.5 Reading Binary Files using FileInputStream
Code:
import java.io.*;
public class ReadBinaryFile {
public static void main(String[] args) {
File file = new File("image.jpg");
try (FileInputStream fis = new FileInputStream(file)) {
int content;
while ((content = fis.read()) != -1) {
System.out.print((char) content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- FileInputStream reads raw bytes from a file.
- Useful for binary files like images, PDFs, etc.
- In the above example, we cast bytes to characters (not meaningful for binary data, just for demonstration).
Output:
Unprintable binary characters. Actual usage would be to process or save the byte data, not print it.
4.6 Specifying Character Encoding
By default, Java uses the platform’s default encoding. To specify encoding explicitly:
Code:
import java.io.*;
import java.nio.charset.StandardCharsets;
public class ReadWithEncoding {
public static void main(String[] args) {
File file = new File("example.txt");
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Using InputStreamReader with FileInputStream allows specifying the character set like UTF-8.
- Essential when dealing with multilingual content.
Comparison Table: File Reading Techniques
Approach | API Used | Suitable For | Java Version |
---|---|---|---|
BufferedReader | BufferedReader + FileReader | Efficient reading line-by-line | Java 1.1+ |
Scanner | Scanner | Simple file input, parsing | Java 5+ |
Files.readAllLines | Files | Small to medium files | Java 7+ |
Files.lines | Stream API | Large files, filtering | Java 8+ |
FileInputStream | FileInputStream | Binary files | Java 1.0+ |
Here is the detailed content for the next section of your Java File Handling series:
5. Writing to Files
Java provides multiple ways to write content to files, whether it's plain text or binary data. You can choose from traditional I/O streams, high-level writers, or Java 8+ utility classes depending on your needs.
5.1 Using FileWriter and BufferedWriter (Character Stream)
Code:
import java.io.*;
public class WriteWithBufferedWriter {
public static void main(String[] args) {
File file = new File("output.txt");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
writer.write("Hello Java");
writer.newLine();
writer.write("File writing using BufferedWriter.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- FileWriter is used to write character data to a file.
- BufferedWriter adds a buffer, which improves performance.
- newLine() writes a line separator (platform-independent).
Output (content in output.txt):
Hello Java
File writing using BufferedWriter.
5.2 Using PrintWriter
Code:
import java.io.*;
public class WriteWithPrintWriter {
public static void main(String[] args) {
try (PrintWriter writer = new PrintWriter("print_output.txt")) {
writer.println("Line 1");
writer.println("Line 2");
writer.printf("Formatted number: %.2f", 123.456);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- PrintWriter provides print-style methods (print, println, printf).
- Good for structured and formatted text.
Output (content in print_output.txt):
Line 1
Line 2
Formatted number: 123.46
5.3 Using Files.write() (Java 8)
Code:
import java.nio.file.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class WriteWithFilesWrite {
public static void main(String[] args) {
Path path = Paths.get("nio_output.txt");
List lines = Arrays.asList("First Line", "Second Line");
try {
Files.write(path, lines);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Files.write(Path, List) writes the entire list to a file.
- Automatically uses UTF-8 encoding.
- Overwrites existing file content.
Output (content in nio_output.txt):
First Line
Second Line
5.4 Appending to a File using FileWriter
Code:
import java.io.*;
public class AppendWithFileWriter {
public static void main(String[] args) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("append.txt", true))) {
writer.write("Appended line");
writer.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Pass true as the second argument to FileWriter to enable append mode.
- New content is added at the end of the file without overwriting.
Output:
Adds "Appended line" to append.txt without deleting previous content.
5.5 Writing Binary Data using FileOutputStream
Code:
import java.io.*;
public class WriteBinaryFile {
public static void main(String[] args) {
byte[] data = {65, 66, 67}; // ASCII for A, B, C
try (FileOutputStream fos = new FileOutputStream("binary.dat")) {
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- FileOutputStream writes raw bytes to a file.
- Ideal for writing binary data such as images, videos, and serialized objects.
Output (content in binary.dat):
Contains binary data representing "ABC" (not human-readable).
5.6 Using Files.write() with OpenOptions (Append in Java 8)
Code:
import java.nio.file.*;
import java.io.IOException;
import java.util.Collections;
public class AppendWithFilesWrite {
public static void main(String[] args) {
Path path = Paths.get("append_nio.txt");
try {
Files.write(path,
Collections.singletonList("Appended using NIO"),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Uses StandardOpenOption.APPEND to add content to an existing file.
- StandardOpenOption.CREATE creates the file if it doesn’t exist.
Output (in append_nio.txt):
Appended using NIO
6. File Streams
In Java, file input/output (I/O) is based on the concept of streams—a continuous flow of data between a source and a destination. Java provides two major categories of file streams:
- Byte Streams: For handling raw binary data (e.g., images, videos, PDFs).
- Character Streams: For handling text data (e.g., .txt, .csv, .json).
6.1 Byte Streams
Byte streams read and write 8-bit binary data. These streams are used when working with binary files or when text encoding is not required.
Main Classes:
- FileInputStream: Reads bytes from a file.
- FileOutputStream: Writes bytes to a file.
Example: Reading a Binary File
import java.io.*;
public class ByteStreamReader {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("sample.bin")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data); // for demo, casting to char
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Reads byte-by-byte from sample.bin.
- Each byte is cast to char just to demonstrate output, though real binary files shouldn't be printed like this.
Example: Writing a Binary File
import java.io.*;
public class ByteStreamWriter {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("output.bin")) {
fos.write(72); // H
fos.write(105); // i
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output (in file):
The binary content will represent:
Hi
6.2 Character Streams
Character streams read and write 16-bit Unicode characters, making them suitable for text files and internationalization.
Main Classes:
- FileReader: Reads characters from a file.
- FileWriter: Writes characters to a file.
Example: Reading a Text File
import java.io.*;
public class CharacterStreamReader {
public static void main(String[] args) {
try (FileReader fr = new FileReader("text.txt")) {
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output (if file contains):
Hello Java
Example: Writing to a Text File
import java.io.*;
public class CharacterStreamWriter {
public static void main(String[] args) {
try (FileWriter fw = new FileWriter("text.txt")) {
fw.write("Hello Java");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output (content in file):
Hello Java
6.3 Buffered Streams
Buffered streams improve performance by reducing the number of read/write operations.
Main Classes:
- BufferedInputStream / BufferedOutputStream
- BufferedReader / BufferedWriter
Example: Using BufferedInputStream
import java.io.*;
public class BufferedBinaryRead {
public static void main(String[] args) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("sample.bin"))) {
int byteData;
while ((byteData = bis.read()) != -1) {
System.out.print((char) byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Reads data in larger chunks (buffered), which is faster than reading byte-by-byte.
6.4 Difference Between Byte and Character Streams
Feature | Byte Stream | Character Stream |
---|---|---|
Classes | FileInputStream / FileOutputStream | FileReader / FileWriter |
Data Type | 8-bit bytes | 16-bit Unicode characters |
Usage | Binary files | Text files |
Encoding Awareness | No | Yes |
Performance | Fast for binary | Suitable for text |
6.5 Choosing Between Streams
Situation | Recommended Stream |
---|---|
Reading text | BufferedReader, FileReader, Scanner |
Writing text | BufferedWriter, FileWriter, PrintWriter |
Reading binary files (images, videos) | FileInputStream, BufferedInputStream |
Writing binary files | FileOutputStream, BufferedOutputStream |
7. Directory Handling
Java allows you to create, inspect, and manipulate directories using both the traditional java.io.File class and the modern java.nio.file API. You can create nested folders, list files inside a directory, or even traverse entire directory trees.
7.1 Creating a Directory Using File
Code:
import java.io.File;
public class CreateDirectory {
public static void main(String[] args) {
File dir = new File("myFolder");
if (dir.mkdir()) {
System.out.println("Directory created.");
} else {
System.out.println("Directory not created.");
}
}
}
Explanation:
- mkdir() creates a single directory.
- Fails if the parent directories do not exist.
Output:
Directory created.
(Or)
Directory not created.
7.2 Creating Nested Directories
Code:
File nestedDir = new File("parent/child/grandchild");
if (nestedDir.mkdirs()) {
System.out.println("Nested directories created.");
} else {
System.out.println("Failed to create nested directories.");
}
Explanation:
- mkdirs() creates all non-existent parent directories in the path.
Output:
Nested directories created.
7.3 Listing Directory Contents
Code:
File directory = new File("myFolder");
String[] contents = directory.list();
if (contents != null) {
for (String item : contents) {
System.out.println(item);
}
} else {
System.out.println("Directory not found or is not a directory.");
}
Explanation:
- list() returns names of all files and folders inside the directory.
- Returns null if the path does not point to a directory.
Output (if files exist):
file1.txt
file2.txt
subFolder
7.4 Listing Files as File Objects
Code:
File directory = new File("myFolder");
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
System.out.println("File: " + file.getName());
} else if (file.isDirectory()) {
System.out.println("Directory: " + file.getName());
}
}
}
Output:
File: file1.txt
Directory: subFolder
7.5 Directory Handling Using java.nio.file (Java 7+)
Creating a Directory Using Files.createDirectory()
import java.nio.file.*;
public class NioCreateDir {
public static void main(String[] args) {
Path path = Paths.get("nioFolder");
try {
Files.createDirectory(path);
System.out.println("Directory created using NIO.");
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Explanation:
- Throws exception if the directory already exists or path is invalid.
7.6 Creating Nested Directories with NIO
Path nestedPath = Paths.get("nioParent/nioChild");
try {
Files.createDirectories(nestedPath);
System.out.println("Nested directories created using NIO.");
} catch (Exception e) {
e.printStackTrace();
}
Explanation:
- createDirectories() creates all nonexistent directories.
7.7 Listing Files in a Directory Using Files.list() (Java 8)
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class ListWithFilesList {
public static void main(String[] args) {
Path dir = Paths.get("myFolder");
try (Stream stream = Files.list(dir)) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- Files.list() returns a lazily populated Stream of files and folders.
- Supports filtering with .filter() and other Stream operations.
7.8 Recursively Traversing Directories Using Files.walk() (Java 8)
Path root = Paths.get("myFolder");
try (Stream stream = Files.walk(root)) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Explanation:
- Files.walk() is used to traverse a directory recursively.
- Useful for searching files or building custom directory trees.
7.9 Filtering Directories or Files
try (Stream stream = Files.list(Paths.get("myFolder"))) {
stream.filter(Files::isDirectory)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Explanation:
- Uses method reference Files::isDirectory to filter only directories.
8. File Permissions and Security
In Java, you can check and control basic file permissions such as read, write, and execute using the java.io.File class. With the advent of java.nio.file in Java 7+, especially on POSIX-compliant systems (like Linux and macOS), you can manage more advanced permission sets.
8.1 Checking File Permissions Using File Class
Code:
import java.io.File;
public class CheckFilePermissions {
public static void main(String[] args) {
File file = new File("example.txt");
System.out.println("Can Read: " + file.canRead());
System.out.println("Can Write: " + file.canWrite());
System.out.println("Can Execute: " + file.canExecute());
}
}
Explanation:
- canRead(): returns true if the file is readable.
- canWrite(): returns true if the file is writable.
- canExecute(): returns true if the file is executable (e.g., .exe, .sh).
Output Example:
Can Read: true
Can Write: true
Can Execute: false
8.2 Modifying Permissions Using File Class
Code:
import java.io.File;
public class SetFilePermissions {
public static void main(String[] args) {
File file = new File("example.txt");
boolean readSet = file.setReadable(true);
boolean writeSet = file.setWritable(false);
boolean executeSet = file.setExecutable(true);
System.out.println("Readable set: " + readSet);
System.out.println("Writable set: " + writeSet);
System.out.println("Executable set: " + executeSet);
}
}
Explanation:
- You can grant or revoke file access using these setter methods.
- These are platform-dependent and may not work as expected on Windows.
Output Example:
Readable set: true
Writable set: true
Executable set: true
8.3 File Permissions with Java NIO (POSIX Support)
Java 7+ introduced PosixFilePermission for fine-grained control over file permissions. This is supported only on POSIX-compliant systems like Linux and macOS.
Code:
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
public class NioFilePermissions {
public static void main(String[] args) {
Path path = Paths.get("example.txt");
try {
Set perms = PosixFilePermissions.fromString("rw-r--r--");
Files.setPosixFilePermissions(path, perms);
System.out.println("Permissions set to rw-r--r--");
} catch (UnsupportedOperationException e) {
System.out.println("POSIX permissions not supported on this OS.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
- rw-r--r-- means: owner can read/write, group can read, others can read.
- Permissions must be passed as a Set.
- On Windows, this throws UnsupportedOperationException.
Output:
Permissions set to rw-r--r--
(Or, on Windows:)
POSIX permissions not supported on this OS.
8.4 Reading POSIX File Permissions
try {
Set permissions = Files.getPosixFilePermissions(Paths.get("example.txt"));
for (PosixFilePermission p : permissions) {
System.out.println(p);
}
} catch (IOException e) {
e.printStackTrace();
}
Sample Output:
OWNER_READ
OWNER_WRITE
GROUP_READ
OTHERS_READ
8.5 Notes on SecurityManager (Legacy)
- Java used to offer a SecurityManager for more advanced security control (e.g., preventing file deletion or write access in sandbox environments).
- As of Java 17, SecurityManager is deprecated for removal.
- It's generally not used in modern applications unless running inside a controlled JVM environment (like applets or enterprise containers).
Summary
In the first half of our comprehensive guide on Java File Handling, we explored the foundations of how Java interacts with the file system. We began with the importance of file handling and moved on to the available APIs—comparing java.io and java.nio.file.
We then detailed how to:
- Work with the File class to create, check, rename, or delete files and directories.
- Read and write text and binary files using streams and buffers.
- Understand the differences between byte streams and character streams.
- Use modern techniques such as buffered and NIO-based file operations.
- Create and traverse directories using both traditional and modern approaches.
- Check and modify file permissions, including support for POSIX permissions where applicable.
These concepts form the backbone of file operations in Java and prepare you to handle files securely and efficiently in real-world applications.
Next Blog- Java Multithreading Basics – Part 1