Java基础之119 NIO基本概念与缓冲区 120 通道文件操作与内存映射 121 Path接口与Files工具类

本文探讨了Java NIO的引入原因与优势,详细解释了NIO如何通过块处理和缓冲区提升I/O效率,对比了流式与块式I/O的不同。同时,文章深入介绍了ByteBuffer和其他类型的缓冲区,以及通道(Channel)的概念。最后,通过实例展示了如何使用NIO和内存映射文件技术进行高效文件操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

New IO

1、为什么要使用 NIO?
NIO是JDK1.4加入的新包,
NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。
NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,
因而可以极大地提高速度。

流与块的比较

原来的 I/O 库(在 java.io.*中) 与 NIO 最重要的区别是数据打包和传输的方式,
原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,
一个输出流消费一个字节的数据。不利的一面是,面向流的 I/O 通常相当慢。
一个 面向块 的 I/O 系统以块的形式处理数据。
每一个操作都在一步中产生或者消费一个数据块。
按块处理数据比按(流式的)字节处理数据要快得多。
但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。

缓冲区

在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。
在写入数据时,它是写入到缓冲区中的。
任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。
但是一个缓冲区不 仅仅 是一个数组。
缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

缓冲区类型

最常用的缓冲区类型是 ByteBuffer。
一个 ByteBuffer 可以在其底层字节数组上进行 get/set 操作(即字节的获取和设置)。ByteBuffer 不是 NIO 中唯一的缓冲区类型。
事实上,对于每一种基本 Java 类型都有一种缓冲区类型:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer

缓冲区内部细节:

状态变量

可以用三个值指定缓冲区在任意时刻的状态:
position limit capacity
在这里插入图片描述

刚开始
在这里插入图片描述
反转的函数内部实现
在这里插入图片描述
结果

在这里插入图片描述
为什么反转呢 因为最后取值的时候 是从position到limit取值
因为申请了8个字节 只存了4个字节数据
所以存的时候不能使用缓存区全部的8个字节 只需要存了数据的前4个字节

hasRemaining 是否有有元素
在这里插入图片描述
remaining 元素个数
在这里插入图片描述

在这里插入图片描述

package com.nio;

import java.nio.ByteBuffer;

public class NIODemo {

	public static void main(String[] args) {
		//创建 一个字节缓冲区,申请内存空间为8字节
		ByteBuffer buf = ByteBuffer.allocate(8);
		
		System.out.println("position="+buf.position());
		System.out.println("limit="+buf.limit());
		System.out.println("capacity="+buf.capacity());
		
		System.out.println("---------");
		
		//向缓冲区中写入数据
		buf.put((byte)10);
		buf.put((byte)20);
		buf.put((byte)30);
		buf.put((byte)40);
		System.out.println("position="+buf.position());
		System.out.println("limit="+buf.limit());
		System.out.println("capacity="+buf.capacity());
		
		//缓冲区反转
		buf.flip();
		System.out.println("-----------");
		System.out.println("position="+buf.position());
		System.out.println("limit="+buf.limit());
		System.out.println("capacity="+buf.capacity());
		//告知在当前位置和限制之间是否有元素。
		if(buf.hasRemaining()){
			//返回当前位置与限制之间的元素数。
			 for(int i=0;i<buf.remaining();i++){
				 byte b = buf.get(i);
				 System.out.println(b);
			 }
		}
		System.out.println("-----------");
		System.out.println("position="+buf.position());
		System.out.println("limit="+buf.limit());
		System.out.println("capacity="+buf.capacity());
	}

}

通道: Channel

Channel 是一个对象,可以通过它读取和写入数据。
拿 NIO 与原来的 I/O 做个比较,通道就像是流。
正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。
同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,
再从缓冲区获取这个字节。
在这里插入图片描述

*** 比较IO操作的性能比较排名:

  • 1、内存映射最快
  • 2、NIO读写文件
  • 3、使用了缓存的IO流
  • 4、无缓存的IO流**

getChannel 获取一个文件通道
在这里插入图片描述
在这里插入图片描述
flip过了 反转了 那个长度就知道了 write就不用写 0,len了

输入流的映射 映射到缓冲区
在这里插入图片描述

使用通道读写文件示例

下面的函数

通过文件通道实现文件的复制CopyFile()
文件的映射randomAccessFileCopy()

用RandomAccessFile类获取通道
通过两个通道之间 获取它们的缓冲区 把inBuf缓冲区的内容 放到outBuf缓冲区里面去
完了一关闭 就会映射到文件里面去

package com.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

/**
 * 比较IO操作的性能比较:
 * 1、内存映射最快
 * 2、NIO读写文件
 * 3、使用了缓存的IO流
 * 4、无缓存的IO流
 * 
 * @author vince
 * @description
 */
public class CopyFileDemo {
	
	
	private static void randomAccessFileCopy() throws Exception{
		RandomAccessFile in = new RandomAccessFile("c:\\3D0.jpg","r");
		RandomAccessFile out = new RandomAccessFile("c:\\test\\3D0.jpg","rw");
		
		FileChannel fcIn = in.getChannel();
		FileChannel fcOut = out.getChannel();
		
		long size = fcIn.size();//输入流的字节大小
		//输入流的缓冲区
		MappedByteBuffer inBuf = fcIn.map(MapMode.READ_ONLY, 0, size);
		//输出流的缓冲区
		MappedByteBuffer outBuf = fcOut.map(MapMode.READ_WRITE, 0, size);
		
		for(int i=0;i<size;i++){
			outBuf.put(inBuf.get());
		}
		
		//关闭(关闭通道时会写入数据块)
		fcIn.close();
		fcOut.close();
		in.close();
		out.close();
		System.out.println("copy success");
	}
	
	/**
	 * 通过文件通道实现文件的复制
	 * @throws Exception
	 */
	private static void copyFile() throws Exception{
		//创建一个输入文件的通道
		FileChannel fcIn = new FileInputStream("c:\\3D0.jpg").getChannel();
		//创建一个输出文件的通道
		FileChannel fcOut = new FileOutputStream("c:\\test\\3D0.jpg").getChannel();
		
		ByteBuffer buf = ByteBuffer.allocate(1024);
		while(fcIn.read(buf)!=-1){
			buf.flip();
			fcOut.write(buf);
			buf.clear();
		}
		fcIn.close();
		fcOut.close();
		System.out.println("copy success.");
	}

	public static void main(String[] args) {
		try {
//			copyFile();
			randomAccessFileCopy();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

Files工具类

对文件的操作 用新特性比较方便 NewIO

1、读写文件

static path write(Path path, byte[] bytes, OpenOption… options) 写入文件
static byte[] readAllBytes(Path path) 读取文件中的所有字节。

2、复制、剪切、删除

static path copy(Path source, Path target, CopyOption… options)
static path move(Path source, Path target, CopyOption… options)
static void delete(Path path) //如果path不存在文件将抛出异常,此时调用下面的比较好
static boolean deleteIfExists(Path path)

3、创建文件和目录

//创建新目录,除了最后一个部件,其他必须是已存在的
Files.createDirectory(path);
//创建路径中的中间目录,能创建不存在的中间部件
Files.createDirectories(path);
//创建一个空文件,检查文件存在,如果已存在则抛出异常而检查文件存在是原子性的,
//因此在此过程中无法执行文件创建操作
Files.createFile(path);
//添加前/后缀创建临时文件或临时目录
Path newPath = Files.createTempFile(dir, prefix, suffix);
Path newPath = Files.createTempDirectory(dir, prefix);

package com.nio;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;

/**
 * JDK1.7新的文件操作类 Path接口 Paths类 Files类
 * 
 * @author vince
 * @description
 */
public class PathFilesDemo {

	public static void main(String[] args) {

		File file = new File("c:\\test\\3D0.jpg");

		// path
		Path p1 = Paths.get("c:\\test", "3D0.jpg");
		System.out.println(p1);

		Path p2 = file.toPath();
		System.out.println(p2);

		Path p3 = FileSystems.getDefault().getPath("c:\\test", "3D0.jpg");

		// Files工具类
		Path p4 = Paths.get("c:\\test\\vince.txt");
		String info = "村花到我家,一起...";
		// try {
		// //写入文件
		// Files.write(p4, info.getBytes("gb2312"),StandardOpenOption.APPEND);
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
		// 读取文件
		try {
			byte[] bytes = Files.readAllBytes(p4);
			System.out.println(new String(bytes));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 复制文件
		// try {
		// Files.copy(p3, Paths.get("c:\\3D0.jpg"),
		// StandardCopyOption.REPLACE_EXISTING);
		// } catch (IOException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }

		// 移动文件
		// try {
		// Files.move(p3,
		// Paths.get("c:\\3D0.jpg"),StandardCopyOption.REPLACE_EXISTING);
		// } catch (IOException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }

		//删除文件
//		try {
//			Files.delete(p3);//static boolean deleteIfExists(Path path) 
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
		//创建新目录,除了最后一个部件,其他必须是已存在的
//		try {
//			Files.createDirectory(Paths.get("c:\\BB"));
//			//Files.createDirectories(path); 创建多级不存在的目录
//		} catch (IOException e) {
//			e.printStackTrace();
//		} 

		//创建文件
		try {
			Files.createFile(Paths.get("c:\\BB.txt"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//添加前/后缀创建临时文件或临时目录
//		Path newPath = Files.createTempFile(dir, prefix, suffix);
//		Path newPath = Files.createTempDirectory(dir, prefix);

	}

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值