Java NIO(二)通道Channel

一、通道(Channel):

用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。


 二、通道的主要实现类
     java.nio.channels.Channel 接口:
         |--FileChannel:用于读取、写入、映射和操作文件的通道。
         |--SocketChannel:通过 UDP 读写网络中的数据通道。
         |--ServerSocketChannel:通过 TCP 读写网络中的数据。
         |--DatagramChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
 
 三、获取通道
 1. Java 针对支持通道的类提供了 getChannel() 方法
         本地 IO:
         FileInputStream/FileOutputStream

         RandomAccessFile
         网络IO:
         Socket
         ServerSocket
         DatagramSocket
         
 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
 
 四、通道之间的数据传输
 transferFrom()
 transferTo()
 五、分散(Scatter)与聚集(Gather)
  分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中

  聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中

  六、字符集:Charset
  编码:字符串 -> 字节数组
  解码:字节数组  -> 字符串

    //非直接缓冲区,通道完成对文件的复制
	@Test
	public void test01() throws Exception{
		FileInputStream in=new FileInputStream("f:/z6.png");
		FileOutputStream ou=new FileOutputStream("f:/2.jpg");
		//获取通道
		FileChannel channeIn = in.getChannel();
		FileChannel channelOu = ou.getChannel();
		//分配指定大小缓冲区
		ByteBuffer allocate = ByteBuffer.allocate(1024);
		//将通道中的数据读入缓冲区
		while(channeIn.read(allocate)!=-1){
			//切换读取数据模式
			allocate.flip();
			//将缓冲区数据写入通道
			channelOu.write(allocate);
			//清楚缓冲区
			allocate.clear();
		}
		channeIn.close();
		channelOu.close();
		in.close();
		ou.close();
	}

静态方法 open()

    //使用直接缓冲区完成文件的复制(内存映射文件)
	@Test
	public void test02() throws IOException{
		FileChannel read = FileChannel.open(Paths.get("z6.png"), StandardOpenOption.READ);
		//StandardOpenOption.CREATE_NEW如果不存在则报错,不进行覆盖,create如果存在则进行覆盖
		//Path path = Paths.get("e:/", "/s","/s");   可以进行路径的拼接
		FileChannel write = FileChannel.open(Paths.get("2.png"), 
			StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
		//内存映射文件
		MappedByteBuffer mapR = read.map(MapMode.READ_ONLY, 0, read.size());
		MappedByteBuffer mapW = write.map(MapMode.READ_WRITE, 0, read.size());
		byte[] dst=new byte[mapR.limit()];
		//直接对缓冲区进行数据的读写操作-因是直接缓冲区可以直接操作,不借用通道
		mapR.get(dst);
		mapW.put(dst);
		read.close();
		write.close();
	}

以上两种方式,在使用1G大小的视频进行复制测试的时候,使用直接缓冲区的速度明显高于非直接缓冲区,但直接缓冲区有的时候会出现文件复制完成,程序却没有结束的情况。直接缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区 。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的机 本机 I/O  操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好
处时分配它们。

通道之间的数据传输

    //通道之间的数据传输,直接缓冲区
	@Test
	public void test03() throws Exception{
		FileChannel read = FileChannel.open(Paths.get("z6.png"), StandardOpenOption.READ);
		FileChannel write = FileChannel.open(Paths.get("7.png"), 
			StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
		//read.transferTo(0, read.size(), write);
		write.transferFrom(read, 0, read.size());
		read.close();
		write.close();
	}

分散(Scatter)与聚集(Gather)

    @Test
	public void test04() throws IOException{
		RandomAccessFile read=new RandomAccessFile("1.txt", "rw");//rw为读写操作简写 
		FileChannel channel = read.getChannel();//获取通道
		ByteBuffer allocate1 = ByteBuffer.allocate(100);//分配缓冲区
		ByteBuffer allocate2 = ByteBuffer.allocate(10000);
		ByteBuffer dsts[]={allocate1,allocate2};
		channel.read(dsts);
		
		System.out.println(new String(dsts[0].array(), 0, dsts[0].limit()));
		System.out.println("==================");
		System.out.println(new String(dsts[1].array(), 0, dsts[1].limit()));
		//以上得出结论在写入缓冲区的时候 ,是有序的,按顺序操作
		//写入
		for (ByteBuffer byteBuffer : dsts) {
		 byteBuffer.flip();
	    }
		RandomAccessFile write=new RandomAccessFile("2.txt", "rw");
		FileChannel channel2 = write.getChannel();
		channel2.write(dsts);
		channel2.close();
		channel.close();
		write.close();
		read.close();
	}

字符集:Charset

    @Test
	public void test05() throws IOException{
		//nio 支持的编码
		/*SortedMap<String,Charset> availableCharsets = Charset.availableCharsets();
		Set<Entry<String,Charset>> entrySet = availableCharsets.entrySet();
		for (Entry<String, Charset> entry : entrySet) {
			System.out.println(entry.getKey()+"=="+entry.getValue());
		}*/
		Charset cs = Charset.forName("GBK");
		CharsetEncoder newEncoder = cs.newEncoder();//编码
		CharsetDecoder newDecoder = cs.newDecoder();	//解码
		CharBuffer cBuf = CharBuffer.allocate(1024);
		cBuf.put("我是中国人");
		cBuf.flip();
		ByteBuffer bBuf = newEncoder.encode(cBuf);//编码
		for (int i = 0; i < bBuf.limit(); i++) {
			System.out.println(bBuf.get());
		}
		bBuf.flip();
		CharBuffer decode = newDecoder.decode(bBuf);	//解码
		System.out.println("=========");
		System.out.println(decode.toString());
	}

猜你喜欢

转载自blog.csdn.net/baidu_36327010/article/details/85032447