LearnContact
Lesson 2765 min read

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.

Simple File Example
import java.nio.file.*;


Path file =
    Path.of("message.txt");


Files.writeString(
    file,
    "Hello, Java!"
);


String content =
    Files.readString(file);


System.out.println(content);
Output
Hello, Java!
What You Will Learn
  • 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.

File Handling Flow
JAVA APPLICATION

        │
        ▼

FILE HANDLING API

        │
        ├── Create
        ├── Read
        ├── Write
        ├── Append
        ├── Copy
        ├── Move
        └── Delete

        │
        ▼

FILE SYSTEM

├── Text Files
├── Binary Files
├── Configuration Files
├── Images
├── Documents
├── Logs
└── Directories

Why 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

Data Storage
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 terminates

Java 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 APIs
JAVA FILE HANDLING

├── java.io
│
│   ├── File
│   ├── InputStream
│   ├── OutputStream
│   ├── Reader
│   ├── Writer
│   ├── BufferedReader
│   ├── BufferedWriter
│   ├── ObjectInputStream
│   └── ObjectOutputStream
│
└── java.nio.file

    ├── Path
    ├── Files
    ├── StandardOpenOption
    ├── StandardCopyOption
    └── DirectoryStream

File Handling APIs

API Comparison
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
Modern Recommendation
  • 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.

Example Paths
Windows:

C:\Users\Student\Documents\notes.txt


Linux:

/home/student/documents/notes.txt


Relative:

data/notes.txt

Absolute Paths

An absolute path describes the complete location of a file starting from the file-system root.

Absolute Path
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.

Relative Path
Path path =
    Path.of(
        "data",
        "notes.txt"
    );


System.out.println(
    path.toAbsolutePath()
);
Working Directory Matters
  • 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

Portable Path Construction
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 File
import java.io.File;
Important Distinction
new File("data.txt")

        │
        ▼

Creates Java File Object

        │

        ✗

Does NOT automatically create
a physical file

Creating a File Object

File Object
File file =
    new File("data.txt");


System.out.println(
    file.getName()
);
Directory and Child
File file = new File(
    "data",
    "users.txt"
);

Creating Files

CreateFileExample.java
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 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

Permission Checks
File file =
    new File("data.txt");


System.out.println(
    file.canRead()
);


System.out.println(
    file.canWrite()
);


System.out.println(
    file.canExecute()
);
Changing Permissions
file.setReadable(true);

file.setWritable(true);

file.setExecutable(false);

Renaming Files

Rename File
File oldFile =
    new File("old.txt");


File newFile =
    new File("new.txt");


boolean renamed =
    oldFile.renameTo(newFile);


System.out.println(renamed);

Deleting Files

Delete File
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

Create One Directory
File directory =
    new File("data");


boolean created =
    directory.mkdir();
Create Nested Directories
File directory =
    new File(
        "data/users/images"
    );


boolean created =
    directory.mkdirs();
mkdir vs mkdirs
mkdir()

Creates one directory

Parent must normally exist



mkdirs()

Creates directory

Also creates missing
parent directories

Listing Directory Contents

List Names
File directory =
    new File("data");


String[] names =
    directory.list();


if (names != null) {

    for (String name : names) {

        System.out.println(name);

    }

}
List File Objects
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 Path
import java.nio.file.Path;

Creating Path Objects

Path.of()
Path file =
    Path.of("data.txt");
Multiple Components
Path file = Path.of(
    "data",
    "users",
    "users.txt"
);
Paths.get()
Path file =
    Paths.get(
        "data",
        "users.txt"
    );

Path Information

Inspect Path
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

resolve()
Path directory =
    Path.of("data");


Path file =
    directory.resolve(
        "users.txt"
    );


System.out.println(file);
Result
data/users.txt

Normalizing Paths

normalize()
Path path = Path.of(
    "data",
    "users",
    "..",
    "products",
    ".",
    "items.txt"
);


System.out.println(
    path.normalize()
);
Result
data/products/items.txt

Comparing Paths

Path Comparison
Path first =
    Path.of("data.txt");


Path second =
    Path.of("data.txt");


System.out.println(
    first.equals(second)
);
Path Equality
  • 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 Files
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

Create File
Path file =
    Path.of("data.txt");


Files.createFile(file);
Existing 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

Single Directory
Files.createDirectory(
    Path.of("data")
);
Nested Directories
Files.createDirectories(
    Path.of(
        "data",
        "users",
        "images"
    )
);

Writing Text Files

Files.writeString()
Path file =
    Path.of("message.txt");


Files.writeString(
    file,
    "Hello, Java!"
);
Files.write()
List<String> lines =
    List.of(
        "Java",
        "Spring",
        "Angular"
    );


Files.write(
    Path.of("courses.txt"),
    lines
);

Reading Text Files

Read Entire File
String content =
    Files.readString(
        Path.of("message.txt")
    );


System.out.println(content);
Read All Lines
List<String> lines =
    Files.readAllLines(
        Path.of("courses.txt")
    );


for (String line : lines) {

    System.out.println(line);

}
Large Files
  • 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

Append Text
Files.writeString(
    Path.of("log.txt"),
    "Application started"
        + System.lineSeparator(),
    StandardOpenOption.CREATE,
    StandardOpenOption.APPEND
);
Common Open Options
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 reading

Copying Files

Copy File
Files.copy(
    Path.of("source.txt"),
    Path.of("backup.txt")
);
Replace Existing File
Files.copy(
    Path.of("source.txt"),
    Path.of("backup.txt"),
    StandardCopyOption.REPLACE_EXISTING
);

Moving and Renaming Files

Move File
Files.move(
    Path.of("source.txt"),
    Path.of(
        "backup",
        "source.txt"
    )
);
Rename File
Files.move(
    Path.of("old.txt"),
    Path.of("new.txt")
);

Deleting with Files

delete()
Files.delete(
    Path.of("data.txt")
);
deleteIfExists()
boolean deleted =
    Files.deleteIfExists(
        Path.of("data.txt")
    );
Difference
delete()

Throws exception if path
does not exist



deleteIfExists()

Returns false if path
does not exist

Checking File Properties

Property Checks
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 Stream Hierarchy
BYTE STREAMS

├── InputStream
│
│   ├── FileInputStream
│   ├── BufferedInputStream
│   ├── DataInputStream
│   └── ObjectInputStream
│
└── OutputStream

    ├── FileOutputStream
    ├── BufferedOutputStream
    ├── DataOutputStream
    └── ObjectOutputStream

InputStream

InputStream is the abstract base class for reading byte data.

Input Flow
FILE

  │
  │ bytes
  ▼

InputStream

  │
  ▼

JAVA PROGRAM

OutputStream

OutputStream is the abstract base class for writing byte data.

Output Flow
JAVA PROGRAM

  │
  │ bytes
  ▼

OutputStream

  │
  ▼

FILE

FileInputStream

Read Bytes
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

Write Bytes
try (
    FileOutputStream output =
        new FileOutputStream(
            "data.bin"
        )
) {

    output.write(65);

    output.write(66);

    output.write(67);

}

Copying Binary Files

Binary File Copy
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
        );

    }

}
Use a Buffer
  • 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 Stream Hierarchy
CHARACTER STREAMS

├── Reader
│
│   ├── FileReader
│   ├── BufferedReader
│   └── InputStreamReader
│
└── Writer

    ├── FileWriter
    ├── BufferedWriter
    ├── OutputStreamWriter
    └── PrintWriter

Reader

Reader is the abstract base class for character input.

Reader Flow
TEXT FILE

    │
    ▼

Reader

    │
    ▼

Characters

    │
    ▼

JAVA PROGRAM

Writer

Writer is the abstract base class for character output.

Writer Flow
JAVA PROGRAM

    │
    ▼

Characters

    │
    ▼

Writer

    │
    ▼

TEXT FILE

FileReader

Read Characters
try (
    FileReader reader =
        new FileReader(
            "message.txt"
        )
) {

    int character;


    while (
        (character =
            reader.read()) != -1
    ) {

        System.out.print(
            (char) character
        );

    }

}

FileWriter

Write Text
try (
    FileWriter writer =
        new FileWriter(
            "message.txt"
        )
) {

    writer.write(
        "Hello, Java!"
    );

}
Append Mode
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 and With Buffer
WITHOUT BUFFER

Program

  │
  ├── Read
  ├── Read
  ├── Read
  └── Read

  │
  ▼

File System



WITH BUFFER

Program

  │
  ▼

Buffer

  │
  ▼

Larger I/O Operations

  │
  ▼

File System

BufferedInputStream

Buffered Binary Input
try (
    BufferedInputStream input =
        new BufferedInputStream(
            new FileInputStream(
                "photo.jpg"
            )
        )
) {

    byte[] buffer =
        new byte[8192];


    while (
        input.read(buffer) != -1
    ) {

        // Process bytes

    }

}

BufferedOutputStream

Buffered Binary Output
try (
    BufferedOutputStream output =
        new BufferedOutputStream(
            new FileOutputStream(
                "data.bin"
            )
        )
) {

    output.write(
        new byte[] {
            10,
            20,
            30
        }
    );

}

BufferedReader

Read Line by Line
try (
    BufferedReader reader =
        Files.newBufferedReader(
            Path.of("data.txt")
        )
) {

    String line;


    while (
        (line =
            reader.readLine()) != null
    ) {

        System.out.println(line);

    }

}

BufferedWriter

Write Buffered Text
try (
    BufferedWriter writer =
        Files.newBufferedWriter(
            Path.of("data.txt")
        )
) {

    writer.write("Java");

    writer.newLine();

    writer.write("Spring");

}

PrintWriter provides convenient methods for writing formatted text.

Formatted Output
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

Read with Scanner
try (
    Scanner scanner =
        new Scanner(
            Path.of("data.txt")
        )
) {

    while (
        scanner.hasNextLine()
    ) {

        String line =
            scanner.nextLine();


        System.out.println(line);

    }

}
Scanner Use Case
  • 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.

Data Stream Types
DataOutputStream

Writes:

int
double
boolean
char
UTF String



DataInputStream

Reads:

int
double
boolean
char
UTF String

DataOutputStream

Write Primitive Data
try (
    DataOutputStream output =
        new DataOutputStream(
            new FileOutputStream(
                "student.dat"
            )
        )
) {

    output.writeInt(101);

    output.writeUTF(
        "Amit"
    );

    output.writeDouble(
        85.5
    );

    output.writeBoolean(
        true
    );

}

DataInputStream

Read Primitive Data
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
    );

}
Read in the Same Order
  • 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.

Object Stream Flow
JAVA OBJECT

    │
    ▼

ObjectOutputStream

    │
    ▼

FILE



FILE

    │
    ▼

ObjectInputStream

    │
    ▼

JAVA OBJECT

Serialization

Serialization is the process of converting an object state into a byte stream that can be stored or transferred.

Serialization
OBJECT

Student {
    id = 101
    name = "Amit"
}

        │
        ▼

SERIALIZATION

        │
        ▼

BYTE STREAM

        │
        ▼

student.ser

Serializable Interface

Student.java
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

Serialize Object
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.

Deserialization
student.ser

    │
    ▼

BYTE STREAM

    │
    ▼

DESERIALIZATION

    │
    ▼

Student Object

ObjectInputStream

Deserialize Object
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.

Version Identifier
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.

Transient Field
public class User
        implements Serializable {

    private String username;

    private transient String password;

}
Sensitive Data
  • 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.

Random Access
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);

}
File Positions
POSITION 0

100


POSITION 4

200


POSITION 8

300

File Attributes

Basic 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

DirectoryStream
Path directory =
    Path.of("data");


try (
    DirectoryStream<Path> stream =
        Files.newDirectoryStream(
            directory
        )
) {

    for (Path path : stream) {

        System.out.println(
            path.getFileName()
        );

    }

}
Filter Text Files
try (
    DirectoryStream<Path> stream =
        Files.newDirectoryStream(
            directory,
            "*.txt"
        )
) {

    for (Path path : stream) {

        System.out.println(path);

    }

}

Walking Directory Trees

Files.walk()
try (
    Stream<Path> paths =
        Files.walk(
            Path.of("data")
        )
) {

    paths.forEach(
        System.out::println
    );

}
Directory Tree
data

├── users
│   ├── users.txt
│   └── profile.txt
│
├── products
│   └── products.txt
│
└── config.txt

Searching Files

Find Text 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

Create Temporary File
Path tempFile =
    Files.createTempFile(
        "report-",
        ".txt"
    );


System.out.println(tempFile);
Create Temporary Directory
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.

Automatic Closing
try (
    BufferedReader reader =
        Files.newBufferedReader(
            Path.of("data.txt")
        )
) {

    System.out.println(
        reader.readLine()
    );

}
Resource Lifecycle
RESOURCE CREATED

        │
        ▼

TRY BLOCK EXECUTES

        │
        ├── Success
        │
        └── Exception

        │
        ▼

RESOURCE CLOSED AUTOMATICALLY

Character Encoding

Text files store characters as bytes according to a character encoding. Using an explicit encoding avoids platform-dependent behavior.

Write UTF-8
Files.writeString(
    Path.of("message.txt"),
    "Hello, Java!",
    StandardCharsets.UTF_8
);
Read UTF-8
String content =
    Files.readString(
        Path.of("message.txt"),
        StandardCharsets.UTF_8
    );
Encoding Best Practice
  • 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

students.csv
id,name,marks

101,Amit,85

102,Neha,92

103,Rahul,78
Simple CSV Reading
try (
    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();

    }

}
Real CSV Files Are More Complex
  • 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

application.properties
app.name=PrograMinds
app.version=1.0
server.port=8080
Read Properties
Properties properties =
    new Properties();


try (
    InputStream input =
        Files.newInputStream(
            Path.of(
                "application.properties"
            )
        )
) {

    properties.load(input);

}


System.out.println(
    properties.getProperty(
        "app.name"
    )
);
Write Properties
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

FileLogger.java
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
            );

        }

    }

}
Main.java
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

Student.java
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;

    }

}
StudentFileService.java
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

FileCopyApplication.java
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

Common 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 Same

Best 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

Avoid These 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

Exercise 1: Create a File
  • Create a Path for notes.txt.
  • Create the file.
  • Check whether it exists.
  • Display its absolute path.
  • Handle an existing file correctly.
Exercise 2: Write and Read Text
  • 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.
Exercise 3: Append Log Entries
  • Create application.log.
  • Append the current date and time.
  • Append an event message.
  • Run the program multiple times.
  • Verify that previous content remains.
Exercise 4: Binary File Copy
  • Choose an image file.
  • Read it using FileInputStream.
  • Write it using FileOutputStream.
  • Use an 8192-byte buffer.
  • Verify that the copied image opens correctly.
Exercise 5: Directory Explorer
  • Accept a directory path.
  • Verify that it exists.
  • List all child paths.
  • Identify files and directories separately.
  • Display file sizes.
Exercise 6: Student CSV
  • 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.
Exercise 7: Object Serialization
  • Create a serializable Employee class.
  • Add serialVersionUID.
  • Serialize an Employee object.
  • Deserialize the object.
  • Display the restored values.
Exercise 8: File Search
  • 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.

Lesson 27 Completed
  • 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.
Next Lesson →

Collections Basics in Java