本笔记记录了视频Java NIO 视频教程全集的学习笔记。
Java NIO( New IO) 是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同, NIO支持面向缓冲区的、基于通道的IO操作。 NIO将以更加高效的方式进行文件的读写操作。
Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。
简而言之,Channel 负责传输, Buffer 负责存储。
缓冲区(Buffer):一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
Buffer 就像一个数组,可以保存多个相同类型的数据。根据数据类型不同( boolean 除外),有以下 Buffer 常用子类:
上述 Buffer 类 他们都采用相似的方法进行管理数据,只是鸽子管理的数据类型不同而已。都是通过 static XxxBuffer allocate(int capacity)
获取一个 Buffer 对象
Buffer 中的重要概念
直接上代码:
package top.db.nio.p2; import java.nio.ByteBuffer; /** */ public class ByteTest { // 缓冲区 Buffer, Java NIO中负责数据的存取。用于存取不同数据类型 // ByteBuffer // CharBuffer // ShortBuffer // IntBuffer // LongBuffer // FloatBuffer // DoubleBuffer // 上述缓存区的管理方式几乎一致,通过allocate()获取缓冲区 // 二、缓冲区存取数据的两个核心方法 // put():存入缓冲区数据 // get():获取缓冲区中的数据 // // 三、缓冲区的4个核心指标属性 // capacity:容量,表示缓冲区中最大存储数据的容量,一旦声明不能改变 // limit:界限,表示缓冲区中路操作数据的大小,(limit后数据不能读写) // position:位置,表示缓存区正在操作数据的位置 // // // position <=limit <=capacity // // /** * 我们通过一个比较常用的ByteBuffer来熟悉一下 * allocate(int capacity) * put(byte[] bytes) * flip() * get(byte[] bytes, int offset, int length) * 我们通过以下代码来观察以上四种方法调用的时候, * position * limit * capacity * 的变化 * * @param args */ public static void main(String[] args) { ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println("-------------allocate-----------------"); System.out.println("capacity:" + buf.capacity()); System.out.println("limit:" + buf.limit()); System.out.println("position:" + buf.position()); String s = "hello"; buf.put(s.getBytes()); System.out.println("-------------put-----------------"); System.out.println("capacity:" + buf.capacity()); System.out.println("limit:" + buf.limit()); System.out.println("position:" + buf.position()); buf.flip(); System.out.println("-------------flip-----------------"); System.out.println("capacity:" + buf.capacity()); System.out.println("limit:" + buf.limit()); System.out.println("position:" + buf.position()); byte[] bytes = new byte[1024]; buf.get(bytes, 0, buf.limit()); System.out.println("-------------get-----------------"); System.out.println("capacity:" + buf.capacity()); System.out.println("limit:" + buf.limit()); System.out.println("position:" + buf.position()); System.out.println(new String(bytes, 0, buf.limit())); } }
通道(Channel):由 java.nio.channels
包定义的。Channel 表示 IO 源与目标打开的连接。 Channel 类似于传统的“流”。只不过 Channel 本身不能直接访问数据, Channel 只能与 Buffer 进行交互。
通道的代码如下:
package top.db.nio.p3; import lombok.extern.slf4j.Slf4j; import org.springframework.util.ResourceUtils; import java.io.*; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; /** * 测试 * */ @Slf4j public class ChannelTest { /* 一、通道(Channel): 用于源节点与目标节点的连接, 在Java NIO中负责数据的传输, Channel本省不存储数据, 因此需要配合缓冲区进行传输 二、通道的主要实现类 java.nio.channels.Channel 接口 since 1.4 java.nio.channels.ByteChannel java.nio.channels.FileChannel |--SocketChannel |--ServerSocketChannel |--DatagramChannel 三、获取通道 1.Java针对通道的类提供了getChannel()方法 本地IO: FileInputStream、FileOutStream RandomAccessFile 网络IO: Socket ServerSocket DatagramSocket 2.在JDK1.7==NIO2 中 静态方法: open() 3.在JDK1.7==NIO2 中 Files工具类中newByteChannel() 四、通道之间传输数据 transferTo() transferFrom() 五、分散(Scatter)与聚集(Gather) 分散读取: 将通道中大数据分散到多个缓冲区中 聚集写入:将多个缓冲区中的数据聚集到通道中 注意:按照缓冲区的顺序,从channel中读取依次将buffer填满 六、字符集:CharSet 编码:字符串-->字节数组 解码:字节数组-->字符串 */ public static void main(String[] args) throws IOException { // new ChannelTest().test1(); // new ChannelTest().test2(); // new ChannelTest().test3(); // new ChannelTest().test4(); // new ChannelTest().test5(); } /** * 字符集 * * @throws CharacterCodingException */ private void test5() throws CharacterCodingException { // 下面三行代码用来查看NIO包下的Charset都有什么字符集 // SortedMap<String, Charset> map = Charset.availableCharsets(); // for (Map.Entry<String, Charset> entry : map.entrySet()) { // System.out.println(entry.getKey() + "--" + entry.getValue()); // } Charset gbkCharset = Charset.forName("GBK"); //获取编码器 CharsetEncoder charsetEncoder = gbkCharset.newEncoder(); //获取解码器 CharsetDecoder charsetDecoder = gbkCharset.newDecoder(); // 分配缓冲空间 CharBuffer cBuffer = CharBuffer.allocate(1024); String msg = "测试测试1234"; // 将数据写入缓冲区 cBuffer.put(msg); // 转换成写模式 cBuffer.flip(); // 编码,使用GBK编码器对流进行编码 ByteBuffer bBuffer = charsetEncoder.encode(cBuffer); // 可以获得编码后的结果 for (int i = 0; i < msg.length(); i++) { // 这里的原理是,每get一次,position都会移动 System.out.println(bBuffer.get()); } // 解码,转换成写模式 bBuffer.flip(); // 使用GBK解码 CharBuffer decodeCharBuffer = charsetDecoder.decode(bBuffer); System.out.println(decodeCharBuffer.toString()); System.out.println("__________________________________"); // 使用UTF-8进行解码 Charset charset2 = Charset.forName("UTF-8"); bBuffer.flip(); CharBuffer decode2 = charset2.decode(bBuffer); // 编码解码使用的字符集不同,必然是乱码的结果 System.out.println(decode2.toString()); } /** * 分散(Scatter)与聚集(Gather) * 老师在这里用到了一个类 RandomAccessFile, 这里用这个类我感觉不太方便我们理解 * 我这边将 RandomAccessFile 改成我们学习阶段比较舒徐的流的方式,方便大家理解 Scatter 与 Gather * @throws IOException */ private void test4() throws IOException { File file = ResourceUtils.getFile("222.jpg"); try ( // 1.获取通道 FileInputStream fileInputStream = new FileInputStream(file); FileOutputStream fileOutputStream = new FileOutputStream("333.jpg"); FileChannel inChannel = fileInputStream.getChannel(); FileChannel outChannel = fileOutputStream.getChannel() ) { // 2.分配指定大小的缓冲区 ByteBuffer buffer1 = ByteBuffer.allocate(1024); ByteBuffer buffer2 = ByteBuffer.allocate(4028000); // 3.分散获取 ByteBuffer[] buffers = {buffer1, buffer2}; long read = inChannel.read(buffers); System.out.println(read); for (ByteBuffer buffer : buffers) { buffer.flip(); } // 4.聚集写入 System.out.println(outChannel.write(buffers)); for (ByteBuffer buffer : buffers) { buffer.clear(); } } } /** * transferTo * transferFrom */ private void test3() { try (// 开启通道 FileChannel inChannel = FileChannel.open(Paths.get("222.jpg"), StandardOpenOption.READ); FileChannel outChannel = FileChannel.open(Paths.get("333.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); ) { // // 使用transferTo // inChannel.transferTo(0, inChannel.size(), outChannel); // 使用transferFrom outChannel.transferFrom(inChannel, 0, inChannel.size()); } catch (IOException e) { e.printStackTrace(); } } /** * 2.利用通道完成文件复制(直接缓冲区--内存映射) * 通过FileChannel类的open()方法直接获取文件通道 * 声明内存映射(直接缓冲) * 文件传输 * */ private void test2() { try ( // 1.创建通道 FileChannel inChannel = FileChannel.open(Paths.get("222.jpg"), StandardOpenOption.READ); FileChannel outChannel = FileChannel.open(Paths.get("333.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE) ) { // 2.声明内存映射 ByteBuffer buffer = ByteBuffer.allocateDirect(1024); MappedByteBuffer inMappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMappedByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size()); // 3.文件传输 -- 直接对缓冲区数据进行读写操作 byte[] dest = new byte[inMappedByteBuffer.limit()]; inMappedByteBuffer.get(dest); outMappedByteBuffer.put(dest); } catch (IOException e) { e.printStackTrace(); } } /** * 本机内文件移动 * 1、利用通道完成文件复制(非直接缓冲区--JVM 堆内存) * 通过FileInputStream获取系统中的文件,将其转为流 * 分配缓冲区,使用ByteBuffer.allocate(1024) * 通过FileInputStream获取通道,使用通道来读取文件 * 边读边写,ByteBuffer既可以写入数据,也可以读取数据 * 因此直接使用FileOutputStream获取输出通道 * 通过输出通道写出数据即可 * * @throws FileNotFoundException */ public void test1() throws FileNotFoundException { File file = ResourceUtils.getFile("D:/壁纸/看电脑的女生.jpg"); String target = "222.jpg"; try ( // 获取流 FileInputStream fileInputStream = new FileInputStream(file); FileOutputStream fileOutputStream = new FileOutputStream(target); // 获取通道 FileChannel fileInputChannel = fileInputStream.getChannel(); FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) { // 分配指定缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); // 将通道中的数据存入缓冲区 // 实际上就是,缓冲区每读满一次,写入一次,直到读完所有数据 while (fileInputChannel.read(buffer) != -1) { // 通过虾米那四行代码可以清晰的看到拷贝的数据流动 System.out.println("------------------------------"); System.out.println("capacity:" + buffer.capacity()); System.out.println("limit:" + buffer.limit()); System.out.println("position:" + buffer.position()); // 将缓冲区(buffer)切换成读数据模式 buffer.flip(); // 将缓冲区(buffer)的数据写入通道 fileOutputStreamChannel.write(buffer); // 清空缓冲区 buffer.clear(); } } catch (IOException e) { log.error("IOException occupy", e); } } }
直接上代码:
package top.db.nio.p9; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; @Slf4j public class ServerSocketChannelTest { //客户端数据发送一个读写请求到服务端 // 服务无端不能确定客户端判断数据真实有效状态时候,该线程会一直处于阻塞状态,服务端会等客户端发送 // // 服务端会判断数据在内核地址空间中是否存在,此时服务端线程阻塞,原来是多线程来解决,为每一个请求分配一个线程来处理请求,充分利用cpu资源, // 后续请求,线程没有100%的利用, // // NIIO--非阻塞式: 通道加缓冲区, // 把每一个用于传输数据的通道注册到选择器,选择器实时监控通道上的状况, // 当某一个通道上的某一个请求的事件完全准备就绪时,选择器才会将这个任务分配到服务端的一个或多个线程上在去运行 // // // // // 读数据状态--完全准备就绪 // 选择器:Selector // //=============================== // 一、使用NIO完成网络通信的三个核心 //1、通道(Channel::负责连接 // java.nio.channels.Channel // |--SelectableChannel // |--ServerSocketChannel // |--DatagramChannel // // |--Pipe.SinkChannel // |--Pipe.SourceChannel // // FileChannel不能切换成非阻塞模式 // // //2、缓冲区(Buffer):负责数据的存取 // //3、选择器(Selector):是SelectableChannel的多路复涌去。用于监控SelectableChannel的IO情况 // // public static void main(String[] args) throws InterruptedException { long s1 = System.currentTimeMillis(); Runnable server = new Runnable() { @Override public void run() { new ServerSocketChannelTest().server(); } }; Runnable client = new Runnable() { @Override public void run() { new ServerSocketChannelTest().client(); } }; //todo 为何两个线程启动 不是串行的 server.run(); Thread.sleep(100L); client.run(); System.out.println("END=" + (System.currentTimeMillis() - s1)); } //客户端 @Test public void client() { //1、获取通道 SocketChannel socketChannel = null; FileChannel inChannel = null; try { socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); //测试中是发送一个图片给server,这里用FileChannel去获取 inChannel = FileChannel.open(Paths.get("T:/data/nio/1.jpg"), StandardOpenOption.READ); //2、分配指定的大小的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //3、发送数据 //long transferTo = inChannel.transferTo(0, inChannel.size(), socketChannel); while (inChannel.read(buffer) != -1) { buffer.flip(); socketChannel.write(buffer); buffer.clear(); } } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } finally { if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } if (socketChannel != null) { try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } } } //服务器--阻塞式 @Test public void server() { ServerSocketChannel ssChannel = null; SocketChannel acceptSocketChannel = null; FileChannel outChannel = null; try { //1、获取通道 ssChannel = ServerSocketChannel.open(); outChannel = FileChannel.open(Paths.get("T:/data/nio/_21.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); //2、绑定连接 ssChannel.bind(new InetSocketAddress(9898)); //3、获取客户端连接的通道 acceptSocketChannel = ssChannel.accept(); //4、分配指定大小的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //5、接收客户端的数据,并保存到本地 while (acceptSocketChannel.read(buffer) != -1) { buffer.flip(); outChannel.write(buffer); buffer.clear(); } } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } finally { if (ssChannel != null) { try { ssChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } //acceptSocketChannel if (acceptSocketChannel != null) { try { acceptSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } //outChannel if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } } } }
package top.db.nio.p10; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.time.LocalDateTime; import java.util.Iterator; import java.util.Scanner; @Slf4j public class NoBlockingNioTest { // 读数据状态--完全准备就绪 // 选择器:Selector // //=============================== // 一、使用NIO完成网络通信的三个核心 //1、通道(Channel::负责连接 // java.nio.channels.Channel // |--SelectableChannel // |--ServerSocketChannel // |--DatagramChannel // // |--Pipe.SinkChannel // |--Pipe.SourceChannel // // FileChannel不能切换成非阻塞模式 // // //2、缓冲区(Buffer):负责数据的存取 // //3、选择器(Selector):是SelectableChannel的多路复涌去。用于监控SelectableChannel的IO情况 // // // public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("请输入一个字符串(中间能加空格或符号)"); String line = input.nextLine(); String s = LocalDateTime.now().toString() + "\n" + line; System.out.println(s); } //客户端 @Test public void client() { SocketChannel socketChannel = null; FileChannel inChannel = null; try { //1、获取通道 socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); //2、切换成非阻塞模式 socketChannel.configureBlocking(false); //3、分配缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //测试中是发送一个 时间字符串给server // String msg = getMsg(); buffer.put(msg.getBytes()); buffer.flip(); //4、发送数据 socketChannel.write(buffer); buffer.clear(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } finally { if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } if (socketChannel != null) { try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } } } private String getMsg() { // Scanner scanner = new Scanner(System.in); // System.out.println("请输入:"); // while (scanner.hasNext()) { // String msg = scanner.next(); // } // return "null"; // Scanner input = new Scanner(System.in); // System.out.println("请输入一个字符串(中间能加空格或符号)"); // String line = input.nextLine(); // String s = LocalDateTime.now().toString() + "\n" + line; // System.out.println(s); // scanner 在junit中不能正常工作 return "1111111"; } // System.out.println("请输入一个字符串(中间不能加空格或符号)"); // String b = input.next(); // System.out.println("请输入一个整数"); // int c; // c = input.nextInt(); // System.out.println("请输入一个double类型的小数"); // double d = input.nextDouble(); // System.out.println("请输入一个float类型的小数"); // float f = input.nextFloat(); // System.out.println("按顺序输出abcdf的值:"); // System.out.println(a); // System.out.println(b); // System.out.println(c); // System.out.println(d); // System.out.println(f); //服务器--阻塞式 @Test public void server() { ServerSocketChannel ssChannel = null; SocketChannel acceptSocketChannel = null; FileChannel outChannel = null; try { //1、获取通道 ssChannel = ServerSocketChannel.open(); //2、设置非阻塞 ssChannel.configureBlocking(false); //3、绑定连接 ssChannel.bind(new InetSocketAddress(9898)); //4、获取选择器 Selector selector = Selector.open(); //5、将通道注册到选择器,并且指定“监听事件” ,对比之前的 accept方法=阻塞 ssChannel.register(selector, SelectionKey.OP_ACCEPT); //6、轮询式的获取选择器上“已经准备就绪”的事件 while (selector.select() > 0) { //至少有一个准备就绪的事件 //7、包括所有注册的“选择键(已就绪的监听事件)” Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); //8、获取准备就绪的事件 while (iterator.hasNext()) { //9、判断具体是什么准备就绪事件 SelectionKey sk = iterator.next(); if (sk.isAcceptable()) { //10、若"接受就绪",获取客户端连接 SocketChannel sChannel = ssChannel.accept(); //11、设置非阻塞模式 sChannel.configureBlocking(false); //12、将该通道注册到选择器上 sChannel.register(selector, SelectionKey.OP_READ); } else if (sk.isReadable()) { //13、获取当前选择器上“读就绪”状态的通道 SocketChannel sChannel = (SocketChannel) sk.channel(); //14、读取数据 ByteBuffer buffer = ByteBuffer.allocate(1024); int len = 0; while ((len = sChannel.read(buffer)) > 0) { buffer.flip(); System.out.println(new String(buffer.array(), 0, len)); buffer.clear(); } } //15、取消选择键 iterator.remove(); } } //4、分配指定大小的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //5、接收客户端的数据,并保存到本地 while (acceptSocketChannel.read(buffer) != -1) { buffer.flip(); outChannel.write(buffer); buffer.clear(); } } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } finally { if (ssChannel != null) { try { ssChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } //acceptSocketChannel if (acceptSocketChannel != null) { try { acceptSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } //outChannel if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("结果={}", e); } } } } }
package top.db.nio.p11; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Iterator; @Slf4j public class NoBlockingNio2Test { //发送端 @Test public void send() { DatagramChannel datagramChannel = null; try { datagramChannel = DatagramChannel.open(); datagramChannel.configureBlocking(false); ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("测试1234".getBytes()); buffer.flip(); datagramChannel.send(buffer, new InetSocketAddress("127.0.0.1", 8080)); buffer.clear(); } catch (IOException e) { e.printStackTrace(); }finally { if (datagramChannel != null) { try { datagramChannel.close(); System.out.println("发送完成关闭 DatagramChannel!"); } catch (IOException e) { e.printStackTrace(); log.error("e={}", e); } } } } //接受端 @Test public void serverDatagramChannel() { DatagramChannel serverDatagramChannel = null; try { serverDatagramChannel = DatagramChannel.open(); serverDatagramChannel.configureBlocking(false); serverDatagramChannel.bind(new InetSocketAddress(8080)); Selector selector = Selector.open(); serverDatagramChannel.register(selector, SelectionKey.OP_READ); while (selector.select() > 0) { Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey sk = iterator.next(); if (sk.isReadable()) { ByteBuffer buffer = ByteBuffer.allocate(1024); serverDatagramChannel.receive(buffer); buffer.flip(); System.out.println(new String(buffer.array(), 0, buffer.limit())); buffer.clear(); } } iterator.remove(); } } catch (IOException e) { e.printStackTrace(); }finally { if (serverDatagramChannel != null) { try { serverDatagramChannel.close(); } catch (IOException e) { e.printStackTrace(); log.error("e={}", e); } } } } }
管道
package top.db.nio.p12; import org.junit.Test; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Pipe; public class PipeTest { /* Java NIO 管道是两个线程之间的 单项数据连接 Pipe有一个source通道和一个sink通道。 数据会被写到sink通道,从source通道读取 */ @Test public void test1() throws IOException { //1、获取管道 Pipe pipe = Pipe.open(); //2、将缓冲区中的数据写入管道 ByteBuffer buffer = ByteBuffer.allocate(1024); Pipe.SinkChannel sinkChannel = pipe.sink(); buffer.put(" 数据会被写到sink通道".getBytes()); buffer.flip(); sinkChannel.write(buffer); //3、数据读取到缓冲区 Pipe.SourceChannel sourceChannel = pipe.source(); buffer.flip(); int len = sourceChannel.read(buffer); System.out.println(new String (buffer.array(),0,len)); sinkChannel.close(); sourceChannel.close(); } }