File Handling and I/O

File and Directory Operations

ReadingPreview

আপনি একটি free preview lesson দেখছেন।

Lesson Overview

এখন পর্যন্ত আমরা file path, reading, writing, streams, এবং resource management শিখেছি।

এই lesson-এ আমরা file system-এর structural operations শিখব:

Create
Copy
Move
Rename
Delete
List
Walk recursively

Java-এর Files class এসব operations-এর জন্য rich API provide করে।

Common methods:

Files.createFile()
Files.createDirectory()
Files.createDirectories()
Files.copy()
Files.move()
Files.delete()
Files.deleteIfExists()
Files.list()
Files.walk()

এই lesson-এ focus থাকবে শুধু syntax-এর ওপর নয়, বরং:

  • Existing target কীভাবে handle করতে হয়
  • Directory delete কেন fail করতে পারে
  • Recursive traversal কীভাবে safely করতে হয়
  • Race conditions কেন pre-check দিয়ে পুরোপুরি solve হয় না
  • Cleanup operations কীভাবে design করা উচিত

Learning Objectives

এই lesson শেষে আপনি পারবেন:

  • File create করতে
  • Single directory এবং nested directories create করতে
  • File copy করতে
  • Move এবং rename করতে
  • Existing destination handle করতে
  • File safely delete করতে
  • Directory contents list করতে
  • Recursive file traversal করতে
  • Recursive directory deletion design করতে
  • File-system race conditions explain করতে
  • Appropriate StandardCopyOption choose করতে

Creating a File

A new empty file create করতে:

Files.createFile(
        path
);

Example:

Path path =
        Path.of(
                "content",
                "empty.md"
        );

Files.createFile(
        path
);

Parent Directory Must Exist

Suppose target:

content/java/module-1/lesson.md

If:

content/java/module-1

does not exist, this:

Files.createFile(
        path
);

will fail।

Create parent directories first:

Path parent =
        path.getParent();

if (parent != null) {
    Files.createDirectories(
            parent
    );
}

Files.createFile(
        path
);

Existing File

If the target already exists:

Files.createFile(
        path
);

throws:

FileAlreadyExistsException

This is useful when:

Creating a duplicate file must fail

Create File vs Write File

If you plan to immediately write content, this:

Files.createFile(
        path
);

Files.writeString(
        path,
        content
);

may be unnecessary।

You can simply use:

Files.writeString(
        path,
        content,
        StandardCharsets.UTF_8,
        StandardOpenOption.CREATE_NEW
);

Choose the simplest operation matching the contract।


Creating a Directory

Create one directory:

Files.createDirectory(
        path
);

Example:

Files.createDirectory(
        Path.of(
                "courses"
        )
);

createDirectory() Requires Existing Parent

Given:

content/java/module-1

If:

content/java

does not exist:

Files.createDirectory(
        Path.of(
                "content",
                "java",
                "module-1"
        )
);

fails।


Creating Nested Directories

Use:

Files.createDirectories(
        path
);

Example:

Files.createDirectories(
        Path.of(
                "content",
                "java",
                "module-1"
        )
);

Java creates all missing levels।


createDirectories() Is Idempotent-Like

If directory already exists:

Files.createDirectories(
        path
);

normally succeeds।

This makes it useful for:

Ensure storage directory exists

Create Directory Helper

public static Path ensureDirectory(
        Path path
) {
    if (path == null) {
        throw new IllegalArgumentException(
                "Directory path is required."
        );
    }

    try {
        return Files.createDirectories(
                path
        );
    } catch (
        IOException exception
    ) {
        throw new IllegalStateException(
                "Could not create directory.",
                exception
        );
    }
}

Copying Files

Use:

Files.copy(
        source,
        target
);

Example:

Path source =
        Path.of(
                "content",
                "java.md"
        );

Path target =
        Path.of(
                "backup",
                "java.md"
        );

Files.copy(
        source,
        target
);

What If Target Exists?

By default:

Files.copy(
        source,
        target
);

fails if target already exists।

Usually:

FileAlreadyExistsException

Replace Existing Target

Use:

StandardCopyOption.REPLACE_EXISTING

Example:

Files.copy(
        source,
        target,
        StandardCopyOption.REPLACE_EXISTING
);

This means:

Copy source
Replace target if it exists

Copying Does Not Automatically Create Parent Directories

If:

backup/

does not exist, copy may fail।

Prepare parent:

Path parent =
        target.getParent();

if (parent != null) {
    Files.createDirectories(
            parent
    );
}

Then copy।


Copy Helper

public static void copyFile(
        Path source,
        Path target
) {
    if (
            source == null
            || target == null
    ) {
        throw new IllegalArgumentException(
                "Source and target are required."
        );
    }

    try {
        Path parent =
                target.getParent();

        if (parent != null) {
            Files.createDirectories(
                    parent
            );
        }

        Files.copy(
                source,
                target,
                StandardCopyOption.REPLACE_EXISTING
        );
    } catch (
        IOException exception
    ) {
        throw new IllegalStateException(
                "Could not copy file.",
                exception
        );
    }
}

Copying a Directory Is Not Recursive by Default

Suppose:

course/
├── lesson-1.md
└── lesson-2.md

This:

Files.copy(
        sourceDirectory,
        targetDirectory
);

does not automatically copy the entire directory tree।

Recursive copy requires traversal।

We will discuss recursive operations later in this lesson।


Moving Files

Use:

Files.move(
        source,
        target
);

Example:

Files.move(
        Path.of(
                "draft.md"
        ),
        Path.of(
                "published.md"
        )
);

This changes the file's location।


Rename Is Usually a Move

File-system APIs usually treat rename as a move within the same directory।

Example:

Path source =
        Path.of(
                "course-old.md"
        );

Path target =
        Path.of(
                "course-new.md"
        );

Files.move(
        source,
        target
);

Conceptually:

Rename file

Move to Another Directory

Files.move(
        Path.of(
                "drafts",
                "course.md"
        ),
        Path.of(
                "published",
                "course.md"
        )
);

Again, ensure target parent exists if required।


Replacing During Move

Files.move(
        source,
        target,
        StandardCopyOption.REPLACE_EXISTING
);

Existing target may be replaced।

Use this deliberately।


Atomic Move

Files.move(
        source,
        target,
        StandardCopyOption.ATOMIC_MOVE
);

This requests an atomic move।

Meaning conceptually:

Observers see either old state or new state

rather than an intermediate move state।


ATOMIC_MOVE Is Not Guaranteed

Some file systems do not support it।

It may throw:

AtomicMoveNotSupportedException

Also, moving across different file systems may not support atomic behavior।


Move With Fallback

try {
    Files.move(
            source,
            target,
            StandardCopyOption.REPLACE_EXISTING,
            StandardCopyOption.ATOMIC_MOVE
    );
} catch (
        AtomicMoveNotSupportedException exception
) {
    Files.move(
            source,
            target,
            StandardCopyOption.REPLACE_EXISTING
    );
}

Use this only when non-atomic fallback is acceptable।


Copy vs Move

Copy

Source remains
Target is created

Move

Source location disappears
Target becomes the new location

Choose according to business semantics।


Deleting a File

Use:

Files.delete(
        path
);

If target does not exist:

NoSuchFileException

may be thrown।


deleteIfExists()

Use:

Files.deleteIfExists(
        path
);

Returns:

true  → File existed and was deleted
false → File did not exist

Example:

boolean deleted =
        Files.deleteIfExists(
                path
        );

delete() vs deleteIfExists()

Use:

delete()

when absence itself should be treated as a failure।

Use:

deleteIfExists()

when cleanup semantics are:

Ensure it is gone

Idempotent Cleanup

A cleanup operation often benefits from:

Files.deleteIfExists(
        temp
);

Calling it repeatedly is safe in the sense that:

Already gone

is not an error।


Deleting a Non-Empty Directory

Suppose:

course/
├── lesson-1.md
└── lesson-2.md

This:

Files.delete(
        Path.of(
                "course"
        )
);

fails because directory is not empty।

Usually with:

DirectoryNotEmptyException

Empty Directory Deletion

If directory has no children:

Files.delete(
        directory
);

can delete it।


Listing a Directory

Use:

Files.list(
        directory
);

It returns:

Stream<Path>

Example:

try (
        Stream<Path> entries =
                Files.list(
                        directory
                )
) {
    entries.forEach(
            System.out::println
    );
}

Remember:

Files.list()

must be closed।


List Only Files

try (
        Stream<Path> entries =
                Files.list(
                        directory
                )
) {
    entries.filter(
            Files::isRegularFile
    ).forEach(
            System.out::println
    );
}

List Only Directories

try (
        Stream<Path> entries =
                Files.list(
                        directory
                )
) {
    entries.filter(
            Files::isDirectory
    ).forEach(
            System.out::println
    );
}

Files.list() Is One Level Deep

Given:

content/
├── java/
│   └── lesson.md
└── backend/
    └── api.md

Files.list(content) returns:

java
backend

It does not recursively return:

lesson.md
api.md

Recursive Traversal with Files.walk()

Use:

Files.walk(
        path
);

It returns a recursive:

Stream<Path>

Example:

try (
        Stream<Path> paths =
                Files.walk(
                        root
                )
) {
    paths.forEach(
            System.out::println
    );
}

Example Tree

Directory:

content/
├── java/
│   ├── oop.md
│   └── collections.md
└── backend/
    └── api.md

Files.walk(content) may produce:

content
content/java
content/java/oop.md
content/java/collections.md
content/backend
content/backend/api.md

The root itself is included।


Filter Recursive Files

try (
        Stream<Path> paths =
                Files.walk(
                        root
                )
) {
    paths.filter(
            Files::isRegularFile
    ).forEach(
            System.out::println
    );
}

Find Markdown Files

try (
        Stream<Path> paths =
                Files.walk(
                        root
                )
) {
    paths.filter(
            Files::isRegularFile
    )
    .filter(
            path ->
                    path.getFileName()
                            .toString()
                            .endsWith(
                                    ".md"
                            )
    )
    .forEach(
            System.out::println
    );
}

Files.walk() Must Also Be Closed

Like:

Files.list()

the returned stream may hold directory resources।

Use try-with-resources।


Limit Walk Depth

You can specify max depth:

Files.walk(
        root,
        2
);

Depth concept:

0 → root
1 → direct children
2 → grandchildren

Useful when full recursion is unnecessary।


Count Files Recursively

public static long countFiles(
        Path root
) {
    try (
            Stream<Path> paths =
                    Files.walk(
                            root
                    )
    ) {
        return paths.filter(
                Files::isRegularFile
        ).count();
    } catch (
            IOException exception
    ) {
        throw new IllegalStateException(
                "Could not inspect directory tree.",
                exception
        );
    }
}

Recursive Directory Deletion

To delete:

course/
├── module-1/
│   └── lesson.md
└── module-2/
    └── lesson.md

you must delete children before parent directories।

Incorrect order:

course
module-1
lesson.md

Parent deletion fails while children still exist।

Correct order:

lesson.md
module-1
...
course

Sort Deepest Paths First

A common simple approach:

try (
        Stream<Path> paths =
                Files.walk(
                        root
                )
) {
    paths.sorted(
            Comparator.reverseOrder()
    ).forEach(
            path -> {
                try {
                    Files.delete(
                            path
                    );
                } catch (
                        IOException exception
                ) {
                    throw new UncheckedIOException(
                            exception
                    );
                }
            }
    );
}

Because child paths sort before their parents in reverse order for typical path traversal usage।


Recursive Delete Helper

public static void deleteRecursively(
        Path root
) {
    if (root == null) {
        throw new IllegalArgumentException(
                "Root path is required."
        );
    }

    if (
            Files.notExists(
                    root
            )
    ) {
        return;
    }

    try (
            Stream<Path> paths =
                    Files.walk(
                            root
                    )
    ) {
        paths.sorted(
                Comparator.reverseOrder()
        ).forEach(
                path -> {
                    try {
                        Files.deleteIfExists(
                                path
                        );
                    } catch (
                            IOException exception
                    ) {
                        throw new UncheckedIOException(
                                exception
                        );
                    }
                }
        );
    } catch (
        UncheckedIOException exception
    ) {
        throw new IllegalStateException(
                "Could not delete directory tree.",
                exception.getCause()
        );
    } catch (
        IOException exception
    ) {
        throw new IllegalStateException(
                "Could not inspect directory tree.",
                exception
        );
    }
}

Why UncheckedIOException?

Stream lambda cannot conveniently throw a checked:

IOException

directly।

So we wrap it:

UncheckedIOException

then translate it outside।


Be Careful with Recursive Delete

Recursive delete is dangerous।

Never run it on a path that has not been carefully validated।

Weak:

deleteRecursively(
        Path.of(
                userInput
        )
);

Potential disaster:

User provides wrong path
Application deletes unintended files

Validate Destructive Paths

For a managed storage root:

Path storageRoot =
        Path.of(
                "/data/liveklass"
        )
        .toAbsolutePath()
        .normalize();

Resolve target:

Path target =
        storageRoot.resolve(
                courseCode
        )
        .normalize();

Then verify:

if (
        !target.startsWith(
                storageRoot
        )
) {
    throw new IllegalArgumentException(
            "Target path escapes storage root."
    );
}

For destructive actions, extra safeguards are worthwhile।


Never Delete the Storage Root Accidentally

Possible additional rule:

if (
        target.equals(
                storageRoot
        )
) {
    throw new IllegalArgumentException(
            "Storage root cannot be deleted."
    );
}

This protects against accidentally resolving an empty identifier to the base directory।


Recursive Copy

A directory tree can be copied by walking the source.

Concept:

For every source path:
Determine relative path
Resolve under target root
Create directory or copy file

Example:

public static void copyDirectory(
        Path sourceRoot,
        Path targetRoot
) {
    try (
            Stream<Path> paths =
                    Files.walk(
                            sourceRoot
                    )
    ) {
        paths.forEach(
                source -> {
                    Path relative =
                            sourceRoot.relativize(
                                    source
                            );

                    Path target =
                            targetRoot.resolve(
                                    relative
                            );

                    try {
                        if (
                                Files.isDirectory(
                                        source
                                )
                        ) {
                            Files.createDirectories(
                                    target
                            );
                        } else {
                            Files.copy(
                                    source,
                                    target,
                                    StandardCopyOption.REPLACE_EXISTING
                            );
                        }
                    } catch (
                            IOException exception
                    ) {
                        throw new UncheckedIOException(
                                exception
                        );
                    }
                }
        );
    } catch (
        UncheckedIOException exception
    ) {
        throw new IllegalStateException(
                "Could not copy directory.",
                exception.getCause()
        );
    } catch (
        IOException exception
    ) {
        throw new IllegalStateException(
                "Could not inspect source directory.",
                exception
        );
    }
}

relativize() in Recursive Copy

Suppose:

sourceRoot = /data/course
source     = /data/course/module-1/lesson.md

Then:

sourceRoot.relativize(
        source
);

returns:

module-1/lesson.md

Resolve under target:

/backup/course/module-1/lesson.md

This preserves the directory structure।


File-System Race Conditions

Suppose:

if (
        Files.exists(
                path
        )
) {
    Files.delete(
            path
    );
}

Between:

exists()

and:

delete()

another process may delete the file।

This is called:

TOCTOU

Time Of Check To Time Of Use।


Prefer Atomic-Like Operations When Available

Instead of:

if (
        Files.exists(
                path
        )
) {
    Files.delete(
            path
    );
}

use:

Files.deleteIfExists(
        path
);

It combines the intent into one operation।


Check-Then-Create Race

Weak:

if (
        Files.notExists(
                path
        )
) {
    Files.createFile(
            path
    );
}

Another process can create the file between check and creation।

Better:

try {
    Files.createFile(
            path
    );
} catch (
        FileAlreadyExistsException exception
) {
    // Handle duplicate
}

Let the operation enforce the final state।


Check-Then-Copy Race

Weak:

if (
        Files.notExists(
                target
        )
) {
    Files.copy(
            source,
            target
    );
}

Another process can create target after the check।

If duplicate target must fail, simply call:

Files.copy(
        source,
        target
);

and handle:

FileAlreadyExistsException

Pre-Checks Still Have Uses

Checks are useful for:

  • Better user feedback
  • Early validation
  • Avoiding unnecessary work

But they are not synchronization mechanisms।

The actual file-system operation remains authoritative।


Symbolic Links

A symbolic link is a file-system entry pointing to another path।

Example conceptually:

current-course -> /data/courses/java-oop

Methods such as:

Files.isSymbolicLink(
        path
);

can identify one।


Why Symbolic Links Matter

Path validation based only on lexical:

normalize()

may not fully capture where symbolic links actually lead।

For security-sensitive storage, consider:

toRealPath()

and clear symbolic-link policies।

Detailed secure file-system sandboxing is beyond this lesson, but you should know symbolic links affect real path semantics।


File Attributes During Copy

A simple:

Files.copy(
        source,
        target
);

copies content।

If you also want supported file attributes:

StandardCopyOption.COPY_ATTRIBUTES

Example:

Files.copy(
        source,
        target,
        StandardCopyOption.COPY_ATTRIBUTES
);

Exact supported attributes depend on the file system।


Common StandardCopyOptions

REPLACE_EXISTING
COPY_ATTRIBUTES
ATOMIC_MOVE

Important:

ATOMIC_MOVE

is relevant to move().

It is not a general copy option for making copy() atomic।


Course Storage Example

Suppose each course has:

content/
└── java-oop/
    ├── introduction.md
    ├── oop.md
    └── collections.md

A storage service may need:

Create course directory
Copy lesson template
Rename content
Delete course directory
List lessons

These should remain storage operations, separate from business rules such as:

Can this course be deleted?
Is this instructor allowed?
Is the course published?

Complete Example: Course File Manager

package io.liveklass.content;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;

public final class CourseFileManager {

    private final Path baseDirectory;

    public CourseFileManager(
            Path baseDirectory
    ) {
        if (baseDirectory == null) {
            throw new IllegalArgumentException(
                    "Base directory is required."
            );
        }

        this.baseDirectory =
                baseDirectory
                        .toAbsolutePath()
                        .normalize();
    }

    public Path createCourseDirectory(
            String courseCode
    ) {
        Path directory =
                resolveCourseDirectory(
                        courseCode
                );

        try {
            return Files.createDirectories(
                    directory
            );
        } catch (
            IOException exception
        ) {
            throw storageFailure(
                    "Could not create course directory.",
                    exception
            );
        }
    }

    public void copyFile(
            String courseCode,
            Path source,
            String targetFileName
    ) {
        if (source == null) {
            throw new IllegalArgumentException(
                    "Source path is required."
            );
        }

        Path target =
                resolveCourseDirectory(
                        courseCode
                )
                .resolve(
                        targetFileName
                )
                .normalize();

        validateInsideBase(
                target
        );

        try {
            Files.createDirectories(
                    target.getParent()
            );

            Files.copy(
                    source,
                    target,
                    StandardCopyOption.REPLACE_EXISTING
            );
        } catch (
            IOException exception
        ) {
            throw storageFailure(
                    "Could not copy course file.",
                    exception
            );
        }
    }

    public void renameFile(
            String courseCode,
            String oldName,
            String newName
    ) {
        Path courseDirectory =
                resolveCourseDirectory(
                        courseCode
                );

        Path source =
                courseDirectory.resolve(
                        oldName
                )
                .normalize();

        Path target =
                courseDirectory.resolve(
                        newName
                )
                .normalize();

        validateInsideBase(
                source
        );

        validateInsideBase(
                target
        );

        try {
            Files.move(
                    source,
                    target
            );
        } catch (
            IOException exception
        ) {
            throw storageFailure(
                    "Could not rename course file.",
                    exception
            );
        }
    }

    public List<Path> listFiles(
            String courseCode
    ) {
        Path directory =
                resolveCourseDirectory(
                        courseCode
                );

        try (
                Stream<Path> paths =
                        Files.list(
                                directory
                        )
        ) {
            return paths.filter(
                    Files::isRegularFile
            ).toList();
        } catch (
            IOException exception
        ) {
            throw storageFailure(
                    "Could not list course files.",
                    exception
            );
        }
    }

    public void deleteCourseDirectory(
            String courseCode
    ) {
        Path directory =
                resolveCourseDirectory(
                        courseCode
                );

        if (
                Files.notExists(
                        directory
                )
        ) {
            return;
        }

        try (
                Stream<Path> paths =
                        Files.walk(
                                directory
                        )
        ) {
            paths.sorted(
                    Comparator.reverseOrder()
            ).forEach(
                    path -> {
                        try {
                            Files.deleteIfExists(
                                    path
                            );
                        } catch (
                            IOException exception
                        ) {
                            throw new UncheckedIOException(
                                    exception
                            );
                        }
                    }
            );
        } catch (
            UncheckedIOException exception
        ) {
            throw storageFailure(
                    "Could not delete course directory.",
                    exception.getCause()
            );
        } catch (
            IOException exception
        ) {
            throw storageFailure(
                    "Could not inspect course directory.",
                    exception
            );
        }
    }

    private Path resolveCourseDirectory(
            String courseCode
    ) {
        if (
                courseCode == null
                || courseCode.isBlank()
        ) {
            throw new IllegalArgumentException(
                    "Course code is required."
            );
        }

        Path directory =
                baseDirectory.resolve(
                        courseCode
                                .strip()
                                .toLowerCase()
                )
                .normalize();

        validateInsideBase(
                directory
        );

        if (
                directory.equals(
                        baseDirectory
                )
        ) {
            throw new IllegalArgumentException(
                    "Course directory cannot be the storage root."
            );
        }

        return directory;
    }

    private void validateInsideBase(
            Path path
    ) {
        if (
                !path.startsWith(
                        baseDirectory
                )
        ) {
            throw new IllegalArgumentException(
                    "Path escapes the allowed storage directory."
            );
        }
    }

    private CourseContentStorageException storageFailure(
            String message,
            IOException cause
    ) {
        return new CourseContentStorageException(
                message,
                cause
        );
    }
}

Design Review

Why Normalize the Base Directory?

So every target can be compared against a stable canonical-looking base path।

Why Validate Every Derived Target?

A file name could contain:

../

and escape the course directory।

Why Use deleteIfExists() for Cleanup?

Deleting an already-removed path is acceptable during cleanup।

Why Reverse Sort Before Recursive Delete?

Children must be deleted before their parent directories।

Why Translate IOException?

Higher layers should depend on storage-level semantics, not low-level file API details।


Common Mistakes

Creating a File Before Ensuring Parent Directory Exists

Nested file creation can fail।


Assuming createDirectory() Creates Parents

It does not।


Copying Over Existing Files Accidentally

Use REPLACE_EXISTING only when overwrite is intentional।


Assuming Directory Copy Is Recursive

It is not।


Assuming Rename Has a Separate File API

Rename is generally a move।


Deleting Non-Empty Directory Directly

It fails until children are removed।


Forgetting to Close Files.list() or Files.walk()

Both return resource-backed streams।


Deleting Parent Before Children

Recursive delete fails।


Running Recursive Delete on Unvalidated Input

Can cause severe data loss।


Using exists() as Synchronization

Another thread or process can change state immediately afterward।


Doing Check-Then-Create

Use the create operation itself to enforce duplicate behavior।


Assuming ATOMIC_MOVE Is Always Available

Support is file-system dependent।


Practice Exercises

Exercise 1: Create a Course Directory

Implement:

Path createCourseDirectory(
        Path base,
        String courseCode
)

Use:

resolve()
normalize()
createDirectories()

Reject paths outside base


Exercise 2: Copy a File

Copy:

template.md

to:

content/java-oop/introduction.md

Create parent directories first।

Do not overwrite existing target।


Exercise 3: Rename a Lesson

Rename:

oop-old.md

to:

oop.md

using:

Files.move()

Exercise 4: Delete If Present

Implement:

boolean removeFile(
        Path path
)

using:

Files.deleteIfExists()

Exercise 5: List Markdown Files

List only direct .md files from a directory using:

Files.list()

Exercise 6: Recursive Markdown Search

Use:

Files.walk()

to find every .md file under a root directory।


Exercise 7: Recursive Delete

Delete a directory tree by:

  • Walking recursively
  • Sorting deepest entries first
  • Calling deleteIfExists()

Exercise 8: Explain the Race

Explain why this is unsafe as a guarantee:

if (
        Files.notExists(
                path
        )
) {
    Files.createFile(
            path
    );
}

Then rewrite it using exception handling around the actual create operation।


Predict the Result

Question 1

A file already exists:

Files.createFile(
        path
);

What happens?

Answer

Usually:

FileAlreadyExistsException

Question 2

Does:

Files.createDirectories(
        existingDirectory
);

normally fail just because the directory already exists?

Answer

No।


Question 3

Does:

Files.copy(
        directory,
        target
);

recursively copy every nested file?

Answer

No।


Question 4

Can a non-empty directory normally be removed with one:

Files.delete(
        directory
);

call?

Answer

No।

Its children must be removed first।


Question 5

What should close a stream returned by:

Files.walk()

?

Answer

The code that opened it, usually via try-with-resources।


Knowledge Check

Question 1

What is the difference between createDirectory() and createDirectories()?

Question 2

What happens if createFile() targets an existing file?

Question 3

What does REPLACE_EXISTING mean?

Question 4

How is a file usually renamed in Java?

Question 5

What does deleteIfExists() return?

Question 6

Why can a non-empty directory not normally be deleted directly?

Question 7

What is the difference between Files.list() and Files.walk()?

Question 8

Why must Files.walk() be closed?

Question 9

Why should recursive delete process children before parents?

Question 10

What is a TOCTOU race?

Question 11

Why is Files.exists() not a synchronization mechanism?

Question 12

When should ATOMIC_MOVE be considered?

Question 13

Why validate paths before recursive deletion?

Question 14

What does relativize() help with during recursive copy?

Question 15

Why should storage operations remain separate from business rules?


Knowledge Check Answers

Answer 1

createDirectory() creates one directory and requires its parent; createDirectories() creates missing parent directories too।

Answer 2

It throws a duplicate-file failure such as FileAlreadyExistsException

Answer 3

The destination may be replaced if it already exists।

Answer 4

Using Files.move() to a new path or name।

Answer 5

true if an existing path was deleted, otherwise false

Answer 6

The file system requires its children to be removed first।

Answer 7

list() returns direct children; walk() recursively traverses a directory tree।

Answer 8

The stream may hold underlying directory resources।

Answer 9

A directory cannot be deleted while it still contains children।

Answer 10

A race where state changes between checking a condition and acting on it।

Answer 11

Other threads or processes can change the file system immediately after the check।

Answer 12

When a move should appear as one indivisible replacement and the file system supports it।

Answer 13

A wrong or malicious path could delete data outside the intended storage area।

Answer 14

It converts a source path into a relative path that can be reproduced under the destination root।

Answer 15

Storage manages file-system mechanics; domain and application layers manage business decisions and permissions।


Lesson Summary

এই lesson-এ আমরা শিখেছি:

  • Files.createFile() new empty file তৈরি করে
  • Existing target হলে strict creation fail করতে পারে
  • createDirectory() one directory তৈরি করে
  • createDirectories() missing parent hierarchy create করে
  • Files.copy() file copy করতে পারে
  • REPLACE_EXISTING overwrite behavior explicitly enable করে
  • Directory copy automatically recursive নয়
  • Files.move() move এবং rename উভয়ের জন্য ব্যবহার করা যায়
  • ATOMIC_MOVE stronger move semantics request করতে পারে
  • Atomic move support guaranteed নয়
  • Files.delete() missing fileকে failure হিসেবে treat করতে পারে
  • deleteIfExists() cleanup-friendly semantics দেয়
  • Non-empty directory delete করতে children আগে remove করতে হয়
  • Files.list() direct children দেয়
  • Files.walk() recursive traversal দেয়
  • Both resource-backed streams try-with-resources দিয়ে close করা উচিত
  • Recursive deletion deepest paths first process করা উচিত
  • Recursive destructive operations-এর আগে path validation essential
  • relativize() recursive copy structure preserve করতে সাহায্য করে
  • File-system check-then-act operations race conditions-এর subject
  • Actual file operation final authority
  • Pre-checks synchronization replace করে না
  • Storage mechanics এবং business rules আলাদা রাখা cleaner design দেয়

Next Lesson

পরবর্তী lesson:

Designing a File-Based Repository

আমরা শিখব:

  • Repository abstraction
  • Domain vs storage responsibilities
  • File naming strategy
  • Serialization format
  • Save and load operations
  • Missing entity handling
  • Storage exception translation
  • Atomic replacement
  • Listing stored entities
  • Repository limitations
  • When file storage is appropriate and when a database is better