目录
NIO2是在NIO的基础上进行的扩展。Java 7 对原有的NIO进行了改进:
- 提供了全面的文件IO和文件系统访问支持:表现为Java 7新增的java.nio.file包及其各个子包
- 基于异步Channel的IO : 在java.nio.channels包下新增了多个以Asynchronous开头的Channel接口和类
Java 8进一步增强了Files工具类的用法,允许开发者使用Stream API来操作文件目录和文件内容。
一、重要类
NIO2或者说NIO中的核心类:Path、 Paths、Files
对应早期Java中的File类,File类是用来访问文件系统,但是功能有限(尽管我还用的挺舒服的),它不能利用特定文件系统的特性,其提供方法的访问性能也不高,NIO2为了弥补这种不足,引入了Path接口,Path代表一个平台无关的平台路径。
另外Files、Paths是两个工具类,包含大量的静态方法。
1、Path类
对应原有的java.io.File类
使用示例:
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathTest {
public static void main(String[] args) {
Path path = Paths.get(".");
System.out.println("path路径:" + path); // 输出: path路径:.
System.out.println("path里包含的路径数量:" + path.getNameCount()); // 结果为1
System.out.println("path的根路径:" + path.getRoot()); // 结果为null
System.out.println("path的父路径:" + path.getParent()); // 输出: path的父路径:null
Path absolutePath = path.toAbsolutePath();
System.out.println("path的绝对路径:" + absolutePath); // 输出: path的绝对路径:/Users/qinwenjing/Documents/Projects/Test/.
System.out.println("path的绝对路径名称:" + absolutePath.getFileName()); // 输出: path的绝对路径名称:.
System.out.println("path的绝对路径的根路径:" + absolutePath.getRoot()); // 输出: path的绝对路径的根路径:/
System.out.println("path的绝对路径数量:" + absolutePath.getNameCount());
System.out.println("path的绝对路径下标为3的路径名称:" + absolutePath.getName(3));
System.out.println("path的绝对路径的父路径:" + absolutePath.getParent()); // path的绝对路径的父路径:/Users/qinwenjing/Documents/Projects/Test
// 以多个string构造Path
Path path1 = Paths.get("g:", "publish", "codes");
System.out.println(path1);
}
}
2、Files工具类、Paths工具类
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class FilesTest {
public static void main(String[] args) {
try {
Path path = Paths.get("/Users/qinwenjing/Documents/Projects/Test/test2.txt");
// 复制文件
Files.copy(path, new FileOutputStream("b.txt"));
// 判断是否是隐藏文件
System.out.println(Files.isHidden(path));
// 输出文件的大小
System.out.println(Files.size(path));
// 一次性读取文件中所有的行
List<String> linesList = Files.readAllLines(path);
System.out.println(linesList);
// 直接将多个字符串写入到指定的文件
Files.write(Paths.get("/Users/qinwenjing/Documents/Projects/Test/d.txt"), linesList, Charset.forName
("utf-8"));
// 列出当前目录下所有的文件和子目录
Files.list(Paths.get(".")).forEach(p -> System.out.println(p));
System.out.println("--------读取文件内容---------");
Files.lines(path, Charset.forName("utf-8")).forEach(line -> System.out.println(line));
FileStore fileStore = Files.getFileStore(path);
System.out.println("共用空间:" + fileStore.getTotalSpace());
System.out.println("可用空间:" + fileStore.getUsableSpace());
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、FileVisitor遍历目录和文件
FileVisitor是一个接口,默认实现类SimpleFileVisitor。FileVisitor中的方法:
1、访问目录前触发的方法
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
2、访问文件时触发的方法
FileVisitResult visitFile(T file, BasicFileAttributes attrs)
3、访问文件失败时触发的方法
FileVisitResult visitFileFailed(T file, IOException exc)
4、访问目录后触发的方法
FileVisitResult postVisitDirectory(T dir, IOException exc)
其返回值是 FileVisitResult, FileVisitResult是个枚举:
public enum FileVisitResult {
/**
* Continue. When returned from a {@link FileVisitor#preVisitDirectory
* preVisitDirectory} method then the entries in the directory should also
* be visited.
*/
CONTINUE,
/**
* Terminate.
*/
TERMINATE,
/**
* Continue without visiting the entries in this directory. This result
* is only meaningful when returned from the {@link
* FileVisitor#preVisitDirectory preVisitDirectory} method; otherwise
* this result type is the same as returning {@link #CONTINUE}.
*/
SKIP_SUBTREE,
/**
* Continue without visiting the <em>siblings</em> of this file or directory.
* If returned from the {@link FileVisitor#preVisitDirectory
* preVisitDirectory} method then the entries in the directory are also
* skipped and the {@link FileVisitor#postVisitDirectory postVisitDirectory}
* method is not invoked.
*/
SKIP_SIBLINGS;
}
4、使用WatchService监控文件变化
考虑这样一个需求:需要监控文件的变化,那该怎样做呢?常规的做法就是启动一个线程(定时任务),每隔一段时间去『遍历』一次指定的目录的文件,如果遍历的结果和上次不同,则认为文件发生了变化。但是有没有更好的方式来实现呢?
NIO2利用WatchService类提供了如下一个监听文件系统的变化的方法:
示例:
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
/**
* WatchService代表一个文件系统监听服务,负责监听path表示的路径下文件变化,
* 一旦使用了register()方法注册之后,接下里就可以调用watchService的方法来获取被监听的文件变化事件
*/
public class WatchServiceTest {
public static void main(String[] args) {
try {
WatchService watchService = FileSystems.getDefault().newWatchService();
Paths.get("/Users/qinwenjing/Documents/Projects/Test/src/main/java")
.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
// 获取下一个文件变化事件 (获取下一个WatchKey),如果没有发生就等待
WatchKey key = watchService.take();
// 获取下一个文件变化事件(获取下一个WatchKey),没有的话就返回null
// WatchKey key = watchService.poll();
System.out.println("拿到watchKey....");
for (WatchEvent event : key.pollEvents()) {
System.out.println(event.context() + " 文件发生了" + event.kind() + "事件!");
}
// 重设WatchKey
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5、访问文件属性的一些高级方式
早期的File类可以访问一些简单的文件属性,比如文件大小、修改时间、文件是否隐藏、是文件还是目录等。如果需要获取或修改更多的文件属性,这是可以利用Java 7的NIO2在java.nio.file.attribute包下提供的大量工具类。
示例:
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.util.Date;
import java.util.List;
public class AttributeViewTest {
public static void main(String[] args) {
Path testPath = Paths.get("/Users/qinwenjing/Documents/Projects/Test/src/main/java/IO/NIO2/AttributeViewTest"
+ ".java");
File file = new File("/Users/qinwenjing/Documents/Projects/Test/src/main/java/IO/NIO2/AttributeViewTest"
+ ".java");
try {
// 1、获取访问 基本属性 的BasicFileAttributeView
BasicFileAttributeView baseView = Files.getFileAttributeView(testPath, BasicFileAttributeView.class);
// 获取访问基本属性的BasicFileAttributes
BasicFileAttributes basicFileAttributes = baseView.readAttributes();
System.out.println("创建时间 :" + basicFileAttributes.creationTime().toMillis());
System.out.println("最后访问时间:" + basicFileAttributes.lastAccessTime().toMillis());
System.out.println("最后修改时间:" + new Date(basicFileAttributes.lastModifiedTime().toMillis()));
// 2、获取访问 文件属主信息 的FileOwnerAttributeView
FileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath,
FileOwnerAttributeView.class);
// 获取文件所属的用户
System.out.println(ownerView.getOwner());
// 获取系统中qinwenjing对应的用户
UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName
("qinwenjing");
// 修改用户
ownerView.setOwner(user);
// 3、获取访问Dos属性的DosFileAttributeView
DosFileAttributeView dosView = Files.getFileAttributeView(testPath, DosFileAttributeView.class);
// 将文件设置为隐藏、只读
dosView.setHidden(true);
dosView.setReadOnly(true);
// 4、获取访问自定义属性的UserDefinedFileAttributeView(可能为null)
UserDefinedFileAttributeView userView = Files.getFileAttributeView(testPath, UserDefinedFileAttributeView
.class);
List<String> attributes = userView.list();
for (String name : attributes) {
ByteBuffer buf = ByteBuffer.allocate(userView.size(name));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}