NIO总结


NIO概述:
NIO在JDK1.4后引入的
NIO是面向块(缓冲区)编程,旧IO是面向流编程

Java中针对NIO的一些核心的包和接口、类

java.nio 主要包含了各种与Buffer相关的类
java.nio.channel 主要包含了与Channel和Selector相关的类和接口
java.nio.charset 主要包含了与编码相关的类接口
java.nio.channels.spi 主要包含了与Channel相关的服务提供者编程接口
javan.nio.charset.spi 主要包含了与charset相关的服务提供者编程接口
四个核心包

  • Buffer
  • Channel
  • CharSet
  • Selector

面向缓冲区编程:
数据的读写必须经过缓冲区
我们可以使用Buffer所对应的子类来数据从通道(Channel)流向缓冲区
从缓冲区写到通道叫做读取缓冲区

Buffer

Buffer是一个抽象类,针对缓冲区封装的一个类,提供相应的方法来操作这个缓冲区
子类有
ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer
核心类
ByteBuffer和CharBuffer
ByteBuffer有一个子类 MappedByteBuffer
MappedByteBuffer类能够将文件直接映射到内存中,那么这样我们就可以像访问内存一样访问文件,非常方便
获取Buffer
获取ByteBuffer

  •  static ByteBuffer allocate(int capacity) 
       		分配一个新的字节缓冲区。 
     static ByteBuffer allocateDirect(int capacity) 
       		分配新的直接字节缓冲区。 
      二者获取Buffer的区别
      1.创建普通Buffer成本低,读写的效率不高
      2.因为创建直接Buffer成本高,所以我们一般用在Buffer生存周期较长的时候使用
      3.只有ByteBuffer才能够创建直接Buffer,其他的Buffer对象是不能够创建
      4.如果创建了直接Buffer但是我又想要使用其他Buffer的功能,可以将ByteBuffer转换成其他Buffer
      		asIntBuffer()
    

四个非常重要的概念

  • capacity: 缓冲区的容量,不可以为负数,一旦创建了就不能够改变
  • limit: 无效缓冲区的第一个位置索引,limit后面的数据既不可读,也不可写 * **position:**下一个可以被读取或者写入的缓冲区位置索引
  • mark: 标记索引,该索引能够用于下次读取或者写入,它只能够在0-position之间

四个系数的关系:
0 < mark < postion < limit < capacity

五个方法
flip(): 将写模式切换为读模式, 将limit的值改为postion的值,同时将postion归0
特点: 就是为下一次数据的读取做好准备
clear(): 将读模式切换为写模式,将limit改为capacity的值,同时将postion归0
特点: 就是为下一次数据的写入做好准备
put(): 相对读取,向Buffer中存储数据
get(): 相对读取,从Buffer中获取数据
mark(): 设置标记位
reset(): 重置
hasRemaining(): 判断当前位置和limit之间是否还有元素可处理
绝对读取: get(index) 不会影响position的位置
相对读取: put() get() 会影响,每次读取一次,指针后移

public class NIODemo02 {
	public static void main(String[] args) {
		// ByteBuffer buffer = ByteBuffer.allocate(5);
		ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
		CharBuffer buffer = CharBuffer.allocate(8);
		// 已经准备好了向Buffer中写数据 写模式
		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 8
		System.out.println("position:" + buffer.position()); // 0

		buffer.put('x');
		buffer.put('y');
		buffer.put('r');
		System.out.println("------------------------");

		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 8
		System.out.println("position:" + buffer.position()); // 3

		System.out.println("------------------------");
		// 切换模式 ,limit变为position的位置然后将position变为0
		buffer.flip();
		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 3
		System.out.println("position:" + buffer.position()); // 0

		System.out.println("------------------------");
		char ch = 0;
		ch = buffer.get();
		System.out.println(ch);

		buffer.mark();// mark: 1

		ch = buffer.get();
		System.out.println(ch);

		System.out.println("------------------------");
		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 3
		System.out.println("position:" + buffer.position()); // 2

		System.out.println("------------------------");
		ch = buffer.get();
		System.out.println(ch);

//		ch = buffer.get();
//		System.out.println(ch);
		System.out.println("---------++---------------");
		buffer.reset();
		
		ch = 0;
		while (buffer.hasRemaining()) {
			ch = buffer.get();
			System.out.println((char) ch);
		}

		System.out.println("------------------");
		buffer.clear(); // 将postion 清 0 ,将limit = capacity

		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 8
		System.out.println("position:" + buffer.position()); // 0

		// 注意: 调用clear方法只是将读模式改为写模式,并不会清空缓冲区的数据
		System.out.println(buffer.get(1));
		System.out.println("执行绝对读取之后Buffer的position位置:" + buffer.position());

	}
}

Channel 通道

Channel原理类似于传统的流对象, FileInputStream FileOutputStream
但是有3个主要的区别

  1. 程序如果想要读取Channel中的数据,不能够直接读写,必须经过Buffer 【唯一性】
  2. 通过Channel通道既能够读取也能够写入数据 【双向性】
  3. Channel能够将指定的部分或者全部文件映射到内存中

全部映射
MappedByteBuffer
部分文件映射
Java中为Channel提供了如下常用的类
FileChannel 和文件相关的通道
DatagramChannel 和UDP协议传输数据相关的通道
SocketChannel 针对TCP协议客户端Socket提供的通道
ServerSocketChannel 针对TCP协议服务器端Socket提供的通道

获取FileChannel对象
和文件相关的普通流有哪些?

FileInputStream FileOutputStream RandomAccessFile

常用的方法

read() : 将Channel中的数据读取到Buffer中
write() : 向Buffer中写入数据
map(): 将channel中的数据全部或者部分映射到Buffer中
		inChannel.map(mode, position, size)
		MappedByteBuffer mappBuffer = inChannel.map(MapMode.READ_ONLY, 0, srcFile.length());
public class NIODemo03 {
	public static void main(String[] args) throws IOException {
		File srcFile = new File("nio.txt");
		FileInputStream fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(new File("nio4.txt"));
		
		// 获取Channel对象
		FileChannel inChannel = fis.getChannel();
		FileChannel outChannel = fos.getChannel();
		
		// 获取Buffer对象
		ByteBuffer buffer = ByteBuffer.allocate(10);
		
		// 将inChannel中的数据读取到Buffer中
//		int len = inChannel.read(buffer);
//		System.out.println(len);
//		byte[] bys = buffer.array();
//		System.out.println(new String(bys, 0, len));
//		//	切换成读模式
//		buffer.flip();
//		len = inChannel.read(buffer);
//		System.out.println(len);
//		System.out.println(new String(buffer.array(), 0, len));
//		buffer.flip();
//		
//		len = inChannel.read(buffer);
//		System.out.println(len);
//		System.out.println(new String(buffer.array(), 0, len));
//		buffer.flip();
		
//		int len = 0;
//		while ((len = inChannel.read(buffer)) != -1) {
//			buffer.flip();
//			System.out.print(new String(buffer.array(), 0, len));
//		}
		
//		while ((inChannel.read(buffer)) != -1) {
//			buffer.flip(); // 为取出数据做好准备
//			outChannel.write(buffer);
//			buffer.clear();
//		}
		
//		3.Channel能够将指定的部分或者全部文件映射到内存中
//		java.nio.channels.NonWritableChannelException
//		java.nio.channels.NonReadableChannelException
//		MappedByteBuffer mapBuffer = inChannel.map(MapMode.READ_WRITE, 0, srcFile.length());
//		byte[] array = mapBuffer.array();
//		System.out.println(new String(array));
	}
}

利用NIO来遍历文件

基于事件驱动的方式遍历文件

public class NIODemo07 {
   public static void main(String[] args) throws IOException {
   	FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
   		@Override
   		public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) throws IOException {
   			System.out.println("正准备访问" + path + "文件");
   			return FileVisitResult.CONTINUE;
   		}
   		
   		@Override
   		public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
   			System.out.println("正在访问" + path + "文件");
   			if (path.endsWith("NIODemo03.java")) {
   				System.out.println("恭喜你找到了我想要的Java文件,你可以停止查找了");
   				return FileVisitResult.TERMINATE;
   			}
   			return FileVisitResult.CONTINUE;
   		}
   		
   		@Override
   		public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException {
   			return FileVisitResult.SKIP_SIBLINGS;
   		}
   		
//			@Override
//			public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException {
//				return FileVisitResult.CONTINUE;
//			}
   	};		
   	Files.walkFileTree(Paths.get("D:\\JavaSE"), visitor);
   }
}

使用NIO的WatchService监控文件系统变化

public class NIODemo08 {
	public static void main(String[] args) throws Exception {
		// 获取到文件系统的WatchService对象
		WatchService watchService = FileSystems.getDefault().newWatchService();

		Paths.get("c:").register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
				StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY,
				StandardWatchEventKinds.OVERFLOW);
		// 通过wachService来监听文件系统
		while (true) {
			WatchKey key = watchService.take();
			List<WatchEvent<?>> pollEvents = key.pollEvents();
			for (WatchEvent<?> watchEvent : pollEvents) {
				System.out.println(watchEvent.context() + "发生了" + watchEvent.kind() + "事件");
			}

			boolean reset = key.reset();
			if (!reset) {
				break;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值