Backend Development 31 min read

A Quick Introduction to Java NIO with Practical Examples

This article explains Java NIO fundamentals—including Channels, Buffers, and Selectors—compares them with traditional IO, and provides multiple runnable code examples such as FileChannel, SocketChannel, Selector loops, memory‑mapped files, scatter/gather, transferTo/From, Pipe and DatagramChannel to illustrate non‑blocking and high‑performance I/O.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
A Quick Introduction to Java NIO with Practical Examples

Java NIO (New I/O) is increasingly used in modern frameworks like Tomcat and Jetty, making it an essential skill for Java developers and interview preparation.

The core of NIO consists of three components: Channel, Buffer, and Selector. Channels replace streams, Buffers act as containers for data, and Selectors enable a single thread to monitor multiple channels for events such as accept, read, write, and connect.

Compared with traditional IO, NIO is buffer‑oriented and supports non‑blocking operations, allowing a thread to continue processing while waiting for data instead of being blocked on read() or write().

Channel is a bidirectional conduit (e.g., FileChannel, DatagramChannel, SocketChannel, ServerSocketChannel). The following code demonstrates reading a file with traditional IO and the equivalent NIO version using FileChannel and ByteBuffer:

public static void method2(){
    InputStream in = null;
    try{
        in = new BufferedInputStream(new FileInputStream("src/nomal_io.txt"));
        byte [] buf = new byte[1024];
        int bytesRead = in.read(buf);
        while(bytesRead != -1){
            for(int i=0;i
public static void method1(){
    RandomAccessFile aFile = null;
    try{
        aFile = new RandomAccessFile("src/nio.txt","rw");
        FileChannel fileChannel = aFile.getChannel();
        ByteBuffer buf = ByteBuffer.allocate(1024);
        int bytesRead = fileChannel.read(buf);
        System.out.println(bytesRead);
        while(bytesRead != -1){
            buf.flip();
            while(buf.hasRemaining()){
                System.out.print((char)buf.get());
            }
            buf.compact();
            bytesRead = fileChannel.read(buf);
        }
    }catch (IOException e){
        e.printStackTrace();
    }finally{
        try{ if(aFile != null){ aFile.close(); } }catch (IOException e){ e.printStackTrace(); }
    }
}
The Buffer usage pattern typically follows: allocate, read into buffer, flip, read from buffer, then clear or compact.
SocketChannel
example shows a non‑blocking client that repeatedly sends a string and a simple blocking server using traditional IO:
public static void client(){
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    SocketChannel socketChannel = null;
    try{
        socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress("10.10.195.115",8080));
        if(socketChannel.finishConnect()){
            int i=0;
            while(true){
                TimeUnit.SECONDS.sleep(1);
                String info = "I'm "+i+++"-th information from client";
                buffer.clear();
                buffer.put(info.getBytes());
                buffer.flip();
                while(buffer.hasRemaining()){
                    System.out.println(buffer);
                    socketChannel.write(buffer);
                }
            }
        }
    }catch (IOException | InterruptedException e){
        e.printStackTrace();
    }finally{
        try{ if(socketChannel!=null){ socketChannel.close(); } }catch(IOException e){ e.printStackTrace(); }
    }
}
public static void server(){
    ServerSocket serverSocket = null;
    InputStream in = null;
    try{
        serverSocket = new ServerSocket(8080);
        int recvMsgSize = 0;
        byte[] recvBuf = new byte[1024];
        while(true){
            Socket clntSocket = serverSocket.accept();
            SocketAddress clientAddress = clntSocket.getRemoteSocketAddress();
            System.out.println("Handling client at "+clientAddress);
            in = clntSocket.getInputStream();
            while((recvMsgSize=in.read(recvBuf))!=-1){
                byte[] temp = new byte[recvMsgSize];
                System.arraycopy(recvBuf, 0, temp, 0, recvMsgSize);
                System.out.println(new String(temp));
            }
        }
    }catch (IOException e){
        e.printStackTrace();
    }finally{
        try{ if(serverSocket!=null){ serverSocket.close(); } if(in!=null){ in.close(); } }catch(IOException e){ e.printStackTrace(); }
    }
}
Selectors allow a single thread to monitor many channels. The following code creates a Selector, registers a ServerSocketChannel, and handles accept, read, and write events in a loop:
public class ServerConnect{
    private static final int BUF_SIZE=1024;
    private static final int PORT = 8080;
    private static final int TIMEOUT = 3000;
    public static void main(String[] args){ selector(); }
    public static void handleAccept(SelectionKey key) throws IOException{
        ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
        SocketChannel sc = ssChannel.accept();
        sc.configureBlocking(false);
        sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));
    }
    public static void handleRead(SelectionKey key) throws IOException{
        SocketChannel sc = (SocketChannel)key.channel();
        ByteBuffer buf = (ByteBuffer)key.attachment();
        long bytesRead = sc.read(buf);
        while(bytesRead>0){
            buf.flip();
            while(buf.hasRemaining()){
                System.out.print((char)buf.get());
            }
            System.out.println();
            buf.clear();
            bytesRead = sc.read(buf);
        }
        if(bytesRead == -1){ sc.close(); }
    }
    public static void handleWrite(SelectionKey key) throws IOException{
        ByteBuffer buf = (ByteBuffer)key.attachment();
        buf.flip();
        SocketChannel sc = (SocketChannel) key.channel();
        while(buf.hasRemaining()){
            sc.write(buf);
        }
        buf.compact();
    }
    public static void selector() {
        Selector selector = null;
        ServerSocketChannel ssc = null;
        try{
            selector = Selector.open();
            ssc = ServerSocketChannel.open();
            ssc.socket().bind(new InetSocketAddress(PORT));
            ssc.configureBlocking(false);
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            while(true){
                if(selector.select(TIMEOUT) == 0){ System.out.println("=="); continue; }
                Iterator
iter = selector.selectedKeys().iterator();
                while(iter.hasNext()){
                    SelectionKey key = iter.next();
                    if(key.isAcceptable()){ handleAccept(key); }
                    if(key.isReadable()){ handleRead(key); }
                    if(key.isWritable() && key.isValid()){ handleWrite(key); }
                    if(key.isConnectable()){ System.out.println("isConnectable = true"); }
                    iter.remove();
                }
            }
        }catch(IOException e){ e.printStackTrace(); }
        finally{ try{ if(selector!=null){ selector.close(); } if(ssc!=null){ ssc.close(); } }catch(IOException e){ e.printStackTrace(); } }
    }
}
Memory‑mapped files (MappedByteBuffer) provide high‑performance access to large files. The article compares reading a 5 MB file with ByteBuffer versus MappedByteBuffer, showing the latter can be orders of magnitude faster for large files.
public static void method4(){
    RandomAccessFile aFile = null;
    FileChannel fc = null;
    try{
        aFile = new RandomAccessFile("src/1.ppt","rw");
        fc = aFile.getChannel();
        long timeBegin = System.currentTimeMillis();
        ByteBuffer buff = ByteBuffer.allocate((int) aFile.length());
        buff.clear();
        fc.read(buff);
        long timeEnd = System.currentTimeMillis();
        System.out.println("Read time: "+(timeEnd-timeBegin)+"ms");
    }catch(IOException e){ e.printStackTrace(); }
    finally{ try{ if(aFile!=null){ aFile.close(); } if(fc!=null){ fc.close(); } }catch(IOException e){ e.printStackTrace(); } }
}
public static void method3(){
    RandomAccessFile aFile = null;
    FileChannel fc = null;
    try{
        aFile = new RandomAccessFile("src/1.ppt","rw");
        fc = aFile.getChannel();
        long timeBegin = System.currentTimeMillis();
        MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, aFile.length());
        long timeEnd = System.currentTimeMillis();
        System.out.println("Read time: "+(timeEnd-timeBegin)+"ms");
    }catch(IOException e){ e.printStackTrace(); }
    finally{ try{ if(aFile!=null){ aFile.close(); } if(fc!=null){ fc.close(); } }catch(IOException e){ e.printStackTrace(); } }
}
Additional NIO features demonstrated include scatter/gather I/O, transferTo/transferFrom for zero‑copy file transfers, Pipe for thread‑to‑thread communication, and DatagramChannel for UDP networking.
// Scatter/Gather example
ByteBuffer header = ByteBuffer.allocate(10);
ByteBuffer body = ByteBuffer.allocate(10);
header.put(new byte[]{'0','1'});
body.put(new byte[]{'2','3'});
ByteBuffer[] buffs = {header, body};
FileOutputStream os = new FileOutputStream("src/scattingAndGather.txt");
FileChannel channel = os.getChannel();
channel.write(buffs);
// transferFrom example
RandomAccessFile fromFile = new RandomAccessFile("src/fromFile.xml","rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("src/toFile.txt","rw");
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
toChannel.transferFrom(fromChannel, position, count);
Overall, the article provides a comprehensive guide to Java NIO, covering concepts, practical code, performance considerations, and common pitfalls such as resource release for MappedByteBuffer.
JavaNIOnetworkingIOchannelBufferSelector
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.