NIO02

本文详细介绍了Java NIO中的通道(Channel)和缓冲区(Buffer)概念,讲解了如何通过通道进行文件读写,并展示了使用FileChannel进行数据传输和映射内存到文件的方法。同时,文章涵盖了随机读写流RandomAccessFile的使用,以及字符编码在NIO中的处理。通过对NIO核心组件的理解,读者可以更好地掌握Java I/O的高级特性。
摘要由CSDN通过智能技术生成

NIO

1.缓冲区

2.通道

Channel是通道,是程序与文件连接的桥梁,不能直接进行数据的交互需要借助buffer来进行数据的传输。要先使用的话需要建立通道,要想进行交互通道必须是打开状态.
在这里插入图片描述

2.1Channel常用方法

  1. void
    close() 关闭此通道。
  2. boolean
    isOpen() 判断此通道是否处于打开状态。

2.2计算机底层工作原理

  • 最原始的思路
    在这里插入图片描述

  • 现在传统的io文件操作
    在这里插入图片描述
    在这里插入图片描述
    如果有大量的请求操作会出现DMA总线冲突问题

  • 独立的channel操作
    【

2.3FileChannel 本地文件的操作

  • 使用channel+buffer实现本地文件数据的读写操作
  1. 通道的获取方式 :
    A. Java 针对支持通道的类提供了 getChannel() 方法

本地 IO:

  • FileInputStream/FileOutputStream

  • RandomAccessFile
    网络IO:

  • Socket

  • ServerSocket

  • DatagramSocket
    B. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
    C. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()

2.4 FileChannel的常用方法

在这里插入图片描述

  1. long
    transferFrom(ReadableByteChannel src, long position, long count) 将字节从给定的可读取字节通道传输到此通道的文件中。
  • src - 源通道
  • position - 文件中的位置,从此位置开始传输;必须为非负数
  • count - 要传输的最大字节数;必须为非负数
  1. abstract long
    transferTo(long position, long count, WritableByteChannel target) 将字节从此通道的文件传输到给定的可写入字节通道。
  • position - 文件中的位置,从此位置开始传输;必须为非负数
  • count - 要传输的最大字节数;必须为非负数
  • target - 目标通道
@Test
	void channel() {
		// 创建io流,指定目标文件
		try {
			FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
			FileOutputStream fos = new FileOutputStream(new File("d:\\newhello.txt"));
			// 创建通道,输入通道和输出通道(FileChannel)
			FileChannel inchannel = fis.getChannel();
			FileChannel outchannel = fos.getChannel();
			// 创建一个缓冲区,用来存放数据
			ByteBuffer buffer = ByteBuffer.allocate(5);// 该缓冲区的大小=文件大小
			// 将源文件中的数据写入到缓冲区中
			inchannel.read(buffer);
			//测试
			System.out.println(" ================== 写入模式 ========================");
			System.out.println("总容量:" + buffer.capacity());
			System.out.println("写入的起始标记:" + buffer.position());
			System.out.println("写入时的limit: " + buffer.limit());
			// 切换模式
			buffer.flip();
			// 读取通道中的缓冲区,将内容写出到新文件中
			outchannel.write(buffer);
			// 关闭资源
			outchannel.close();
			inchannel.close();
			fos.flush();
			fos.close();
			fis.close();
			System.out.println("文件写出成功");

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Test
	void channel01() {
		try {
			// 创建io流
			FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
			FileOutputStream fos = new FileOutputStream(new File("d:\\newhello.txt"));
			// 创建通道
			FileChannel inchannel = fis.getChannel();
			FileChannel outchannel = fos.getChannel();
			// 创建一个缓冲区,用来存放数据
			ByteBuffer buffer = ByteBuffer.allocate(5);// 该缓冲区的大小=文件大小

//			int read = inchannel.read(buffer);
//			System.out.println(read);
//			buffer.clear();
//			read = inchannel.read(buffer);
//			System.out.println(read);
//			buffer.clear();
//			read = inchannel.read(buffer);
//			System.out.println(read);
//			buffer.clear();
//			read = inchannel.read(buffer);
//			System.out.println(read);
			while (inchannel.read(buffer) != -1) {
				// 转模式
				buffer.flip();
				// 写出操作
				outchannel.read(buffer);
				// 清空缓冲区
				buffer.clear();
			}

			// 关闭资源
			outchannel.close();
			inchannel.close();
			fos.flush();
			fos.close();
			fis.close();
			System.out.println("文件写出成功");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	//使用直接缓冲区
	@Test
	void channel03() {
		try {
			// 创建io流
			FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
			FileOutputStream fos = new FileOutputStream(new File("d:\\newhello.txt"),true);
			// 创建通道
			FileChannel inchannel = fis.getChannel();
			FileChannel outchannel = fos.getChannel();
			// 创建一个缓冲区,用来存放数据
			ByteBuffer buffer = ByteBuffer.allocate(5);// 该缓冲区的大小=文件大小
			while (inchannel.read(buffer) != -1) {
				// 转模式
				buffer.flip();
				// 写出操作
				outchannel.read(buffer);
				// 清空缓冲区
				buffer.clear();
			}

			// 关闭资源
			outchannel.close();
			inchannel.close();
			fos.flush();
			fos.close();
			fis.close();
			System.out.println("文件写出成功");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	
	//通道io文件拷贝
	@Test
	void channel04() {
		//缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(100);
		//读取数据到io流,保存到buffer中
		try {
			FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
			FileChannel inchannel = fis.getChannel();
			inchannel.read(buffer);
			buffer.flip();
			//从buffer中读取数据,写出到目标文件中
			FileOutputStream fos = new FileOutputStream(new File("hello.txt"));
			FileChannel outchannel = fos.getChannel();
			outchannel.write(buffer);
			//关闭资源
			inchannel.close();
			outchannel.close();
			System.out.println("文件写出成功");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

JDK1.7以后增加的方法

  1. abstract MappedByteBuffer
    map(FileChannel.MapMode mode, long position, long size) 将此通道的文件的区域映射到内存中。
    在这里插入图片描述

  2. static FileChannel
    open(Path path, OpenOption… options) 打开或创建一个文件,返回一个文件通道来访问该文件。

  • path -打开或创建的文件的路径
  • options选项指定如何打开文件
  1. static FileChannel
    open(Path path, Set<? extends OpenOption> options, FileAttribute<?>… attrs) 打开或创建一个文件,返回一个文件通道来访问该文件。
	void channel02() {
		//源文件,读源文件,设置读取权限
		try {
			FileChannel inChannel = FileChannel.open(Paths.get("d:\\hello.txt"), StandardOpenOption.READ);
			//创建文件,先读取该文件在写出去
			FileChannel outChannel = FileChannel.open(Paths.get("d:\\newhello.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
			//创建内存映射文件MappedByteBuffer
			MappedByteBuffer inmap = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
			MappedByteBuffer outmap = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
			
			//直接缓冲区
			ByteBuffer buffer = ByteBuffer.allocateDirect(inmap.limit());
			byte[] b=new byte[inmap.limit()];
			//通过直接缓冲区进行文件操作
			//将源文件通过物理内存映射文件写入到缓冲区中
			//将缓冲区中的数据要通过物理内存映射文件,将数据写出到目标文件
			inmap.get(b);
			outmap.get(b);
			//关闭通道
			inChannel.close();
			outChannel.close();
			System.out.println("文件拷贝成功");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

3.通道的操作

创建channel的方式三种:

  • Io的getchannel方法,非直接缓冲区,需要缓冲区Buffer.
  • Filechannel的open,直接缓冲区,不需要buffer.
  • Files 工具类的 newByteChannel()

(1)通道与通道之间的数据传输

  • 要使用FileChannel,只有1.7以后才能直接通过 channel来进行文件的拷贝操作.
	@Test
	void ChannerDemo() {
		// 创建一个通道
		try {
			FileInputStream fis = new FileInputStream(new File("channel.txt"));
			System.out.println(fis.available());
			// 创建一个通道
			FileChannel inchannel = fis.getChannel();
			System.out.println(inchannel.size());

			FileOutputStream fos = new FileOutputStream(new File("D:\\java笔记\\图片\\slxy2.jpg"));
			// 创建一个写通道
			FileChannel outchannel = fos.getChannel();
			System.out.println(outchannel.size());
			// 其他属性
			System.out.println(inchannel.position());
			// 将inchannel中的数据转到outchannel中
			inchannel.transferTo(0, inchannel.size(), outchannel);
			System.out.println(outchannel.size());
			System.out.println(inchannel.size());

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	//可以通过channel将数据快速复制到其他channel中
	@Test
	void ChannerDemo01() {
		
		try {
			//通道一
			FileChannel inChannel=FileChannel.open(Paths.get("D:\\图片\\德云社06.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
			//通道二
			FileChannel outChannel=FileChannel.open(Paths.get("D:\\图片\\德云社06.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
			System.out.println("交互之前"+inChannel.size());
			//数据交互
			inChannel.transferFrom(outChannel, 0, outChannel.size());//out-->in
			outChannel.transferTo(0, outChannel.size(), inChannel);//in-->out
			System.out.println("交互之后"+inChannel.size());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Test
	void ChannerDemo02() {
		try {
		//通道一
		FileChannel inChannel=FileChannel.open(Paths.get("D:\\图片\\德云社05.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
		//通道二
		FileChannel outChannel=FileChannel.open(Paths.get("D:\\图片\\德云社08.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
		
		outChannel.transferTo(0, inChannel.size(),outChannel);
		System.out.println("拷贝成功");
		
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	}

(2)分散和聚合数据操作

  • 多个缓冲区与一个通道的操作

在这里插入图片描述

//将通道中的数据保存到多个缓冲区中
	@Test
	void channelDemo() {
		//创建通道
		try {
			FileInputStream fis=new FileInputStream(new File("D:\\hello.txt"));
			FileChannel channel=fis.getChannel();
			//创建缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(7);
			 ByteBuffer buffer01 = ByteBuffer.allocate(6);
			 ByteBuffer[] bbf=new ByteBuffer[2];
			 bbf[0]=buffer;
			 bbf[1]=buffer01;
			 //将数据写入到缓冲区中
			 channel.read(bbf);
			 System.out.println(buffer.position());
			 System.out.println(buffer01.position());
			 //获取所有缓冲区中的数据
			 for (ByteBuffer byteBuffer : bbf) {
				byteBuffer.flip();
				System.out.println(byteBuffer.array());
			}
			 
			 //获取Buffer中的数据打印到控制台
			 buffer01.flip();
			 byte[] b=new byte[buffer01.limit()];
			 buffer01.get(b);
			 System.out.println(new String(b));
			 byte[] b1=buffer01.array();
			 System.out.println(new String(b1));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Test
	void channelDemo01() {
		//创建缓冲区
		ByteBuffer allocate01 = ByteBuffer.allocate(5);
		ByteBuffer allocate02 = ByteBuffer.allocate(5);
		ByteBuffer allocate03 = ByteBuffer.allocate(5);
		//添加数据
		allocate01.put("12345".getBytes());
		allocate02.put("lucky".getBytes());
		allocate03.put("64486".getBytes());
		ByteBuffer[] bbf=new ByteBuffer[3];
		bbf[0]=allocate01;
		bbf[1]=allocate02;
		bbf[2]=allocate03;
		//转为写入模式
		for (ByteBuffer byteBuffer : bbf) {
			byteBuffer.flip();
		}
		//创建写入通道
		try {
			FileOutputStream fos=new FileOutputStream(new File("D:\\hello.txt"));
			FileChannel channel = fos.getChannel();
			channel.write(bbf);
			
			//在关闭资源之前进行非null判断
			if(channel!=null) {
				channel.close();
			}
			fos.flush();
			fos.close();
			System.out.println("写出成功");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

4.随机读写流RandomAccessFile

  • 支持对随机存取文件的读取和写入

对文件来说

含义
“r”以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
“rw”打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
“rws”打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到基础存储设备。
“rwd”打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到基础存储设备。
  • 读取文件内容
@Test
	void randomRead() {
	
		try {
			//创建随机读写流
			RandomAccessFile raf=new RandomAccessFile(new File("D:\\java笔记\\newhello.txt"),"rw");
			//文件长度
			System.out.println(raf.length());
			//获取指针位置
			System.out.println("初始指针位置"+raf.getFilePointer());
			//设置指针位置
			raf.seek(5);
			System.out.println("修改后指针位置"+raf.getFilePointer());
			//读取数据
			byte[] bytes=new byte[(int)raf.length()];
			raf.read(bytes);
			System.out.println(new String(bytes));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}@Test
	void randomWrite() {
		//创建随机读写流
		try {
			RandomAccessFile raf=new RandomAccessFile(new File("hello.txt"),"rw");
			String mess="1234567890";
			raf.seek(4);
			raf.write(mess.getBytes());
			raf.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

5.字符编码设置

//java中,使用Charset类表示表示编码对象
	@Test
	void character() {
		//获取所有编码
		Map<String,Charset> charsets = Charset.availableCharsets();
		
		Set<Entry<String, Charset>> entrySet = charsets.entrySet();
		
		for (Entry<String, Charset> entry : entrySet) {
			System.out.println(entry.getKey()+"-----"+entry.getValue().name());
		}
	}
	
	
	//java进行编码转换
	@Test
	void character01() {
		String mess="未来,你好!!!";
		try {
			byte[] bytes=mess.getBytes("GBK");//将内容使用其他编码进行转换(编码)
			System.out.println(new String(bytes,"GBK"));//解码
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}//
	}

NIO处理编码的

  • 使用编码器和解码器的方式
  • 创建charset对象,指定需要使用的编码对象
  • 创建编码器和解码器,通过编码对象
  • 通过编码器或者缓冲区,解码是使用缓冲区来进行读取数据
	@Test
	void  charset02() {     
		// 编码器和解码器  使用的是同一charset得到的 
		System.out.println("================正常编码和解码 ===================");
		Charset cs = Charset.forName("GBK");//编码对象 
		System.out.println(cs.name());
		//创建编码器 
		CharsetEncoder encoder = cs.newEncoder();
		//创建解码器   
		CharsetDecoder decoder = cs.newDecoder();
		//创建字符缓冲区   用来存放  内容
		CharBuffer cbuff = CharBuffer.allocate(10);
		cbuff.put("世界美好与你环环相扣");
		cbuff.flip();
		try {
			// 编码,获取字符缓冲区中的数据进行重新编码,得到一个字节缓冲区  
			ByteBuffer buff = encoder.encode(cbuff);//buff编码是GBK
			
//			System.out.println(new String(buff.array(),"GBK"));
			// 依次获取 buff中的元素    
			for (int i = 0; i < 12; i++) {
				  System.out.println(buff.get());
			}
			// 解码  
			buff.flip();
			buff.limit(12);// 设置limit 
			System.out.println(buff.position());
			System.out.println(buff.limit());
			CharBuffer bf = decoder.decode(buff);
			System.out.println(bf.position());
			System.out.println(bf.limit());
			System.out.println("===="+bf.toString());
			
			System.out.println("--------------------非正常解码 -----------------");	
			//解码器和编码器使用的不是同一个charset 
			Charset ucs = Charset.forName("utf-8");// 12  
			buff.flip();
//			buff.limit(12);// 设置limit 
			System.out.println(buff.position());
			System.out.println(buff.limit());
			CharBuffer decode = ucs.decode(buff);
			System.out.println(decode.position());
			System.out.println(decode.limit());
			System.out.println("不同解码器进行解码:"+decode.toString());
		} catch (CharacterCodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值