Java NIO.2的主要表现为增加了Files、Path、Paths等功能类和基于异步Channel的IO。
1、Path、Paths
如下为使用Path和工具类Paths的示例:
import java.io.*;
import java.net.*;
import java.nio.file.*;
public class Test
{
public static void main(String[] args)
throws Exception
{
// 以当前路径来创建Path对象
Path path = Paths.get(".");
// 获取path对应的绝对路径。
Path absolutePath = path.toAbsolutePath();
System.out.println(absolutePath); // “D:\java_project\Test\sources\.”
// 获取绝对路径的根路径
System.out.println("absolutePath的根路径:" + absolutePath.getRoot()); // “D:\”
// 获取绝对路径所包含的路径数量
int iCnt = absolutePath.getNameCount();
System.out.println("absolutePath里包含的路径数量:" + iCnt); // 4
for(int i = 0; i < iCnt; ++i)
System.out.println(absolutePath.getName(i));// “java_project”、“Test”、“sources”、“.”
// 以多个String来构建Path对象
Path path2 = Paths.get("g:" , "publish" , "codes");
System.out.println(path2); //“g:\publish\codes”
Path p1 = Paths.get("C:\\workspace");
Path p2 = Paths.get("Desktop\\books");
Path p3 = Paths.get(System.getProperty("user.home"), "Documents", "Downloads"); //获得用户目录下的Documents\Downloads目录
String str = p3.toString(); // "C:\Users\Administrator\Documents\Downloads"
p3.forEach(System.out::println); //Users,Administrator,Documents,Downloads
str = "" + p3.getFileName(); // "Downloads"
int n = p3.getNameCount(); //4
str = "" + p1.resolve(p3.getName(0)); //"C:\workspace\Users"
str = "" + p3.subpath(0, 2); //"Users\\Administrator"
str = "" + p3.getRoot(); //"C:\"
}
}
使用Path的register()可以监听该Path代表的目录下文件的变化。
2、BasicFileAttributes
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;
public class Test
{
public static void main(String[] args) throws IOException
{
//获得目录的属性
Path path = Paths.get("C:\\Windows");
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); //获得目录的所有属性
String str = "" + attrs.lastModifiedTime(); //上次修改时间,也可以直接通过Files.getLastModifiedTime()来获取
boolean b = attrs.isDirectory(); //是否是目录
b = attrs.isSymbolicLink(); //是否是符号链接
long size = attrs.size(); //大小
int uid = (Integer)Files.getAttribute(path, "unix:uid"); //获得指定属性
//获得文件的属性
Path path2 = Paths.get("C:\\Windows\\bootstat.dat");
attrs = Files.readAttributes(path2, BasicFileAttributes.class); //获得目录的所有属性
size = attrs.size();
//获得根目录路径
Iterable<Path> dirs = FileSystems.getDefault().getRootDirectories();
dirs.forEach(System.out::println); //输出 C:\,D:\,E:\,F:\
//获得各个盘的容量和使用情况
FileSystem fs = FileSystems.getDefault();
for(FileStore store:fs.getFileStores())
{
long total = store.getTotalSpace(); //总容量
long used = total - store.getUnallocatedSpace(); //已用空间
long usable = store.getUsableSpace(); //可用空间
System.out.println(String.format("%s, %dG, %dG, %dG", store.toString(), total / 1024 / 1024 / 1024, used / 1024 / 1024 / 1024, usable / 1024 / 1024 / 1024));
}
}
}
3、Files
Files是File对应的工具类,通过它不仅可以直接获得文件大小、复制文件等,它还可以直接来读取文件、写入文件, 以下为使用工具类Files的示例:
import java.nio.file.*;
import java.nio.charset.*;
import java.io.*;
import java.util.*;
public class FilesTest
{
public static void main(String[] args)
throws Exception
{
//判断文件是否存在
boolean b = Files.exists(Paths.get("FilesTest.java"));
//是否是目录
b = Files.isDirectory(Paths.get("FilesTest.java"));
// 复制文件
Files.copy(Paths.get("FilesTest.java"), new FileOutputStream("a.txt"));
Files.copy(Paths.get("D:\\TestU.java"), Paths.get("E:\\TestU.java"), StandardCopyOption.REPLACE_EXISTING); //复制文件,已存在则覆盖
//移动文件
Files.move(...);
//建立目录,
Files.createDirectory(); //如果父目录不存在会抛出异常
Files.createDirectories(); //如果父目录不存在则创建
//删除文件或目录
Path path = Paths.get("D:\\TestU.java");
Files.delete(path); //文件不存在或目录不为空会抛出异常
Files.deleteIfExists(path); //文件不存在不会抛出异常
// 判断FilesTest.java文件是否为隐藏文件/可执行文件
System.out.println("FilesTest.java是否为隐藏文件:" + Files.isHidden/*isExecutable*/(Paths.get("FilesTest.java")));
//一次性读取文件的所有数据
byte[] ary = Files.readAllBytes();
// 一次性读取字符文件FilesTest.java的所有行
List<String> lines = Files.readAllLines(Paths.get("FilesTest.java"), Charset.forName("gbk"));
System.out.println(lines);
// 获得指定文件的大小
System.out.println("FilesTest.java的大小为:"
+ Files.size(Paths.get("FilesTest.java")));
// 直接将多个字符串内容写入指定文件中
List<String> poem = new ArrayList<>();
poem.add("水晶潭底银鱼跃");
poem.add("清徐风中碧竿横");
Files.write(Paths.get("pome.txt") , poem, Charset.forName("gbk"));
// 使用Java 8新增的管线化(流式操作)列出当前目录下所有文件和子目录
// Files.list()列出当前的文件和目录,Files.walk()还会递归列出子目录中的文件
Files.list(Paths.get(".")).forEach(path -> System.out.println(path));
// 使用Java 8新增的Stream读取文件中每行内容
Files.lines(Paths.get("FilesTest.java") , Charset.forName("gbk")).forEach(line -> System.out.println(line));
// 获得C盘的总空间,可用空间
FileStore cStore = Files.getFileStore(Paths.get("C:"));
System.out.println("C:共有空间:" + cStore.getTotalSpace());
System.out.println("C:可用空间:" + cStore.getUsableSpace());
}
}
除了使用Files的list()外,还有传统的遍历目录的方法,如下所示,Files.newDirectoryStream()的第二个参数可以指定Glob比较范例来设置过滤的文件,如"*.{class, jar}"表示只获得.class或.jar文件,其它的Glob范例如下所示,如果Glob范例无法满足需求的话可以使用DirectoryStream.Filter接口的操作对象,操作其accept()方法来自行定义过滤条件,该方法返回true表示符合过滤条件。
import java.nio.file.*;
import java.io.IOException;
import java.util.*;
public class Test
{
public static void main(String[] args) throws IOException
{
//遍历D盘,先输出目录名再输出文件名
try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get("D:\\")/*, "*.{class, jar}"*/))
{
List<String> files = new ArrayList<>();
for(Path path : directoryStream)
{
if(Files.isDirectory(path))
System.out.println(path.getFileName());
else
files.add(path.getFileName().toString());
}
files.forEach(System.out::println);
}
}
}
使用Files的walkFileTree()方法遍历文件和目录会更加方便,它还可以指定遍历的深度,递归进行遍历,其参数类型FileVisitor代表一个文件访问器接口,当遍历的时候会触发其中相应的方法,必须实现这四个方法:
preVisitDirectory()为访问子目录之前触发。
postVisitDirectory()为访问子目录之后触发。
visitFile()为访问文件时触发。
visitFileFailed为访问文件失败时触发。
上面这些方法都返回一个FileVisitResult对象,它是一个枚举类对象,用来定义后续行为:
CONTINUE:继续访问。
SKIP_SIBLINGS:不访问兄弟文件或目录。
SKIP_SUBTREE:不访问子目录树。
TERMINATE:终止访问。
如下为使用Files的walkFileTree()方法遍历指定目录的示例:
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
public class FileVisitorTest
{
public static void main(String[] args)
throws Exception
{
// 遍历g:\publish\codes\15目录下的所有文件和子目录
Files.walkFileTree(Paths.get("g:", "publish" , "codes" , "15")
, new SimpleFileVisitor<Path>()
{
// 访问文件时候触发该方法
@Override
public FileVisitResult visitFile(Path file
, BasicFileAttributes attrs) throws IOException
{
System.out.println("正在访问" + file + "文件");
// 找到了FileInputStreamTest.java文件
if (file.endsWith("FileInputStreamTest.java"))
{
System.out.println("--已经找到目标文件--");
return FileVisitResult.TERMINATE;
}
return FileVisitResult.CONTINUE;
}
// 开始访问目录时触发该方法
@Override
public FileVisitResult preVisitDirectory(Path dir
, BasicFileAttributes attrs) throws IOException
{
System.out.println("正在访问:" + dir + " 路径");
return FileVisitResult.CONTINUE;
}
});
}
}
java.nio.file.attribute包下提供了XxxAttributeView工具类可以查看或设置文件的修改时间、访问时间、是否只读、是否为系统文件、所有者权限、组所有者等。
4、异步IO
AsynchronousChannel的实现类AsynchronousFileChannel、AsynchronousSocketChannel、AsynchronousServerSocketChannel用来进行异步IO,具体的实现可以参考Java网络编程(2):TCP和UDP里AIO部分。