文章目录
NIO
Java NIO ( New IO )是从 Java 1.4 版本开始引入的一个新的 IO API , 可以替代标准的 Java IO API 。NIO 与原来的 IO 有同样的作用和目的,但是使用的方式完全不同, NIO 支持面向缓冲区的、基于通道的 IO 操作。 NIO 将以更加高效的方式进行文件的读写操作。
一、缓冲区(Buffer)
1、Buffer概述
缓冲区( Buffer ):一个用于特定基本数据类型的容器,底层由数组实现,用来存储数据。
非直接缓冲区:将缓冲区建立在JVM的内存中
直接缓冲区:将缓冲区建立在物理内存中
根据数据类型不同 (boolean 除外 ) ,有以下 Buffer子类:
ByteBuffer(最常用)
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
这些类都采用相似的方法进行管理数据,只是各自管理的数据类型不同。
XxxBuffer xxxBuffer=XxxBuffer.allocate(int capacity)//创建一个容量为capacity的非直接缓冲区
XxxBuffer xxxBuffer1 = XxxBuffer.allocateDirect(int capacity);//创建一个容量为capacity的直接缓冲区
2、缓冲区基本属性
容量 (capacity) : 表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创建后不能更改。
限制 (limit) : 第一个不能读取或写入的数据的索引,即位于 limit 后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。
位置 (position) : 下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制
标记 (mark) 与 重置 (reset) : 标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position ,之后可以通过调用 reset() 方法恢复到这个 position.
标记、位置、限制、容量遵守以下不变式:
0 <= mark <= position <= limit <= capacity
3、缓冲区常用方法
put(byte b) : 将给定单个字节写入缓冲区的当前位置
put(byte[] src) :将 src 中的字节写入缓冲区的当前位置
put(int index, byte b) :将指定字节写入缓冲区的索引位置 ( 不会移动 position)
get() : 读取缓冲区中的数据
get(byte[] dst) :批量读取多个字节到 dst 中
get(int index) :读取指定索引位置的字节 ( 不会移动 position)
flip(); 切换读取数据模式
rewind() : 可重复读
clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,只是将缓冲区所有基本属性回到初始状态
mark() : 标记一个位置
reset():回到上次标记位置
hasRemaining();//判断是否还有下一个可读元素
remaining();//获取还有几个元素可读
4、演示
import java.nio.ByteBuffer;
public class Blog {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(10);//创建一个容量为10的缓冲区
int capacity = byteBuffer.capacity();//获取容量
System.out.println(capacity);//10
int position = byteBuffer.position();//获取当前指针位置
System.out.println(position);//0
int limit = byteBuffer.limit();//获取当前限制位置
System.out.println(limit);//10
byteBuffer.put("abcde".getBytes());//存入数据
int position1 = byteBuffer.position();//获取当前指针位置
System.out.println(position1);//5
byteBuffer.flip();//切换到读取模式
int position2 = byteBuffer.position();//获取当前指针位置
System.out.println(position2);//0
int limit1 = byteBuffer.limit();//获取当前限制位置
System.out.println(limit1);//5
byteBuffer.mark();//标记当前指针位置
//读取数据
while (byteBuffer.hasRemaining()){
byte b = byteBuffer.get();//获取数据
System.out.println(b);//91~101
}
byteBuffer.reset();//让指针回到上次标记位置
//重复读取数据
while (byteBuffer.hasRemaining()){
byte b = byteBuffer.get();//获取数据
System.out.println(b);//91~101
}
byteBuffer.rewind();//重绕此缓冲区,将位置设为为0,取消设置的mark,可实现重新读取
}
}
二、通道(Channel)
1、Channel概述
Java 为 Channel 接口提供的最主要实现类如下:
本地文件传输通道:
FileChannel :用于读取、写入、映射和操作文件的通道
网络数据传输的通道:
DatagramChannel :通过 UDP 读写网络中的数据通道
SocketChannel :通过 TCP 读写网络中的数据。
ServerSocketChannel :可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个SocketChannel
2、获取通道
方式一:使用支持通道的类的对象调用getChannel() 方法
支持通道的类:
本地I/O:
FileInputStream:
FileInputStream fileInputStream = new FileInputStream("MyTest.java");
FileChannel channel = fileInputStream.getChannel();
FileOutputStream:
FileOutputStream fileOutputStream = new FileOutputStream("MyTest.java");
FileChannel channel1 = fileInputStream.getChannel();
RandomAccessFile:
RandomAccessFile randomAccessFile = new RandomAccessFile("MyTest.java", "rw");
FileChannel channel2 = randomAccessFile.getChannel();
网络 I/O:
DatagramSocket
Socket
ServerSocket
方式二:使用 Files 类的静态方法newByteChannel() 获取字节通道
SeekableByteChannel inChannel = Files.newByteChannel(Paths.get("MyTest.java"), StandardOpenOption.READ);
SeekableByteChannel outChannel = Files.newByteChannel(Paths.get("MyTest2.java"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//模式:
//StandardOpenOption.READ 可读
//StandardOpenOption.WRITE 可写
//StandardOpenOption.CREATE 文件不存在就创建,存在就覆盖
//StandardOpenOption.CREATE_NEW 文件不存在就创建,存在就报错
方式三:通道的静态方法 open() 打开并返回指定通道
FileChannel inChannel = FileChannel.open(Paths.get("MyTest.java"),StandardOpenOption.READ);
FileChannel outChannl = FileChannel.open(Paths.get("MyTest2.java"),StandardOpenOption.WRITE, StandardOpenOption.CREATE);
3、通道的数据传输
将 Buffer 中数据写入 Channel: inChannel.read(byteBuffer)
从 Channel 读取数据到 Buffer: outChannel.write(byteBuffer);
4、NIO复制文件
A:使用非直接缓冲区复制文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class CopyFile1 {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("MyTest.java");
FileOutputStream out = new FileOutputStream("MyTest3.java");
//获取通道
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
//创建非直接缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 8);
//将通道数据放进缓冲区
while ((inChannel.read(byteBuffer))!=-1){
byteBuffer.flip();//切换到读取模式
outChannel.write(byteBuffer);//将缓冲区数据写入通道
byteBuffer.clear();//清空缓冲区
}
//释放资源
in.close();
out.close();
inChannel.close();
outChannel.close();
}
}
B:使用直接缓冲区复制文件
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class CopyFile2 {
public static void main(String[] args) throws IOException {
//获取通道
FileChannel inChannel = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("MyTest4.java"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//操作内存映射文件
MappedByteBuffer inByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//直接使用缓冲区读写文件
byte[] bytes = new byte[inByteBuffer.limit()];
inByteBuffer.get(bytes);
outByteBuffer.put(bytes);
//释放资源
inChannel.close();
outChannel.close();
}
}
5、通道之间的数据传输
/*通道之间的数据传输,使用直接缓冲区的方式
* transferFrom()
* transferTo()*/
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class MyTest {
public static void main(String[] args) throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("MyTest5.java"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//以下两种数据传输方式任选一种即可
//将inChannel复制到outChannel
inChannel.transferTo(0, inChannel.size(), outChannel);
//outChannel的数据来自inChannel
//outChannel.transferFrom(inChannel, 0, inChannel.size());
}
}
三、Files 类常用方法
1、复制文件的方法
static long copy(InputStream in, Path target, CopyOption... options)
//将所有字节从输入流复制到文件。
Files.copy(new FileInputStream("demo.txt"), Paths.get("demo55.txt"), StandardCopyOption.REPLACE_EXISTING);
static long copy(Path source, OutputStream out)
//将从文件到输出流的所有字节复制到输出流中。
Files.copy(Paths.get("demo55.txt"),new FileOutputStream("demo66.txt"));
static Path copy(Path source, Path target, CopyOption... options)
//将一个文件复制到目标文件。
Files.copy(Paths.get("demo.txt"), Paths.get("demo77.txt"), StandardCopyOption.REPLACE_EXISTING);
//StandardCopyOption.REPLACE_EXISTING 可选参数,复制文件,如果文件存在就覆盖
2、其他方法
Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录
Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件
void delete(Path path) : 删除一个文件
Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
ong size(Path path) : 返回 path 指定文件的大小
static Path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options) 可以将List集合中的数据写到文件中
Files用于判断的方法:
boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
boolean isExecutable(Path path) : 判断是否是可执行文件
boolean isHidden(Path path) : 判断是否是隐藏文件
boolean isReadable(Path path) : 判断文件是否可读
boolean isWritable(Path path) : 判断文件是否可写
boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
Files用于操作内容的方法:
SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
DirectoryStream newDirectoryStream(Path path) : 打开 path 指定的目录
InputStream newInputStream(Path path, OpenOption…how): 获取 InputStream 对象
OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象
四、Path 与 Paths
java.nio.file.Path 接口代表一个平台无关的平台路径,描述了目录结构中文件的位置。
Paths 提供的get()方法用来获取Path对象
Path get(String first,String… more): 用于将多个字符串串连成路径
Path 常用方法:
boolean endsWith(String path) : 判断是否以 path 路径结束
boolean startsWith(String path) : 判断是否以 path 路径开始
boolean isAbsolute() : 判断是否是绝对路径
Path getFileName() : 返回与调用 Path 对象关联的文件名
Path getName(int idx) : 返回的指定索引位置 idx 的路径名称
int getNameCount() : 返回 Path 根目录后面元素的数量
Path getParent() :返回 Path 对象包含整个路径,不包含 Path 对象指定的文件路径
Path getRoot() :返回调用 Path 对象的根路径
Path resolve(Path p) : 将相对路径解析为绝对路径
Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
String toString() : 返回调用 Path 对象的字符串表示形式