File Handling and I/O
File and Directory Operations
আপনি একটি 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
StandardCopyOptionchoose করতে
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_EXISTINGoverwrite behavior explicitly enable করে- Directory copy automatically recursive নয়
Files.move()move এবং rename উভয়ের জন্য ব্যবহার করা যায়ATOMIC_MOVEstronger 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