google-site-verification: google61fe8ba583a51912.html
Java NIO (New I/O) in Java

Java NIO (New I/O) is an alternative I/O API introduced in Java 1.4 that provides high-speed, block-oriented I/O capabilities. It's designed to be more efficient than traditional Java I/O for certain use cases.

1. NIO vs Traditional I/O

Key Differences

Traditional I/OJava NIO
Stream-orientedBuffer-oriented
Blocking I/ONon-blocking I/O
SynchronousAsynchronous
One thread per connectionOne thread can handle multiple connections

2. Core Components of NIO

Basic Imports

import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SocketChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.DatagramChannel; import java.nio.file.*; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.io.IOException; import java.util.EnumSet;

3. Buffers

Buffers are the fundamental data structure in NIO used for reading and writing data.

Buffer Types

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

Buffer Properties

  • Capacity: Maximum number of elements buffer can hold
  • Position: Current position for reading/writing
  • Limit: First element that should not be read/written
  • Mark: Remembered position for reset

Basic Buffer Operations

public class BufferExample { public static void main(String[] args) { // Create a ByteBuffer with capacity of 1024 bytes ByteBuffer buffer = ByteBuffer.allocate(1024); System.out.println("Initial state:"); printBufferInfo(buffer); // Write data to buffer String data = "Hello, NIO!"; buffer.put(data.getBytes()); System.out.println("\nAfter putting data:"); printBufferInfo(buffer); // Flip buffer for reading buffer.flip(); System.out.println("\nAfter flip (ready for reading):"); printBufferInfo(buffer); // Read data from buffer byte[] readData = new byte[buffer.remaining()]; buffer.get(readData); System.out.println("Read data: " + new String(readData)); System.out.println("\nAfter reading:"); printBufferInfo(buffer); // Clear buffer for reuse buffer.clear(); System.out.println("\nAfter clear:"); printBufferInfo(buffer); } private static void printBufferInfo(ByteBuffer buffer) { System.out.println("Position: " + buffer.position()); System.out.println("Limit: " + buffer.limit()); System.out.println("Capacity: " + buffer.capacity()); System.out.println("Remaining: " + buffer.remaining()); } }

Direct vs Non-Direct Buffers

public class DirectBufferExample { public static void main(String[] args) { // Non-direct buffer (allocated in JVM heap) ByteBuffer heapBuffer = ByteBuffer.allocate(1024); System.out.println("Is direct: " + heapBuffer.isDirect()); // Direct buffer (allocated in OS memory) ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024); System.out.println("Is direct: " + directBuffer.isDirect()); // Direct buffers are faster for I/O operations but // more expensive to allocate and deallocate } }

Buffer View Operations

public class BufferViews { public static void main(String[] args) { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // Write some data for (int i = 0; i < 10; i++) { byteBuffer.put((byte) i); } byteBuffer.flip(); // Create read-only view ByteBuffer readOnlyBuffer = byteBuffer.asReadOnlyBuffer(); System.out.println("Read-only: " + readOnlyBuffer.isReadOnly()); // Create duplicate ByteBuffer duplicateBuffer = byteBuffer.duplicate(); // Create slice (shares portion of original buffer) byteBuffer.position(2); byteBuffer.limit(6); ByteBuffer sliceBuffer = byteBuffer.slice(); System.out.println("Original capacity: " + byteBuffer.capacity()); System.out.println("Slice capacity: " + sliceBuffer.capacity()); } }

4. Channels

Channels represent connections to entities capable of performing I/O operations.

FileChannel Examples

Reading from a File

public class FileChannelReadExample { public static void main(String[] args) { Path path = Paths.get("test.txt"); try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) { ByteBuffer buffer = ByteBuffer.allocate(1024); while (channel.read(buffer) > 0) { buffer.flip(); // Prepare buffer for reading // Process data while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } buffer.clear(); // Prepare buffer for next read } } catch (IOException e) { e.printStackTrace(); } } }

Writing to a File

public class FileChannelWriteExample { public static void main(String[] args) { Path path = Paths.get("output.txt"); String data = "Hello, Java NIO!\nThis is a test file."; try (FileChannel channel = FileChannel.open( path, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { ByteBuffer buffer = ByteBuffer.wrap(data.getBytes()); channel.write(buffer); System.out.println("Data written successfully"); } catch (IOException e) { e.printStackTrace(); } } }

File Copy with Transfer

public class FileCopyExample { public static void main(String[] args) { Path sourcePath = Paths.get("source.txt"); Path destPath = Paths.get("destination.txt"); try (FileChannel sourceChannel = FileChannel.open(sourcePath, StandardOpenOption.READ); FileChannel destChannel = FileChannel.open(destPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { // Efficient file transfer (uses OS-level zero-copy when possible) long position = 0; long size = sourceChannel.size(); while (position < size) { position += sourceChannel.transferTo(position, size - position, destChannel); } System.out.println("File copied successfully"); } catch (IOException e) { e.printStackTrace(); } } }

SocketChannel Examples

Non-blocking Client

public class NIOClient { public static void main(String[] args) { try (SocketChannel socketChannel = SocketChannel.open()) { // Configure non-blocking mode socketChannel.configureBlocking(false); // Connect to server socketChannel.connect(new InetSocketAddress("localhost", 8080)); // Wait for connection to complete while (!socketChannel.finishConnect()) { System.out.println("Still connecting..."); Thread.sleep(1000); } // Send data String message = "Hello from NIO Client!"; ByteBuffer buffer = ByteBuffer.wrap(message.getBytes()); while (buffer.hasRemaining()) { socketChannel.write(buffer); } System.out.println("Message sent to server"); // Read response buffer.clear(); StringBuilder response = new StringBuilder(); while (socketChannel.read(buffer) > 0) { buffer.flip(); while (buffer.hasRemaining()) { response.append((char) buffer.get()); } buffer.clear(); } System.out.println("Server response: " + response.toString()); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }

5. Selectors

Selectors allow a single thread to handle multiple channels.

Server with Selector

public class NIOServer { private Selector selector; private ServerSocketChannel serverChannel; public void start(int port) throws IOException { // Create selector selector = Selector.open(); // Create server socket channel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); serverChannel.bind(new InetSocketAddress(port)); // Register server channel with selector for accept events serverChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server started on port " + port); // Event loop while (true) { // Wait for events (blocks until at least one event occurs) int readyChannels = selector.select(); if (readyChannels == 0) continue; // Process selected keys Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { acceptConnection(key); } else if (key.isReadable()) { readData(key); } else if (key.isWritable()) { writeData(key); } keyIterator.remove(); } } } private void acceptConnection(SelectionKey key) throws IOException { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel clientChannel = serverChannel.accept(); clientChannel.configureBlocking(false); // Register client channel for read operations clientChannel.register(selector, SelectionKey.OP_READ); System.out.println("Client connected: " + clientChannel.getRemoteAddress()); } private void readData(SelectionKey key) throws IOException { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = clientChannel.read(buffer); if (bytesRead == -1) { // Connection closed clientChannel.close(); System.out.println("Client disconnected"); return; } if (bytesRead > 0) { buffer.flip(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); String message = new String(data); System.out.println("Received: " + message); // Echo back the message ByteBuffer response = ByteBuffer.wrap(("Echo: " + message).getBytes()); clientChannel.write(response); } } private void writeData(SelectionKey key) throws IOException { // Handle write operations SocketChannel clientChannel = (SocketChannel) key.channel(); // Write logic here } public static void main(String[] args) { try { new NIOServer().start(8080); } catch (IOException e) { e.printStackTrace(); } } }

6. java.nio.file Package

Path Operations

public class PathOperations { public static void main(String[] args) { // Creating Path objects Path path1 = Paths.get("/home/user/documents/file.txt"); Path path2 = Paths.get("C:", "Users", "John", "file.txt"); Path path3 = Paths.get("relative/path/file.txt"); // Path information System.out.println("File name: " + path1.getFileName()); System.out.println("Parent: " + path1.getParent()); System.out.println("Root: " + path1.getRoot()); System.out.println("Name count: " + path1.getNameCount()); // Path navigation Path subPath = path1.subpath(1, 3); System.out.println("Subpath: " + subPath); // Resolving paths Path base = Paths.get("/base"); Path relative = Paths.get("subdir/file.txt"); Path resolved = base.resolve(relative); System.out.println("Resolved: " + resolved); // Normalizing paths Path withDots = Paths.get("/home/./user/../john/file.txt"); Path normalized = withDots.normalize(); System.out.println("Normalized: " + normalized); } }

Files Utility Class

public class FilesExamples { public static void main(String[] args) throws IOException { Path source = Paths.get("source.txt"); Path target = Paths.get("target.txt"); Path directory = Paths.get("testdir"); // Create directory Files.createDirectories(directory); // Create file with content Files.writeString(source, "Hello, Java NIO Files!", StandardOpenOption.CREATE); // Copy file Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); // Read file content String content = Files.readString(target); System.out.println("File content: " + content); // File attributes System.out.println("Size: " + Files.size(target) + " bytes"); System.out.println("Last modified: " + Files.getLastModifiedTime(target)); System.out.println("Is readable: " + Files.isReadable(target)); // List directory contents Files.createDirectories(Paths.get("testdir/subdir")); Files.writeString(Paths.get("testdir/file1.txt"), "File 1"); Files.writeString(Paths.get("testdir/file2.txt"), "File 2"); try (Stream<Path> stream = Files.list(directory)) { stream.forEach(System.out::println); } // Walk directory tree System.out.println("\nWalking directory tree:"); Files.walk(directory) .forEach(System.out::println); } }

File Watching Service

public class FileWatcherExample { public static void main(String[] args) throws IOException, InterruptedException { WatchService watchService = FileSystems.getDefault().newWatchService(); Path directory = Paths.get("."); // Register for create, modify, and delete events directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE); System.out.println("Watching directory: " + directory.toAbsolutePath()); while (true) { WatchKey key = watchService.take(); // Blocks until event occurs for (WatchEvent<?> event : key.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); Path fileName = (Path) event.context(); System.out.println(kind + ": " + fileName); if (kind == StandardWatchEventKinds.OVERFLOW) { continue; } } // Reset the key to receive further events boolean valid = key.reset(); if (!valid) { break; } } } }

7. Character Sets and Encoding

public class CharsetExample { public static void main(String[] args) { String text = "Hello, 世界!"; // Available charsets System.out.println("Available charsets:"); Charset.availableCharsets().keySet().stream() .sorted() .forEach(System.out::println); // Encoding and decoding Charset utf8 = StandardCharsets.UTF_8; Charset iso = StandardCharsets.ISO_8859_1; // Encode string to bytes ByteBuffer utf8Buffer = utf8.encode(text); ByteBuffer isoBuffer = iso.encode(text); System.out.println("\nUTF-8 bytes: " + Arrays.toString(utf8Buffer.array())); System.out.println("ISO-8859-1 bytes: " + Arrays.toString(isoBuffer.array())); // Decode bytes to string utf8Buffer.rewind(); isoBuffer.rewind(); String decodedUtf8 = utf8.decode(utf8Buffer).toString(); String decodedIso = iso.decode(isoBuffer).toString(); System.out.println("Decoded UTF-8: " + decodedUtf8); System.out.println("Decoded ISO-8859-1: " + decodedIso); // Character buffer operations CharBuffer charBuffer = CharBuffer.allocate(100); charBuffer.put("Hello, NIO!"); charBuffer.flip(); ByteBuffer byteBuffer = utf8.encode(charBuffer); System.out.println("Encoded bytes: " + byteBuffer.remaining()); } }

8. Memory-Mapped Files

public class MemoryMappedFileExample { public static void main(String[] args) throws IOException { Path filePath = Paths.get("largefile.dat"); // Create a large file for demonstration try (FileChannel channel = FileChannel.open(filePath, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)) { // Create a 100MB file long fileSize = 100 * 1024 * 1024; // 100MB channel.position(fileSize - 1); channel.write(ByteBuffer.wrap(new byte[1])); // Map entire file to memory MappedByteBuffer mappedBuffer = channel.map( FileChannel.MapMode.READ_WRITE, 0, fileSize); // Write data directly to memory for (int i = 0; i < 1000; i++) { mappedBuffer.putInt(i); } // Read data from memory mappedBuffer.flip(); for (int i = 0; i < 10; i++) { System.out.println("Read: " + mappedBuffer.getInt()); } // Force changes to disk mappedBuffer.force(); } catch (IOException e) { e.printStackTrace(); } } }

9. Asynchronous File Channel

public class AsyncFileChannelExample { public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { Path path = Paths.get("async_test.txt"); // Create file and write some content Files.writeString(path, "Initial content\n", StandardOpenOption.CREATE); try (AsynchronousFileChannel asyncChannel = AsynchronousFileChannel.open( path, StandardOpenOption.READ, StandardOpenOption.WRITE)) { // Asynchronous write String data = "Hello, Async NIO!\n"; ByteBuffer writeBuffer = ByteBuffer.wrap(data.getBytes()); Future<Integer> writeResult = asyncChannel.write(writeBuffer, 0); // Do other work while writing... System.out.println("Doing other work while writing..."); // Wait for write to complete Integer bytesWritten = writeResult.get(); System.out.println("Bytes written: " + bytesWritten); // Asynchronous read ByteBuffer readBuffer = ByteBuffer.allocate(1024); Future<Integer> readResult = asyncChannel.read(readBuffer, 0); // Do other work while reading... System.out.println("Doing other work while reading..."); Integer bytesRead = readResult.get(); readBuffer.flip(); byte[] readData = new byte[bytesRead]; readBuffer.get(readData); System.out.println("Read data: " + new String(readData)); } catch (IOException e) { e.printStackTrace(); } } }

10. Best Practices and Performance Tips

1. Use Direct Buffers for Large I/O Operations

// Good for large files ByteBuffer buffer = ByteBuffer.allocateDirect(8192); // Good for small, frequent allocations ByteBuffer buffer = ByteBuffer.allocate(1024);

2. Reuse Buffers

public class BufferPool { private final Queue<ByteBuffer> bufferPool = new ConcurrentLinkedQueue<>(); public ByteBuffer getBuffer(int size) { ByteBuffer buffer = bufferPool.poll(); if (buffer == null || buffer.capacity() < size) { return ByteBuffer.allocate(size); } buffer.clear(); return buffer; } public void returnBuffer(ByteBuffer buffer) { buffer.clear(); bufferPool.offer(buffer); } }

3. Proper Buffer Management

public class BufferManagement { public static void processFile(Path path) throws IOException { try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) { ByteBuffer buffer = ByteBuffer.allocateDirect(8192); while (channel.read(buffer) != -1) { buffer.flip(); // Switch to read mode processBuffer(buffer); buffer.compact(); // Keep unread data and prepare for next read } // Process any remaining data buffer.flip(); if (buffer.hasRemaining()) { processBuffer(buffer); } } } private static void processBuffer(ByteBuffer buffer) { while (buffer.hasRemaining()) { // Process each byte byte b = buffer.get(); // ... processing logic } } }

Summary

Java NIO Advantages:

  • Non-blocking I/O for better scalability
  • Memory-mapped files for high-performance file access
  • Selectors for handling multiple channels with single thread
  • Buffer-oriented operations for efficient data handling

When to Use NIO:

  • High-performance server applications
  • Large file processing
  • Applications requiring non-blocking I/O
  • Network applications with many concurrent connections

When to Use Traditional I/O:

  • Simple file operations
  • Sequential processing
  • When simplicity is more important than performance
  • Small-scale applications

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper