AIO (Asynchronous I/O) 异步非阻塞I/O 是 Java 1.7 之后的,,在java.nio包,AIO是是 NIO 的升级版本(所以AIO又叫NIO.2),提供了异步非堵塞的 IO 操作方式,异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
AIO和NIO相比,同样有Channel和Buffer,但没有Selector。
Paths,Path与Files是在JDK7中新添加进来的,都在 java.nio.file包下面,使用时注意JDK版本
一、Paths类和Path接口1、java.nio.file.Paths类
作用:由专门的静态方法返回一个 Path对象,通过转换路径字符串或 URI。
Paths类是 final修饰的,仅由两个static方法组成。
-
-
static Path
get(String first, String... more)
将路径字符串或连接到路径字符串的字符串序列转换为
Path
。static Path
get(URI uri)
将给定的URI转换为
Path
对象。
-
注意:文件系统的分隔符(Unix文件系统是 / ,Windows是 \,/也可以 )
路径中的第一个部件可以是根部件,例如 / 或 C:\ 。以根部件开始的是绝对路径;否则就是相对路径。
@Test
public void testPaths() throws URISyntaxException {
Path path = Paths.get("E:/java");
System.out.println(path); // E:\java
Path path2 = Paths.get("E:/java","/赵云.jpg");
System.out.println(path2);// E:\java\赵云.jpg
URI uri = new URI("file:///E:/java/赵云.jpg");
Path path3 = Paths.get(uri);
System.out.println(path3); //E:\java\赵云.jpg
}
2、java.nio.file.Path接口
Path实例表示文件系统中的路径。路径可以指向一个文件或目录。它通常表示系统相关的文件路径,可用于在文件系统中定位文件的对象。
在许多情况下,Path接口可以取代File类的使用。Path类包含一些对文件或目录处理的方法,但是和File类处理上也有一些区别。传送门:Java中 File类(文件、目录和文件过滤器)
1)判断路径以某一分层路径的开始或结束
-
-
boolean
endsWith(Path other)
测试此路径是否以给定的路径结束。
boolean
endsWith(String other)
测试此路径是否以
Path
结束,通过转换给定的路径字符串,完全按照endsWith(Path)
方法指定的方式构建。boolean
startsWith(Path other)
测试此路径是否以给定的路径开始。
boolean
startsWith(String other)
测试此路径是否以
Path
,通过转换给定的路径字符串,按照startsWith(Path)
方法指定的方式构建。
-
@Test
public void testPaths() throws URISyntaxException {
Path path = Paths.get("E:/java","/赵云.jpg");
System.out.println(path);// E:\java\赵云.jpg
System.out.println(path.endsWith("jpg"));//false 以某一分层路径
System.out.println(path.endsWith("赵云.jpg"));//true
System.out.println(path.startsWith(Paths.get("E:/java")));// true
}
2)File 和 Path之间的转换,File和URI之间的转换
-
-
File
toFile()
返回表示此路径的
File
对象。URI
toUri()
返回一个URI来表示此路径。
-
@Test
public void testPaths() throws URISyntaxException {
Path path = Paths.get("E:/java","/赵云.jpg");
File file = path.toFile();
URI uri = path.toUri();
Path path1 = file.toPath();
URI uri1 = file.toURI();
}
3)其他的一些方法
-
-
Path
getParent()
返回 父路径 ,或
null
如果此路径没有父。Path
getRoot()
返回此路径的根组分作为
Path
对象,或null
如果该路径不具有根组件。int
compareTo(Path other)
比较两个抽象路径字典。
boolean
endsWith(Path other)
测试,如果这个路径结束与给定的路径。
Path
getFileName()
将此路径表示的文件或目录的名称返回为
Path
对象。FileSystem
getFileSystem()
返回创建此对象的文件系统。
boolean
isAbsolute()
告诉这条路是否是绝对的。
Iterator
iterator()
返回此路径的名称元素的迭代器。
Path
toRealPath(LinkOption... options)
返回现有文件的 真实路径。
-
二、java.nio.file.Files类
Files类提供了对文件、目录或者其他类型文件进行操作的静态方法。例如文件复制,移动,删除,读取文件内容,写入文件内容等。
Files类与Path实例一起配合使用。下面介绍一些常用的方法:
1、文件、目录判断与创建,删除
-
-
static boolean
exists(Path path, LinkOption... options)
测试一个文件是否存在。
static boolean
notExists(Path path, LinkOption... options)
测试位于此路径的文件是否不存在。
static long
size(Path path)
返回一个文件的大小(以字节为单位)。
static Path
createDirectories(Path dir, FileAttribute... attrs)
创建一个目录,通过创建所有不存在的父目录。
static Path
createDirectory(Path dir, FileAttribute... attrs)
创建一个新目录。
static Path
createFile(Path path, FileAttribute... attrs)
创建一个新的和空的文件,如果文件已经存在,失败了。
static Path
createLink(Path link, Path existing)
创建一个新的链接(目录项)为现有的文件(可选操作)。
static void
delete(Path path)
删除一个文件。
static boolean
deleteIfExists(Path path)
如果它存在的话,删除一个文件。
static boolean
isDirectory(Path path, LinkOption... options)
测试一个文件是否是一个目录。
static boolean
isExecutable(Path path)
测试一个文件是否可执行。
static boolean
isHidden(Path path)
告诉是否一个文件被认为是隐藏的。
static boolean
isReadable(Path path)
测试一个文件是否可读。
static boolean
isWritable(Path path)
测试文件是否可写。
-
java.nio.file.LinkOption枚举类定义如何处理符号链接的选项,但是只有一个字段NOFOLLOW_LINKS(不遵循符号链接),所以固定写法。
注意:
新文件创建:
如果该新文件已经存在,则会抛出java.nio.file.FileAlreadyExistsException。
如果该新文件的父目录不存在或者其他I/O错误,则会抛出IOException。
目录创建使用 createDirectories(),父目录不存在也会创建。
文件删除:
NoSuchFileException -如果文件不存在(可选特定异常)
DirectoryNotEmptyException -如果文件是一个目录,否则不能被删除,因为目录不是空的(可选特定异常)
目录删除:delete()方法只会删除一个文件和空目录。目录不为空时,抛异常。
public static void main(String[] args) throws IOException {
Path path = Paths.get("E:/java000/AIO/aa");
if (Files.notExists(path)) {
Files.createDirectories(path);
}
System.out.println(Files.isDirectory(path)); // true
Path path2 = Paths.get("E:/java000/AIO/aa/text000.txt");
if (Files.notExists(path2, LinkOption.NOFOLLOW_LINKS)) {
if (!Files.isDirectory(path2)) {
Files.createFile(path2);
}
}
Files.deleteIfExists(path2);
Files.delete(path); // DirectoryNotEmptyException:
}
2、文件复制与移动
-
-
static long
copy(InputStream in, Path target, CopyOption... options)
将输入流中的所有字节复制到文件。
static long
copy(Path source, OutputStream out)
将文件中的所有字节复制到输出流。
static Path
copy(Path source, Path target, CopyOption... options)
将文件复制到目标文件。
static Path
move(Path source, Path target, CopyOption... options)
移动或重命名一个文件到目标文件。
-
@Test
public void testPaths() throws Exception {
Path path = Paths.get("E:/java","/赵云image.jpg");
FileInputStream inputStream = new FileInputStream(new File("E:/java/赵云.jpg"));
// REPLACE_EXISTING - 如果目标文件已经存在,则替换
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
Path destPath = Paths.get("E:/java/aa/aa赵云image.txt");
Files.move(path, destPath, StandardCopyOption.REPLACE_EXISTING);
}
注意:FileAlreadyExistsException -如果目标文件存在,并且没有指定 StandardCopyOption.REPLACE_EXISTING选项替换(可选特定异常)
3、遍历一个文件夹目录获取文件(不包含嵌套文件夹中文件):newDirectoryStream
-
-
static DirectoryStream
newDirectoryStream(Path dir)
打开一个目录,返回一个
DirectoryStream
以遍历目录中的所有条目。static DirectoryStream
newDirectoryStream(Path dir, DirectoryStream.Filter... attrs)
打开或创建一个用于读取和/或写入的文件,返回一个异步文件通道来访问该文件。
abstract Future
read(ByteBuffer dst, long position)
从这个通道读取一个字节序列到给定的缓冲区,从给定的文件位置开始。
abstract void
read(ByteBuffer dst, long position, A attachment, CompletionHandler handler)
从这个通道读取一个字节序列到给定的缓冲区,从给定的文件位置开始。
abstract Future
write(ByteBuffer src, long position)
从给定的缓冲区中从给定的缓冲区中写入一个字节序列,从给定的文件位置开始。
abstract void
write(ByteBuffer src, long position, A attachment, CompletionHandler handler)
从给定的缓冲区中从给定的缓冲区中写入一个字节序列,从给定的文件位置开始。
abstract long
size()
返回此通道文件的当前大小。
-
OpenOption参数是可选的,表示打开文件的方式 。如:StandardOpenOption.READ,StandardOpenOption.WRITE等。
2、读数据
方式一:通过 Future 读数据
AsynchronousFileChannel fileChannel = null;
try {
Path path = Paths.get("D:/E/AIO/aa/text.txt");
// 缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 1.创建AsynchronousFileChannel
fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
// 2.读数据
Future future = fileChannel.read(byteBuffer, 0);
// 在调用 read() 之后,循环调用 Future 的 isDone() 方法直到返回 true,则数据读完了。
while (!future.isDone()) {
}
// 3.写数据
System.out.println(new String(byteBuffer.array()));
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭资源
if (fileChannel != null) {
try {
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
read()接受一个ByteBuffer作为第一个参数,数据会被读取到ByteBuffer中。第二个参数是开始读取数据的位置。
read()方法会立刻返回,即使读操作没有完成。我们可以通过isDone()方法检查操作是否完成。一直循环,直到返回的isDone()方法返回true。循环操作比较低效,需要等到读取操作完成之后才会执行。
这里读取操作完成后,数据读取到ByteBuffer中,然后把缓冲区中的字符串打印。
方式二:通过 CompletionHandler 读数据
public static void main(String[] args) {
AsynchronousFileChannel fileChannel = null;
try {
Path path = Paths.get("D:/E/AIO/aa/text.txt");
// 缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 1.创建AsynchronousFileChannel
fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
// 2.读数据
fileChannel.read(byteBuffer, 0, byteBuffer, new CompletionHandler() {
/**
* 从通道中把数据读到缓冲区完成之后,会调用它
* @param result - 读取得字节数
* @param attachment
*/
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("result: " + result);
System.out.println(new String(attachment.array()));
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭资源
if (fileChannel != null) {
try {
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 给显示留一些时间,实际项目可以删除
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这里,一旦读取完成,将会触发CompletionHandler的completed()方法,并传入一个Integer和ByteBuffer。第一个参数Integer表示的是读取到的字节数大小。第二个参数ByteBuffer也可以换成其他合适的对象方便数据写入。 如果读取操作失败了,那么会触发failed()方法。
3、写数据
方式一:通过 Future 写数据
public static void main(String[] args) {
AsynchronousFileChannel fileChannel = null;
try {
Path path = Paths.get("D:/E/AIO/aa/text1.txt");
if(Files.notExists(path)){
Files.createFile(path);
}
// 缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("test data".getBytes());
byteBuffer.flip();
// 1.创建AsynchronousFileChannel
fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
// 2.写数据
Future future = fileChannel.write(byteBuffer, 0);
// isDone() 方法直到返回 true,则数据写完了。
while (!future.isDone()) {
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭资源
if (fileChannel != null) {
try {
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
首先把文件已写方式打开,然后把缓冲区的数据写入。最后检查一下是否写入完成。 注意:文件必须是已经存在的,否则会抛出一个java.nio.file.NoSuchFileException。
方式二:通过 CompletionHandler 写数据
public static void main(String[] args) {
AsynchronousFileChannel fileChannel = null;
try {
Path path = Paths.get("D:/E/AIO/aa/text1.txt");
if(Files.notExists(path)){
Files.createFile(path);
}
// 缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("test data123121".getBytes());
byteBuffer.flip();
// 1.创建AsynchronousFileChannel
fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
// 2.写数据
fileChannel.write(byteBuffer, 0, byteBuffer, new CompletionHandler() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("result: " + result);
System.out.println(new String(attachment.array()));
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭资源
if (fileChannel != null) {
try {
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 给显示留一些时间,实际项目可以删除
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
当数据写入完成后completed()会被调用,如果失败了那么failed()会被调用。
参考文章:异步文件通道 AsynchronousFileChannel
ends~