File Handling in Java
Learn File Handling in Java in detail, including File, Path, Files, streams, readers, writers, buffered I/O, serialization, random access, directory operations, file attributes, and best practices.
Introduction
File handling is the process of creating, reading, writing, updating, copying, moving, and deleting data stored in files. Java provides several APIs for working with text files, binary files, directories, file attributes, and persistent objects.
import java.nio.file.*;
Path file =
Path.of("message.txt");
Files.writeString(
file,
"Hello, Java!"
);
String content =
Files.readString(file);
System.out.println(content);Hello, Java!- What file handling is.
- Why applications need persistent storage.
- The difference between temporary and permanent data.
- The structure of Java I/O APIs.
- How file paths work.
- The difference between absolute and relative paths.
- How to use the File class.
- How to create and delete files.
- How to create and manage directories.
- How to use Path.
- How to use the Files utility class.
- How to read and write text files.
- How to append data to files.
- How to copy and move files.
- How byte streams work.
- How character streams work.
- How buffered streams improve performance.
- How to read files line by line.
- How to write formatted text.
- How data streams store primitive values.
- How object serialization works.
- How deserialization works.
- How RandomAccessFile works.
- How to inspect file attributes.
- How to walk directory trees.
- How to search for files.
- How to create temporary files.
- How try-with-resources manages file resources.
- Why character encoding matters.
- How to work with CSV and properties files.
- How to design safe file-handling code.
What is File Handling?
File handling allows a program to communicate with data stored outside the running application. Unlike variables in memory, file data can remain available after the program terminates.
JAVA APPLICATION
│
▼
FILE HANDLING API
│
├── Create
├── Read
├── Write
├── Append
├── Copy
├── Move
└── Delete
│
▼
FILE SYSTEM
├── Text Files
├── Binary Files
├── Configuration Files
├── Images
├── Documents
├── Logs
└── DirectoriesWhy File Handling?
Persistent Storage
File data remains available after the program terminates.
Configuration
Applications can store settings and configuration values.
Data Processing
Programs can import, process, and export structured data.
Logging
Applications can record events, errors, and activity.
Data Exchange
Files allow data to move between different applications.
Binary Content
Applications can process images, videos, documents, and other binary data.
Temporary vs Permanent Data
TEMPORARY DATA
Stored in memory
Examples:
Variables
Objects
Arrays
│
▼
Usually lost when
program terminates
PERMANENT DATA
Stored outside program memory
Examples:
Text Files
Binary Files
Databases
│
▼
Remains available after
program terminatesJava I/O Overview
Java provides two major groups of file-handling APIs: the traditional java.io package and the modern java.nio.file package.
JAVA FILE HANDLING
├── java.io
│
│ ├── File
│ ├── InputStream
│ ├── OutputStream
│ ├── Reader
│ ├── Writer
│ ├── BufferedReader
│ ├── BufferedWriter
│ ├── ObjectInputStream
│ └── ObjectOutputStream
│
└── java.nio.file
├── Path
├── Files
├── StandardOpenOption
├── StandardCopyOption
└── DirectoryStreamFile Handling APIs
java.io.File
Represents file and directory paths
Traditional API
java.io Streams
Read and write data
Byte and character based
java.nio.file.Path
Modern path representation
java.nio.file.Files
Modern file operations
Create
Read
Write
Copy
Move
Delete
Search- Prefer Path and Files for modern file-system operations.
- Use streams when processing data progressively.
- Use try-with-resources for resources that must be closed.
- Choose byte streams for binary data.
- Choose character streams for text data.
File Paths
A file path describes the location of a file or directory in the file system.
Windows:
C:\Users\Student\Documents\notes.txt
Linux:
/home/student/documents/notes.txt
Relative:
data/notes.txtAbsolute Paths
An absolute path describes the complete location of a file starting from the file-system root.
Path path = Path.of(
"C:\\Users\\Student\\Documents\\notes.txt"
);
System.out.println(
path.isAbsolute()
);Relative Paths
A relative path is interpreted from the current working directory of the application.
Path path =
Path.of(
"data",
"notes.txt"
);
System.out.println(
path.toAbsolutePath()
);- Relative paths depend on the current working directory.
- The working directory may differ between IDEs, terminals, servers, and tests.
- Use toAbsolutePath() while debugging path problems.
Path Separators
Path path = Path.of(
"data",
"users",
"users.txt"
);Passing path components separately is more portable than manually combining operating-system-specific separators.
The File Class
The java.io.File class represents the path of a file or directory. A File object does not automatically create a physical file.
import java.io.File;new File("data.txt")
│
▼
Creates Java File Object
│
✗
Does NOT automatically create
a physical fileCreating a File Object
File file =
new File("data.txt");
System.out.println(
file.getName()
);File file = new File(
"data",
"users.txt"
);Creating Files
import java.io.*;
public class CreateFileExample {
public static void main(
String[] args
) {
File file =
new File("data.txt");
try {
boolean created =
file.createNewFile();
if (created) {
System.out.println(
"File created"
);
} else {
System.out.println(
"File already exists"
);
}
} catch (IOException e) {
System.out.println(
"Unable to create file"
);
}
}
}Getting File Information
File file =
new File("data.txt");
System.out.println(
"Name: "
+ file.getName()
);
System.out.println(
"Path: "
+ file.getPath()
);
System.out.println(
"Absolute Path: "
+ file.getAbsolutePath()
);
System.out.println(
"Exists: "
+ file.exists()
);
System.out.println(
"File: "
+ file.isFile()
);
System.out.println(
"Directory: "
+ file.isDirectory()
);
System.out.println(
"Size: "
+ file.length()
);File Permissions
File file =
new File("data.txt");
System.out.println(
file.canRead()
);
System.out.println(
file.canWrite()
);
System.out.println(
file.canExecute()
);file.setReadable(true);
file.setWritable(true);
file.setExecutable(false);Renaming Files
File oldFile =
new File("old.txt");
File newFile =
new File("new.txt");
boolean renamed =
oldFile.renameTo(newFile);
System.out.println(renamed);Deleting Files
File file =
new File("data.txt");
if (file.delete()) {
System.out.println(
"File deleted"
);
} else {
System.out.println(
"Unable to delete file"
);
}Directories with File
File directory =
new File("data");
boolean created =
directory.mkdir();File directory =
new File(
"data/users/images"
);
boolean created =
directory.mkdirs();mkdir()
Creates one directory
Parent must normally exist
mkdirs()
Creates directory
Also creates missing
parent directoriesListing Directory Contents
File directory =
new File("data");
String[] names =
directory.list();
if (names != null) {
for (String name : names) {
System.out.println(name);
}
}File[] files =
directory.listFiles();
if (files != null) {
for (File file : files) {
System.out.println(
file.getName()
);
}
}The Path Interface
Path is the modern Java representation of a file-system path. It is part of the java.nio.file package.
import java.nio.file.Path;Creating Path Objects
Path file =
Path.of("data.txt");Path file = Path.of(
"data",
"users",
"users.txt"
);Path file =
Paths.get(
"data",
"users.txt"
);Path Information
Path path = Path.of(
"data",
"users",
"users.txt"
);
System.out.println(
path.getFileName()
);
System.out.println(
path.getParent()
);
System.out.println(
path.getRoot()
);
System.out.println(
path.getNameCount()
);
System.out.println(
path.toAbsolutePath()
);Resolving Paths
Path directory =
Path.of("data");
Path file =
directory.resolve(
"users.txt"
);
System.out.println(file);data/users.txtNormalizing Paths
Path path = Path.of(
"data",
"users",
"..",
"products",
".",
"items.txt"
);
System.out.println(
path.normalize()
);data/products/items.txtComparing Paths
Path first =
Path.of("data.txt");
Path second =
Path.of("data.txt");
System.out.println(
first.equals(second)
);- Path.equals() compares path representations.
- Two different path representations may still refer to the same physical file.
- Use Files.isSameFile() when checking whether two existing paths identify the same file.
The Files Class
The Files class provides static utility methods for performing modern file and directory operations.
import java.nio.file.Files;Create
Create files and directories.
Read
Read text, lines, and binary data.
Write
Write and append content.
Copy
Copy files and directories.
Move
Move and rename paths.
Delete
Delete files and directories.
Creating Files with Files
Path file =
Path.of("data.txt");
Files.createFile(file);- Files.createFile() throws FileAlreadyExistsException when the file already exists.
- Check Files.notExists() when creation should happen only for a missing file.
- Do not rely only on a check when concurrent programs may modify the same path.
Creating Directories
Files.createDirectory(
Path.of("data")
);Files.createDirectories(
Path.of(
"data",
"users",
"images"
)
);Writing Text Files
Path file =
Path.of("message.txt");
Files.writeString(
file,
"Hello, Java!"
);List<String> lines =
List.of(
"Java",
"Spring",
"Angular"
);
Files.write(
Path.of("courses.txt"),
lines
);Reading Text Files
String content =
Files.readString(
Path.of("message.txt")
);
System.out.println(content);List<String> lines =
Files.readAllLines(
Path.of("courses.txt")
);
for (String line : lines) {
System.out.println(line);
}- readString() loads the complete text into memory.
- readAllLines() loads every line into memory.
- For very large files, prefer buffered reading or Files.lines().
Appending to Files
Files.writeString(
Path.of("log.txt"),
"Application started"
+ System.lineSeparator(),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);CREATE
Create file if missing
CREATE_NEW
Create only if missing
APPEND
Write at end of file
TRUNCATE_EXISTING
Remove existing content
WRITE
Open for writing
READ
Open for readingCopying Files
Files.copy(
Path.of("source.txt"),
Path.of("backup.txt")
);Files.copy(
Path.of("source.txt"),
Path.of("backup.txt"),
StandardCopyOption.REPLACE_EXISTING
);Moving and Renaming Files
Files.move(
Path.of("source.txt"),
Path.of(
"backup",
"source.txt"
)
);Files.move(
Path.of("old.txt"),
Path.of("new.txt")
);Deleting with Files
Files.delete(
Path.of("data.txt")
);boolean deleted =
Files.deleteIfExists(
Path.of("data.txt")
);delete()
Throws exception if path
does not exist
deleteIfExists()
Returns false if path
does not existChecking File Properties
Path path =
Path.of("data.txt");
System.out.println(
Files.exists(path)
);
System.out.println(
Files.notExists(path)
);
System.out.println(
Files.isRegularFile(path)
);
System.out.println(
Files.isDirectory(path)
);
System.out.println(
Files.isReadable(path)
);
System.out.println(
Files.isWritable(path)
);
System.out.println(
Files.isExecutable(path)
);Byte Streams
Byte streams process raw 8-bit bytes. They are suitable for binary files such as images, audio, video, archives, and documents.
BYTE STREAMS
├── InputStream
│
│ ├── FileInputStream
│ ├── BufferedInputStream
│ ├── DataInputStream
│ └── ObjectInputStream
│
└── OutputStream
├── FileOutputStream
├── BufferedOutputStream
├── DataOutputStream
└── ObjectOutputStreamInputStream
InputStream is the abstract base class for reading byte data.
FILE
│
│ bytes
▼
InputStream
│
▼
JAVA PROGRAMOutputStream
OutputStream is the abstract base class for writing byte data.
JAVA PROGRAM
│
│ bytes
▼
OutputStream
│
▼
FILEFileInputStream
try (
FileInputStream input =
new FileInputStream(
"data.bin"
)
) {
int value;
while (
(value = input.read()) != -1
) {
System.out.println(value);
}
}The read() method returns the next byte as an integer or -1 when the end of the stream is reached.
FileOutputStream
try (
FileOutputStream output =
new FileOutputStream(
"data.bin"
)
) {
output.write(65);
output.write(66);
output.write(67);
}Copying Binary Files
try (
FileInputStream input =
new FileInputStream(
"photo.jpg"
);
FileOutputStream output =
new FileOutputStream(
"photo-copy.jpg"
)
) {
byte[] buffer =
new byte[8192];
int bytesRead;
while (
(bytesRead =
input.read(buffer)) != -1
) {
output.write(
buffer,
0,
bytesRead
);
}
}- Reading one byte at a time can be inefficient.
- A byte array reduces the number of I/O operations.
- Always write only the number of bytes actually read.
Character Streams
Character streams process text as characters rather than raw bytes.
CHARACTER STREAMS
├── Reader
│
│ ├── FileReader
│ ├── BufferedReader
│ └── InputStreamReader
│
└── Writer
├── FileWriter
├── BufferedWriter
├── OutputStreamWriter
└── PrintWriterReader
Reader is the abstract base class for character input.
TEXT FILE
│
▼
Reader
│
▼
Characters
│
▼
JAVA PROGRAMWriter
Writer is the abstract base class for character output.
JAVA PROGRAM
│
▼
Characters
│
▼
Writer
│
▼
TEXT FILEFileReader
try (
FileReader reader =
new FileReader(
"message.txt"
)
) {
int character;
while (
(character =
reader.read()) != -1
) {
System.out.print(
(char) character
);
}
}FileWriter
try (
FileWriter writer =
new FileWriter(
"message.txt"
)
) {
writer.write(
"Hello, Java!"
);
}try (
FileWriter writer =
new FileWriter(
"log.txt",
true
)
) {
writer.write(
"New log entry"
+ System.lineSeparator()
);
}Buffered Streams
Buffered streams use an in-memory buffer to reduce direct interactions with the underlying file system.
WITHOUT BUFFER
Program
│
├── Read
├── Read
├── Read
└── Read
│
▼
File System
WITH BUFFER
Program
│
▼
Buffer
│
▼
Larger I/O Operations
│
▼
File SystemBufferedInputStream
try (
BufferedInputStream input =
new BufferedInputStream(
new FileInputStream(
"photo.jpg"
)
)
) {
byte[] buffer =
new byte[8192];
while (
input.read(buffer) != -1
) {
// Process bytes
}
}BufferedOutputStream
try (
BufferedOutputStream output =
new BufferedOutputStream(
new FileOutputStream(
"data.bin"
)
)
) {
output.write(
new byte[] {
10,
20,
30
}
);
}BufferedReader
try (
BufferedReader reader =
Files.newBufferedReader(
Path.of("data.txt")
)
) {
String line;
while (
(line =
reader.readLine()) != null
) {
System.out.println(line);
}
}BufferedWriter
try (
BufferedWriter writer =
Files.newBufferedWriter(
Path.of("data.txt")
)
) {
writer.write("Java");
writer.newLine();
writer.write("Spring");
}PrintWriter
PrintWriter provides convenient methods for writing formatted text.
try (
PrintWriter writer =
new PrintWriter(
"report.txt"
)
) {
writer.println(
"Student Report"
);
writer.printf(
"Name: %s%n",
"Amit"
);
writer.printf(
"Marks: %d%n",
85
);
}Scanner for Files
try (
Scanner scanner =
new Scanner(
Path.of("data.txt")
)
) {
while (
scanner.hasNextLine()
) {
String line =
scanner.nextLine();
System.out.println(line);
}
}- Scanner is convenient for token-based parsing.
- It can parse integers, doubles, and words.
- BufferedReader is often faster for simple line-based reading.
- Choose based on parsing requirements.
Data Streams
Data streams read and write Java primitive values in binary form.
DataOutputStream
Writes:
int
double
boolean
char
UTF String
DataInputStream
Reads:
int
double
boolean
char
UTF StringDataOutputStream
try (
DataOutputStream output =
new DataOutputStream(
new FileOutputStream(
"student.dat"
)
)
) {
output.writeInt(101);
output.writeUTF(
"Amit"
);
output.writeDouble(
85.5
);
output.writeBoolean(
true
);
}DataInputStream
try (
DataInputStream input =
new DataInputStream(
new FileInputStream(
"student.dat"
)
)
) {
int id =
input.readInt();
String name =
input.readUTF();
double marks =
input.readDouble();
boolean active =
input.readBoolean();
System.out.println(
id
+ " "
+ name
+ " "
+ marks
+ " "
+ active
);
}- Data must be read in the same order in which it was written.
- The data type used for reading must match the written type.
- A mismatched order can produce incorrect data or exceptions.
Object Streams
Object streams allow complete Java objects to be written to and read from streams.
JAVA OBJECT
│
▼
ObjectOutputStream
│
▼
FILE
FILE
│
▼
ObjectInputStream
│
▼
JAVA OBJECTSerialization
Serialization is the process of converting an object state into a byte stream that can be stored or transferred.
OBJECT
Student {
id = 101
name = "Amit"
}
│
▼
SERIALIZATION
│
▼
BYTE STREAM
│
▼
student.serSerializable Interface
import java.io.Serializable;
public class Student
implements Serializable {
private int id;
private String name;
public Student(
int id,
String name
) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return id
+ " - "
+ name;
}
}Serializable is a marker interface. It does not declare methods but indicates that objects of the class can participate in Java serialization.
ObjectOutputStream
Student student =
new Student(
101,
"Amit"
);
try (
ObjectOutputStream output =
new ObjectOutputStream(
new FileOutputStream(
"student.ser"
)
)
) {
output.writeObject(
student
);
}Deserialization
Deserialization reconstructs an object from its serialized byte representation.
student.ser
│
▼
BYTE STREAM
│
▼
DESERIALIZATION
│
▼
Student ObjectObjectInputStream
try (
ObjectInputStream input =
new ObjectInputStream(
new FileInputStream(
"student.ser"
)
)
) {
Student student =
(Student)
input.readObject();
System.out.println(student);
}serialVersionUID
serialVersionUID is a version identifier used during serialization and deserialization to verify class compatibility.
public class Student
implements Serializable {
private static final long
serialVersionUID = 1L;
private int id;
private String name;
}The transient Keyword
A transient field is excluded from default Java serialization.
public class User
implements Serializable {
private String username;
private transient String password;
}- Do not assume transient alone provides complete security.
- Sensitive data should be designed and protected carefully.
- Avoid storing plaintext passwords in objects or files.
RandomAccessFile
RandomAccessFile allows reading and writing at arbitrary positions within a file.
try (
RandomAccessFile file =
new RandomAccessFile(
"data.dat",
"rw"
)
) {
file.writeInt(100);
file.writeInt(200);
file.writeInt(300);
file.seek(4);
int value =
file.readInt();
System.out.println(value);
}POSITION 0
100
POSITION 4
200
POSITION 8
300File Attributes
Path path =
Path.of("data.txt");
BasicFileAttributes attributes =
Files.readAttributes(
path,
BasicFileAttributes.class
);
System.out.println(
attributes.size()
);
System.out.println(
attributes.creationTime()
);
System.out.println(
attributes.lastModifiedTime()
);
System.out.println(
attributes.isRegularFile()
);
System.out.println(
attributes.isDirectory()
);Directory Streams
Path directory =
Path.of("data");
try (
DirectoryStream<Path> stream =
Files.newDirectoryStream(
directory
)
) {
for (Path path : stream) {
System.out.println(
path.getFileName()
);
}
}try (
DirectoryStream<Path> stream =
Files.newDirectoryStream(
directory,
"*.txt"
)
) {
for (Path path : stream) {
System.out.println(path);
}
}Walking Directory Trees
try (
Stream<Path> paths =
Files.walk(
Path.of("data")
)
) {
paths.forEach(
System.out::println
);
}data
├── users
│ ├── users.txt
│ └── profile.txt
│
├── products
│ └── products.txt
│
└── config.txtSearching Files
try (
Stream<Path> files =
Files.find(
Path.of("data"),
10,
(
path,
attributes
) ->
attributes
.isRegularFile()
&&
path.toString()
.endsWith(".txt")
)
) {
files.forEach(
System.out::println
);
}Temporary Files
Path tempFile =
Files.createTempFile(
"report-",
".txt"
);
System.out.println(tempFile);Path tempDirectory =
Files.createTempDirectory(
"application-"
);
System.out.println(
tempDirectory
);Try-with-Resources
File streams, readers, writers, and directory streams should normally be managed using try-with-resources.
try (
BufferedReader reader =
Files.newBufferedReader(
Path.of("data.txt")
)
) {
System.out.println(
reader.readLine()
);
}RESOURCE CREATED
│
▼
TRY BLOCK EXECUTES
│
├── Success
│
└── Exception
│
▼
RESOURCE CLOSED AUTOMATICALLYCharacter Encoding
Text files store characters as bytes according to a character encoding. Using an explicit encoding avoids platform-dependent behavior.
Files.writeString(
Path.of("message.txt"),
"Hello, Java!",
StandardCharsets.UTF_8
);String content =
Files.readString(
Path.of("message.txt"),
StandardCharsets.UTF_8
);- Use UTF-8 for modern text files unless another format is required.
- Use the same encoding when reading and writing.
- Encoding mismatches can produce corrupted or unreadable text.
Working with CSV Files
id,name,marks
101,Amit,85
102,Neha,92
103,Rahul,78try (
BufferedReader reader =
Files.newBufferedReader(
Path.of(
"students.csv"
)
)
) {
String line;
while (
(line =
reader.readLine()) != null
) {
String[] values =
line.split(",");
for (String value : values) {
System.out.print(
value + " | "
);
}
System.out.println();
}
}- Values may contain commas.
- Values may contain quotes.
- Values may contain line breaks.
- Simple split(",") is suitable only for simple controlled data.
- Production applications should use a proper CSV parser for complex CSV formats.
Working with Properties Files
app.name=PrograMinds
app.version=1.0
server.port=8080Properties properties =
new Properties();
try (
InputStream input =
Files.newInputStream(
Path.of(
"application.properties"
)
)
) {
properties.load(input);
}
System.out.println(
properties.getProperty(
"app.name"
)
);Properties properties =
new Properties();
properties.setProperty(
"app.name",
"PrograMinds"
);
properties.setProperty(
"app.version",
"1.0"
);
try (
OutputStream output =
Files.newOutputStream(
Path.of(
"application.properties"
)
)
) {
properties.store(
output,
"Application Settings"
);
}Log File Example
import java.io.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.time.*;
public class FileLogger {
private final Path logFile;
public FileLogger(
String filename
) {
this.logFile =
Path.of(filename);
}
public void log(
String message
) {
String entry =
LocalDateTime.now()
+ " - "
+ message
+ System.lineSeparator();
try {
Files.writeString(
logFile,
entry,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
} catch (IOException e) {
throw new RuntimeException(
"Unable to write log file",
e
);
}
}
}public class Main {
public static void main(
String[] args
) {
FileLogger logger =
new FileLogger(
"application.log"
);
logger.log(
"Application started"
);
logger.log(
"User logged in"
);
}
}Student Records Example
public class Student {
private final int id;
private final String name;
private final double marks;
public Student(
int id,
String name,
double marks
) {
this.id = id;
this.name = name;
this.marks = marks;
}
public String toCsv() {
return id
+ ","
+ name
+ ","
+ marks;
}
}public class StudentFileService {
private final Path file;
public StudentFileService(
String filename
) {
this.file =
Path.of(filename);
}
public void save(
Student student
) {
try {
Files.writeString(
file,
student.toCsv()
+ System.lineSeparator(),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
} catch (IOException e) {
throw new RuntimeException(
"Unable to save student",
e
);
}
}
public List<String> readAll() {
try {
if (
Files.notExists(file)
) {
return List.of();
}
return Files.readAllLines(
file
);
} catch (IOException e) {
throw new RuntimeException(
"Unable to read students",
e
);
}
}
}File Copy Application
import java.io.*;
import java.nio.file.*;
public class FileCopyApplication {
public static void copy(
Path source,
Path target
) {
if (
Files.notExists(source)
) {
throw new IllegalArgumentException(
"Source file does not exist"
);
}
try {
Files.copy(
source,
target,
StandardCopyOption
.REPLACE_EXISTING
);
} catch (IOException e) {
throw new RuntimeException(
"Unable to copy file",
e
);
}
}
public static void main(
String[] args
) {
Path source =
Path.of("source.txt");
Path target =
Path.of("backup.txt");
copy(
source,
target
);
System.out.println(
"File copied successfully"
);
}
}Common File Handling Errors
FILE HANDLING ERRORS
├── Assuming File Object Creates a File
├── Wrong File Paths
├── Hardcoded Absolute Paths
├── Wrong Working Directory
├── Platform-Specific Separators
├── Missing Parent Directories
├── Ignoring File Existence
├── Overwriting Existing Files Accidentally
├── Forgetting Append Mode
├── Using Wrong Open Options
├── Ignoring FileAlreadyExistsException
├── Ignoring NoSuchFileException
├── Ignoring AccessDeniedException
├── Forgetting to Close Resources
├── Closing Resources Manually Incorrectly
├── Ignoring Try-with-Resources
├── Reading One Byte at a Time
├── Writing One Byte at a Time
├── Loading Huge Files Entirely into Memory
├── Using readAllLines() for Huge Files
├── Using readString() for Huge Files
├── Using Byte Streams for Text Without Reason
├── Using Character Streams for Binary Files
├── Ignoring Character Encoding
├── Reading with Different Encoding
├── Corrupting Binary Files
├── Incorrect Buffer Handling
├── Writing Entire Buffer Instead of bytesRead
├── Wrong DataInputStream Read Order
├── Wrong Primitive Data Types
├── Serializing Non-Serializable Objects
├── Missing serialVersionUID
├── Assuming transient Means Encryption
├── Deserializing Untrusted Data
├── Unsafe File Names
├── Path Traversal Vulnerabilities
├── Exposing Internal File Paths
├── Ignoring File Permissions
├── Assuming Delete Always Succeeds
├── Deleting Non-Empty Directories
├── Forgetting Stream Closure
├── Forgetting Files.walk() Returns a Stream
├── Ignoring Exceptions During File Operations
├── Empty catch Blocks
├── Losing Original Exception Causes
├── Returning null for Every Failure
├── Mixing File Logic with Business Logic
├── Using split(",") for Complex CSV
├── Storing Passwords in Plaintext Files
├── Writing Sensitive Data to Logs
├── Unbounded Log File Growth
├── Race Conditions Between Check and Use
├── Assuming Path.equals() Means Same Physical File
└── Treating All I/O Failures the SameBest Practices
- Prefer Path and Files for modern file-system operations.
- Use File when working with older APIs that require it.
- Understand that creating a File object does not create a physical file.
- Use Path.of() for path creation.
- Build paths from separate components.
- Avoid manually combining path separators.
- Avoid hardcoded absolute paths when portability matters.
- Understand the application working directory.
- Use toAbsolutePath() when debugging relative paths.
- Use normalize() when processing paths containing redundant elements.
- Validate external file paths carefully.
- Prevent path traversal when accepting user-controlled file names.
- Do not expose unrestricted file-system access to users.
- Create required parent directories before creating files.
- Use Files.createDirectories() for nested directory structures.
- Check whether operations may overwrite existing files.
- Use REPLACE_EXISTING only when replacement is intentional.
- Use APPEND only when existing content should be preserved.
- Use CREATE when a missing file should be created.
- Use CREATE_NEW when accidental replacement must be prevented.
- Use deleteIfExists() when a missing path is acceptable.
- Handle specific file exceptions when useful.
- Distinguish missing files from permission failures.
- Use byte streams for binary data.
- Use character streams for text data.
- Use buffering for repeated I/O operations.
- Use a byte array when copying binary streams.
- Write only the number of bytes actually read.
- Use BufferedReader for line-based reading.
- Use BufferedWriter for efficient text writing.
- Use PrintWriter for convenient formatted text.
- Use Scanner when token parsing is required.
- Avoid loading very large files completely into memory.
- Process large files incrementally.
- Close Files.lines() streams.
- Close Files.walk() streams.
- Use try-with-resources for AutoCloseable resources.
- Avoid manual resource management when try-with-resources applies.
- Use explicit character encodings.
- Prefer UTF-8 unless another encoding is required.
- Use the same encoding for reading and writing.
- Read DataInputStream values in the same order they were written.
- Use matching primitive read and write methods.
- Use serialization carefully.
- Declare serialVersionUID for serializable classes.
- Use transient only when excluding fields from default serialization.
- Do not treat transient as encryption.
- Avoid deserializing untrusted data.
- Use safer structured formats when long-term compatibility matters.
- Do not store plaintext passwords.
- Do not write secrets into log files.
- Control log file growth.
- Use meaningful file names.
- Use temporary files for short-lived intermediate data.
- Clean temporary resources when appropriate.
- Check file permissions when access problems occur.
- Do not assume delete operations always succeed.
- Remember that directories normally must be empty before deletion.
- Use Files.isSameFile() when checking physical file identity.
- Separate file access from business logic.
- Create dedicated file service classes.
- Translate low-level I/O exceptions when higher layers need meaningful failures.
- Preserve original exception causes.
- Do not leave catch blocks empty.
- Log file failures with useful context.
- Do not expose sensitive internal paths to end users.
- Use proper CSV libraries for complex CSV data.
- Test file-not-found scenarios.
- Test permission failures.
- Test empty files.
- Test large files.
- Test invalid paths.
- Test existing destination files.
- Test character encoding.
- Test resource cleanup.
- Design file operations to avoid accidental data loss.
- Create backups before destructive operations when necessary.
- Use atomic operations where consistency requirements demand them.
- Treat external file content as untrusted input.
- Validate parsed data before using it.
- Handle partial failures carefully.
- Make file operations understandable and predictable.
Common Misconceptions
- Creating a File object does not create a physical file.
- A relative path is not relative to the source code file.
- Relative paths depend on the working directory.
- File and Files are different APIs.
- Path represents a path but does not itself perform every file operation.
- Files contains utility methods for file-system operations.
- Byte streams are not only for text.
- Character streams should not be used for arbitrary binary data.
- Reading one byte at a time is not always efficient.
- A larger file should not always be loaded completely into memory.
- Closing resources is necessary even when exceptions occur.
- Try-with-resources simplifies resource cleanup.
- FileReader and FileWriter are not ideal when explicit encoding control is required.
- Character encoding matters.
- UTF-8 is not automatically guaranteed in every older Java environment or API.
- Appending and overwriting are different operations.
- Files.copy() does not automatically replace an existing destination unless instructed.
- Deleting a directory is not always recursive.
- A non-empty directory normally cannot be deleted directly.
- Path.equals() does not always prove two paths identify the same physical file.
- Serialization is not the same as encryption.
- transient is not a security mechanism.
- Serializable is a marker interface.
- Deserialization of untrusted content can be dangerous.
- Simple split(",") does not correctly parse every valid CSV file.
- File handling is not only about text files.
- Directories are also part of file-system handling.
- Exceptions should not be silently ignored.
- Successful existence checks do not guarantee later operations will succeed.
- File-system state can change between operations.
Practice Exercises
- Create a Path for notes.txt.
- Create the file.
- Check whether it exists.
- Display its absolute path.
- Handle an existing file correctly.
- Write five programming language names to a file.
- Read the complete file.
- Read the file line by line.
- Display every line.
- Use UTF-8 encoding.
- Create application.log.
- Append the current date and time.
- Append an event message.
- Run the program multiple times.
- Verify that previous content remains.
- Choose an image file.
- Read it using FileInputStream.
- Write it using FileOutputStream.
- Use an 8192-byte buffer.
- Verify that the copied image opens correctly.
- Accept a directory path.
- Verify that it exists.
- List all child paths.
- Identify files and directories separately.
- Display file sizes.
- Create a Student class.
- Store students in a CSV file.
- Read all student records.
- Convert each row into a Student object.
- Display the student list.
- Create a serializable Employee class.
- Add serialVersionUID.
- Serialize an Employee object.
- Deserialize the object.
- Display the restored values.
- Walk through a directory tree.
- Find all .java files.
- Display each matching path.
- Count the number of matches.
- Close the stream correctly.
Common Interview Questions
What is file handling in Java?
File handling is the process of creating, reading, writing, updating, copying, moving, and deleting files and directories.
What is the difference between File and Files?
File is a traditional java.io class representing file and directory paths, while Files is a modern utility class providing static file-system operations.
What is Path?
Path is the modern java.nio.file representation of a file-system path.
Does new File("data.txt") create a physical file?
No. It only creates a Java object representing the path.
What is the difference between an absolute and relative path?
An absolute path starts from the file-system root, while a relative path is resolved from the current working directory.
What is the difference between byte streams and character streams?
Byte streams process raw bytes and are suitable for binary data, while character streams process text characters.
What are InputStream and OutputStream?
They are abstract base classes for reading and writing byte data.
What are Reader and Writer?
They are abstract base classes for reading and writing character data.
Why are buffered streams used?
They reduce direct I/O operations by processing data through an in-memory buffer.
What is try-with-resources?
It automatically closes resources that implement AutoCloseable.
What is serialization?
Serialization converts object state into a byte stream for storage or transfer.
What is deserialization?
Deserialization reconstructs an object from its serialized byte representation.
What is serialVersionUID?
It is a version identifier used to check compatibility between serialized data and a class.
What does transient do?
It excludes a field from default Java serialization.
What is RandomAccessFile?
It allows reading and writing at arbitrary positions in a file.
What is the difference between mkdir() and mkdirs()?
mkdir() creates one directory, while mkdirs() also creates missing parent directories.
Why is character encoding important?
Text characters must be converted to and from bytes using a consistent encoding to prevent corrupted text.
Frequently Asked Questions
Should I use File or Path?
Prefer Path and Files for modern Java code unless an older API specifically requires File.
Should I use FileReader for every text file?
Not necessarily. Files.newBufferedReader() provides convenient buffered reading with explicit encoding support.
How should I process a very large file?
Process it incrementally using buffered reading or streaming instead of loading the entire file into memory.
Should streams always be closed?
Resources that hold file-system resources should be closed, preferably using try-with-resources.
Can Java read images and videos?
Yes. Binary files can be processed using byte streams or modern Files APIs.
Is Java serialization recommended for every persistent object?
No. Serialization has compatibility and security concerns. Structured formats or databases may be better for long-term application data.
What should I learn after File Handling?
The next lesson introduces Collections Basics in Java.
Key Takeaways
- File handling manages data stored outside application memory.
- Files provide persistent storage.
- Java provides java.io and java.nio.file APIs.
- File is the traditional path representation.
- Creating a File object does not create a physical file.
- Path is the modern path representation.
- Files provides modern file-system utility methods.
- Absolute paths begin from the file-system root.
- Relative paths depend on the current working directory.
- Path components should be constructed portably.
- File can inspect files and directories.
- createNewFile() creates a physical file.
- mkdir() creates one directory.
- mkdirs() creates missing parent directories.
- Path.of() creates Path objects.
- resolve() combines paths.
- normalize() removes redundant path elements.
- Files.createFile() creates files.
- Files.createDirectories() creates nested directories.
- Files.writeString() writes text.
- Files.readString() reads complete text.
- Files.write() writes collections of lines.
- Files.readAllLines() reads all lines into memory.
- Large files should be processed incrementally.
- StandardOpenOption.APPEND appends content.
- Files.copy() copies paths.
- Files.move() moves or renames paths.
- Files.delete() deletes a path.
- Files.deleteIfExists() safely handles missing paths.
- Byte streams process raw bytes.
- InputStream reads bytes.
- OutputStream writes bytes.
- FileInputStream reads file bytes.
- FileOutputStream writes file bytes.
- Binary files should be copied using byte streams.
- Buffers improve repeated I/O performance.
- Character streams process text.
- Reader reads characters.
- Writer writes characters.
- BufferedReader reads text efficiently.
- BufferedWriter writes text efficiently.
- PrintWriter provides formatted output.
- Scanner is useful for token parsing.
- Data streams read and write primitive values.
- Primitive values must be read in the same order they were written.
- Object streams support Java object serialization.
- Serializable is a marker interface.
- Serialization converts object state into bytes.
- Deserialization reconstructs objects.
- serialVersionUID identifies serialization versions.
- transient excludes fields from default serialization.
- transient is not encryption.
- RandomAccessFile supports arbitrary file positions.
- File attributes provide metadata.
- DirectoryStream iterates directory contents.
- Files.walk() traverses directory trees.
- Files.find() searches paths.
- Temporary files support short-lived data.
- Try-with-resources automatically closes resources.
- Character encoding must be handled consistently.
- UTF-8 is a strong default for modern text files.
- Simple CSV parsing has limitations.
- Properties files store key-value configuration.
- File operations can fail because of missing files, permissions, invalid paths, and other I/O conditions.
- Exceptions should be handled meaningfully.
- Original exception causes should be preserved.
- External file content should be treated as untrusted input.
- Sensitive information should not be stored carelessly.
- Modern Java code should generally prefer Path and Files.
- Safe file handling prevents data loss and resource leaks.
Summary
File handling allows Java applications to store and retrieve data beyond the lifetime of the running program. Applications use files for configuration, logs, documents, binary content, data exchange, and many other forms of persistent information.
Java provides the traditional java.io API and the modern java.nio.file API. The File class represents file and directory paths, while Path and Files provide a more modern approach to path management and file-system operations.
Files can be created, read, written, appended, copied, moved, and deleted. Directories can be created, listed, searched, and traversed using modern file APIs.
Byte streams process binary data, while character streams process text. Buffered streams improve efficiency by reducing direct file-system interactions.
Data streams support primitive values, object streams support serialization, and RandomAccessFile supports reading and writing at specific file positions.
Try-with-resources should be used to manage streams, readers, writers, and other AutoCloseable resources. It ensures that resources are closed correctly even when exceptions occur.
Reliable file handling requires careful path management, explicit character encoding, appropriate stream selection, safe resource cleanup, meaningful exception handling, validation of external data, and protection against accidental data loss.
- You understand what file handling is.
- You understand temporary and permanent data.
- You know the major Java file APIs.
- You understand file paths.
- You know absolute and relative paths.
- You understand path separators.
- You can use the File class.
- You can create files.
- You can inspect file information.
- You can check file permissions.
- You can rename files.
- You can delete files.
- You can create directories.
- You can list directory contents.
- You understand Path.
- You can create Path objects.
- You can inspect paths.
- You can resolve and normalize paths.
- You understand path comparison.
- You can use the Files class.
- You can create files and directories with Files.
- You can write text files.
- You can read text files.
- You can append data.
- You can copy files.
- You can move and rename files.
- You can delete paths.
- You can inspect file properties.
- You understand byte streams.
- You can use InputStream.
- You can use OutputStream.
- You can use FileInputStream.
- You can use FileOutputStream.
- You can copy binary files.
- You understand character streams.
- You can use Reader and Writer.
- You can use FileReader and FileWriter.
- You understand buffered streams.
- You can use BufferedReader.
- You can use BufferedWriter.
- You can use PrintWriter.
- You can read files with Scanner.
- You understand data streams.
- You can store primitive values.
- You understand object streams.
- You understand serialization.
- You understand deserialization.
- You understand serialVersionUID.
- You understand transient fields.
- You can use RandomAccessFile.
- You can inspect file attributes.
- You can use directory streams.
- You can walk directory trees.
- You can search for files.
- You can create temporary files.
- You can manage resources with try-with-resources.
- You understand character encoding.
- You understand basic CSV file handling.
- You can work with properties files.
- You can build practical file-handling applications.
- You can identify common file-handling errors.
- You know file-handling best practices.
- You are ready to learn Collections Basics in Java.