月眸


java NIO学习笔记

毛毛小妖 2019-08-09 85浏览 2条评论
首页/ 正文
分享到: / / / /

Java NIO(New IO)是从java1.4版本引入的一个新的IO API,可以替代标准的java IO API。NIO与原来的IO有着相同的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更高效的方式进行文件的读写操作。

Java IO是面向流的,分为输入流和输出流,是单向的,就好比我们要从水桶取水和向水桶存水操作,其通信示意图如下:

Java NIO 是面向缓冲区的,这里有两个概念:通道和缓冲区。通道就好比火车轨道,缓冲区就好比火车,通道的作用是连接源和目的地,而缓冲区是存储数据的,其通信示意图如下:

下面我们来看Java IO和Java NIO的主要区别:

IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO
选择器

了解了NIO,下面来具体解释一下NIO中的两个概念:通道和缓冲区。

Java NIO系统的核心在于:通道和缓冲区。通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区,然后操作缓冲区。总之就是:通道负责传输,缓冲区负责存储。

1、缓冲区

一个用于特定基本数据类型的容器(比如:IntBuffer、ByteBuffer、LongBuffer等)。由java.nio包定义的,所有缓冲区都是Buffer抽象类的子类。java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道的。

下面用代码来简单说明一下:

package com.sy.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 一、缓冲区:在java NIO中负责数据的存取。缓冲区是数组,属于存储不同的数据类型
 * 
 * 根据数据类型的不同(boolean除外),提供了相应的缓冲区:
 * ByteBuffer
 * CharBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * FloatBuffer
 * DoubleBuffer
 * 
 * 上述缓冲区的管理机制几乎一样,通过allocate()获取缓冲区
 * 
 * 二、缓冲区存取数据的两个核心方法
 * put():存入数据到缓冲区
 * get():获取缓冲区中的数据
 * 
 * 三、缓冲区中四个核心属性
 * capacity:容量,表示缓冲区中最大存储数据的容量,一旦声明不能改变。
 * limit:界限,表示缓冲区中可以操作数据的大小。
 * position:位置,表示缓冲区中正在操作数据的位置。
 * mark:标记,表示记录当前position的位置,可通过reset()恢复到mark的位置
 * 
 * 0 <= mark <= position <= limit <= capacity
 */
public class TestBuffer {
	public static void main(String[] args) {
		
		//1、分配一个指定大小的缓冲区
		ByteBuffer buf = ByteBuffer.allocate(1024);
		
		System.out.println("----------allocate()----------");
		System.out.println(buf.position());
		System.out.println(buf.capacity());
		System.out.println(buf.limit());
		
		
		//2、利用put()存入数据到缓冲区
		buf.put("hello".getBytes());
		
		System.out.println("----------put()----------");
		System.out.println(buf.position());
		System.out.println(buf.capacity());
		System.out.println(buf.limit());
		
		
		//3、切换读取数据模式
		buf.flip();
		
		System.out.println("----------flip()----------");
		System.out.println(buf.position());
		System.out.println(buf.capacity());
		System.out.println(buf.limit());
		
		
		//4、利用get()读取缓冲区的数据
		byte[] dst = new byte[buf.limit()];
		buf.get(dst);
		
		System.out.println("----------get()----------");
		System.out.println(new String(dst));
		System.out.println(buf.position());
		System.out.println(buf.capacity());
		System.out.println(buf.limit());
		
		//5、rewind():可重复读
		buf.rewind();
		
		System.out.println("----------rewind()----------");
		System.out.println(buf.position());
		System.out.println(buf.capacity());
		System.out.println(buf.limit());
		
		
		//6、清空缓冲区
		buf.clear();
		
		System.out.println("----------clear()----------");
		System.out.println(buf.position());
		System.out.println(buf.capacity());
		System.out.println(buf.limit());
	}
}

2、通道

通道(channel)表示IO源与目标打开的连接,channel类似于传统的“流”。只不过channel本身不能直接访问数据,channel只能与Buffer进行交互。

package com.sy.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
/**
 * 一、通道(channel):用于源节点与目标节点的连接。在java NIO中负责缓冲区中数据的传输。channel本身不传输数据,因此需配合缓冲区进行传输
 * 二、通道的主要实现类
 * java.nio.channels.Channel 接口:
 *     |--FileChannel
 *     |--SocketChannel
 *     |--ServerSocketChannel
 *     |--DatagramChannel
 * 三、获取通道
 * 1、java针对支持通道的类提供了getChannel()方法
 * 本地IO:FileInputStream/FileOutputStream
 * 网络IO:Socket
 *        ServerSocket
 *        DatagramSocket
 * 2、在JDK1.7中的NIO.2 针对各个通道提供了静态方法open()
 * 3、在JDK1.7中的NIO.2 的Files工具类的newByteChannel()
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 * 五、分散与聚集
 * 分散读取:将通道中的数据分散到多个缓冲区中
 * 聚集写入:将多个缓冲区中的数据聚集到通道中
 */
public class TestChannel {
	public static void main(String[] args) {
		String inPath = "D://fileTest//1.pdf";
		String outPath = "D://fileTest//2.pdf";
		//copyFile1(inPath,outPath);
		//TestScatterAndGather("D://fileTest//tree.txt","D://fileTest//tree2.txt");
		TestCharSet();
	}
	
	public static void TestCharSet(){
		Map<String, Charset> map = Charset.availableCharsets();
		Set<Entry<String, Charset>> set = map.entrySet();
		for(Entry<String, Charset> entry : set){
			System.out.println(entry.getKey()+":"+entry.getValue());
		}
	}
	/**
	 * 描述: 分散读取聚集写入
	 * @author shengyu
	 * @param inPath
	 * @param outPath 
	 * @date:2019年8月28日 下午2:19:01
	 */
	public static void TestScatterAndGather(String inPath,String outPath){
		try {
			RandomAccessFile raf = new RandomAccessFile(inPath, "rw");
			FileChannel channel = raf.getChannel();
			
			ByteBuffer buf1 = ByteBuffer.allocate(100);
			ByteBuffer buf2 = ByteBuffer.allocate((int) channel.size());
			
			//分散读取
			ByteBuffer[] bufs = {buf1,buf2};
			channel.read(bufs);
			
			for(ByteBuffer b : bufs){
				b.flip();
			}
			
			System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
			System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
			
			//聚集写入
			RandomAccessFile raf2 = new RandomAccessFile(outPath, "rw");
			FileChannel channel2 = raf2.getChannel();
			channel2.write(bufs);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 描述: 利用transferTo()或transferFrom()方法完成文集复制
	 * @author shengyu 
	 * @date:2019年8月9日 下午4:02:16
	 */
	public static void copyFile1(String inPath,String outPath){
		long start = System.currentTimeMillis();
		FileChannel inChannel = null;
		FileChannel outChannel = null;
		try {
			inChannel = FileChannel.open(Paths.get(inPath), StandardOpenOption.READ);
			outChannel = FileChannel.open(Paths.get(outPath), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
			
			//inChannel.transferTo(0, inChannel.size(), outChannel);
			outChannel.transferFrom(inChannel, 0, inChannel.size());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			long end = System.currentTimeMillis();
			System.out.println("复制成功,用时:"+(end-start)/1000+"秒");
			if(inChannel!=null){
				try {
					inChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(outChannel!=null){
				try {
					outChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 描述: 利用内存映射完成文件复制
	 * @author shengyu
	 * @param inPath
	 * @param outPath 
	 * @date:2019年8月9日 下午4:08:34
	 */
	public void copyFile2(String inPath,String outPath){
		long start = System.currentTimeMillis();
		FileChannel inChannel = null;
		FileChannel outChannel = null;
		MappedByteBuffer inMapBuffer = null;
		MappedByteBuffer outMapBuffer = null;
		try {
			inChannel = FileChannel.open(Paths.get(inPath), StandardOpenOption.READ);
			outChannel = FileChannel.open(Paths.get(outPath), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW);
			
			inMapBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
			outMapBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
			
			byte[] dst = new byte[inMapBuffer.limit()];
			inMapBuffer.get(dst);
			outMapBuffer.put(dst);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			long end = System.currentTimeMillis();
			System.out.println("复制成功,用时:"+(end-start)/1000+"秒");
			if(inChannel!=null){
				try {
					inChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(outChannel!=null){
				try {
					outChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 描述: 利用通道完成文件的复制
	 * 使用了:非直接缓冲区 + FileInputStream + FileOutputStream
	 * @author shengyu 
	 * @date:2019年8月9日 下午3:07:02
	 */
	public static void copyFile3(String inPath,String outPath){
		FileInputStream fis = null;
		FileOutputStream fos = null;
		long start = System.currentTimeMillis();
		try {
			fis = new FileInputStream(new File(inPath));
			fos = new FileOutputStream(new File(outPath));
			
			//1、获取通道
			FileChannel inChannel = fis.getChannel();
			FileChannel outChannel = fos.getChannel();
			
			//2、分配指定大小的缓冲区(非直接缓冲区)
			ByteBuffer buf = ByteBuffer.allocate(1024);
			
			//3、将通道中的数据存入缓冲区中
			while(inChannel.read(buf)!=-1){
				//4、切换读取数据的模式
				buf.flip();
				//5、将缓冲区中的数据写入通道中
				outChannel.write(buf);
				//6、清空缓冲区
				buf.clear();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			long end = System.currentTimeMillis();
			System.out.println("复制成功,用时:"+(end-start)/1000+"秒");
			if(fis!=null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos!=null){
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

 

最后修改:2019-08-09 16:25:44 © 著作权归作者所有
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付

上一篇

发表评论

评论列表

匿名用户 2019-10-15 00:26:23
我发斯蒂芬
回复