🚀 Unlock Your Path to Success with the Ultimate Everything Bundle! 🚀
Introducing the Everything Bundle — your one-stop solution to mastering Java, Spring Boot, SQL, acing interviews, and securing certifications!
Introduction
Java NIO (New I/O) provides an improved and more flexible I/O API compared to the traditional I/O (java.io). Here are the top 6 features related to Java NIO.
Top 6 Features
Files.lines
for reading lines from a file:
The Files.lines method in Java NIO is a convenient way to read all the lines from a file as a stream of strings. This method is especially useful for processing large files efficiently since it processes the lines lazily and in a memory-efficient manner.
Example code:
private static void feature1(String filePath){
Path path = Paths.get(filePath);
try (var lines = Files.lines(path)) {
lines.forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
One of the benefits of using the above method is Lazy evaluation, which means it only processes the line as they needed.
2. Files.walk
for recursively walking a directory tree:
This method in Java NIO is used to recursively walk a directory tree, providing a stream of path
objects that represent the files and directories encountered. This is useful for performing operations on each file or directory within a given root directory and its subdirectories.
Example Code:
private static void feature2(){
Path path = Paths.get("/Users/surajmishra/workspace/Projects/");
try (var lines = Files.walk(path)) {
lines.forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
One of the benefits of using this method is that it recursively walks through the entire directory tree, including subdirectories.
3. Files.newBufferedWriter
and Files.newBufferedReader
for efficient I/O operations:
Using Files.newBufferedWriter
and Files.newBufferedReader
from the java.nio.file
package is a convenient way to handle efficient I/O operations with text files.
These methods provide efficient buffered streams for reading and writing, which can significantly improve performance compared to unbuffered I/O operations.
Example Code:
private static void feature3(){
Path path1 = Paths.get("/Users/surajmishra/workspace/Projects/JavaPlayGround/names.txt");
// Reading from the file
try (BufferedReader reader = Files.newBufferedReader(path1, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// Writing to the file
Path path2 = Paths.get("/Users/surajmishra/workspace/Projects/JavaPlayGround/names1.txt");
try (BufferedWriter writer = Files.newBufferedWriter(path2, StandardCharsets.UTF_8)) {
writer.write("Hello, world!");
writer.newLine();
writer.write("This is a test.");
} catch (IOException e) {
e.printStackTrace();
}
}
Buffering reduces the number of I/O operations by combining multiple small reads or writes into fewer, larger operations. This can significantly improve performance, especially for larger files.
4. FileChannel.transerTo
and FileChannel.transferFrom
for efficient file transfer:
Using FileChannel.transferTo
and FileChannel.transferFrom
for file transfers is a highly efficient way to copy data between files. These methods leverage the underlying operating system's capabilities to transfer data directly between channels, reducing the overhead of transferring data through the Java application layer.
Example Code:
private static void feature4(){
try (FileChannel sourceChannel = new FileInputStream("/Users/surajmishra/workspace/Projects/JavaPlayGround/names.txt").getChannel();
FileChannel targetChannel = new FileOutputStream("/Users/surajmishra/workspace/Projects/JavaPlayGround/target.txt").getChannel()) {
// Transfer data from sourceChannel to targetChannel
sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
These methods can be more efficient than manually reading from the source file and writing to the target file, as they use the underlying operating system’s capabilities to transfer data directly between channels.
🚀 DSA for the Cracking the Coding Interview. Animated Examples for Faster Learning and Deeper Understanding. 🚀
Java Data Structures & Algorithms + LEETCODE Exercises
Bestseller | Rating: 4.7 ⭐/5 (5k+ ratings) | 36k+ students
5: Asynchronous File I/O with AsynchronousFileChannel
:
Asynchronous file I/O using AsynchronousFileChannel
in Java allows for non-blocking operations, which can improve performance and responsiveness in applications that perform extensive file I/O operations. This is particularly useful for applications that need to handle multiple I/O tasks concurrently without being blocked.
Example Code:
private static void feature5(){
try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(Paths.get("names.txt"), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> result = fileChannel.read(buffer, 0);
// Perform other tasks while reading file
// Wait for the read operation to complete and get the result
int bytesRead = result.get();
System.out.println("Bytes read: " + bytesRead);
// Print the content read
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Asynchronous operations do not block the calling thread, allowing the application to continue performing other tasks while the I/O operation is in progress.
6: File and Directory Watchers:
Using a WatchService
in Java allows you to monitor directories for changes, such as file creation, modification, or deletion. This can be particularly useful for applications that need to react to file system events in real time.
Example Code:
private static void feature6(){
Path directory = Paths.get("/Users/surajmishra/workspace/Projects/JavaPlayGround/");
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
while (true){
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path fileName = ev.context();
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("New file created: " + fileName);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("File deleted: " + fileName);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("File modified: " + fileName);
}
}
// Reset the key -- this step is critical if you want to receive further watch events.
boolean valid = key.reset();
if(!valid){
break;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In the above logic, we are continuously watching for events, iterating through the event, and handling the event accordingly.
Conclusion
Overall, the Java NIO library offers a rich set of features for handling I/O operations efficiently and effectively in Java applications. By leveraging these features, developers can build high-performance, scalable, and robust applications that meet the demands of modern software development.
Subscribe to the Java newsletter for Java/Spring-related content.