java.nio.file
java.nio.file的使用
这个包中最核心的概念就是 Path
, 可以说 Path
是整个java.nio.file包和java.nio.file.attribute包中操作的目标,几乎所有的API都是围绕Path
进行的。
Path的获取
通过 File 类
1
2File file = new File("/file.txt");
Path path = file.toPath();通过 Paths 类
1
Path path = Paths.getPath("/file.txt");
通过 FileSystem 类
1
2FileSystem fileSystem = FileSystems.getDefault();
Path path = fileSystem.getPath("/file.txt");
其中前两种方式,获得的是当前JVM默认的FileSystem来获取Path.
default file system:
The default file system creates objects that provide access to the file systems accessible to the Java virtual machine.
java.io.File 使用是就是JVM运行环境所在的文件系统。所以通过将两种方式获得的 Path 可以和 File 对象进行互操作。
FileSystems 类可以获得默认的文件系统,同时也提供了创建文件系统的方法。例如:
1 | // 获得 zip 文件系统 |
FileSystem的获取
- FileSystems.newFileSystem
- path.getFileSystem
第二种方式是通过 Path 类的实现,来获得其所在的文件系统的抽象 FileSystem
FileSystem & FileSystemProvider
wiki 中关于 File System 的描述:
In computing, a file system or filesystem is used to control how data is stored and retrieved. Without a file system, information placed in a storage medium would be one large body of data with no way to tell where one piece of information stops and the next begins. By separating the data into pieces and giving each piece a name, the information is easily isolated and identified. Taking its name from the way paper-based information systems are named, each group of data is called a “file”. The structure and logic rules used to manage the groups of information and their names is called a “file system”.
可以看到文件系统,其实就是对数据如何存储的一种机制。从而有了目录和文件的概念,目录和文件就可以使用 Path 来表示。
同时对于一个目录和文件,文件系统可以提供一种抽象,就是对它们进行属性设置,例如:文件A 不能被执行。目录B 不可以被某个用户访问。这些信息,本身和文件数据(data)是没有任何关系,只是文件系统提供的一种属性,而这种属性则是存储在文件系统中的,所以 FileStore 可以用来表示这种抽象,通过FileStore可以查询到文件的属性。
基于目录层次文件系统抽象,还有一个概念就是根目录(RootDirectory)。所有新创建的文件和目录必然是从根目录开始的。
对于操作系统而言,文件系统提供了创建目录及文件的功能,自然对于文件的任何变化,例如文件属性的变化,文件内容的变化,都由文件系统来完成,则文件系统就可以提供一种机制,让用户(其它程序)监测文件的变化,如果文件发生变化,则这个程序可以得到通知。所以文件系统也应该能够提供 WatchService 的功能。
而上面的这些功能就可以接口的形式提供给用户,所以对于 java 中 FileSystem 类就像一个工厂一样,将一个文件系统所能提供的功能都以接口的形式提供出来:Path, PathMatcher,FileStore,UserPrincipalLookupService,WatchService 等等。
所以说 FileSystemProvider 中提供的仅仅是一个 文件系统 提供给用户的服务,例如:创建目录,创建文件等。
但 文件系统 并不等同于上面的服务。还有其存储元数据的信息等等。
所以说 将文件系统抽象为 FileSystem & FileSystemProvider 这两个类,是比较清晰的表达的文件系统的概念。
其实可以把 FileSystemProvider 类的方法移到 FileSystem 中,从逻辑上讲也是没有问题的。可以参考 Hadoop 对 文件系统 的抽象,就是这么做的。
FileSystem:
Provides an interface to a file system and is the factory for objects to access files and other objects in the file system.
FileSystemProvider:
Service-provider class for file systems.
A file system provider is a concrete implementation of this class that implements the abstract methods defined by this class.
A provider is identified by a URI scheme.
A provider is a factory for one or more FileSystem instances.
Path
FileSystem.getPath
PathMatcher
FileSystem.getPathMatcher
FileStore
FileSystem.getFileStores
UserPrincipalLookupService
FileSystem.getUserPrincipalLookupService
WatchService
FileSystem.newWatchService
JVM如何动态发现已经安装的Provider
通过 java.util.ServiceLoader
A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services. The file’s name is the fully-qualified binary name of the service’s type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line.
WatchService & Watchable
WatchService 可以用来检测文件系统中目录的变化。其使用方法如下。
1 |
|
WindowsWatchService
windows 平台如何实现 WatchService:
创建 WatchService
1 | /* |
WindowsWatchService 对象持有一个 Poller 对象。Poller 中持有一个 port 这是一个IO完成端口。
WindowsWatchService 被创建(FileSystem.newWatchService)时创建一个Poller 对象,然后调用 Poller 对象的 start 方法,start 方法中会启动一个线程。这个线程调用 Poller(实现了Runnable接口)的 run 方法。开始进行循环。这个循环的功能就是:
- 在 io 完成端口上等待
- 上面的等待可能会返回,其返回的原因,可能是,被其它线程唤醒了
- 线程因为:Path.register 方法被唤醒,则是提交新的 Watch.Event
- 线程因为:WatchService.close 方法,Closes this watch service. 关闭 WatchService 就是关闭 Poller 的主循环。
- 线程因为:WatchKey.cancel 方法,Cancels the registration with the watch service.
上面三种方法,都调用 AbstractPoller.invoke 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// Enqueues request to poller thread and waits for result
private Object invoke(RequestType type, Object... params) throws IOException {
// 1. submit request
Request req = new Request(type, params);
synchronized (requestList) {
if (shutdown) {
throw new ClosedWatchServiceException();
}
requestList.add(req);
}
// 2. wakeup thread
// 也就是唤醒 Poller 主循环
// 处理请求 requestList 中的请求。
wakeup();
// 3. wait for result
// 等待请求的结束
Object result = req.awaitResult();
if (result instanceof RuntimeException)
throw (RuntimeException)result;
if (result instanceof IOException )
throw (IOException)result;
return result;
}如果主循环是因为上面三种情况被循环则,在主循环中调用 processRequests用来相应请求
- 由于IO请求完成,而 main loop 被唤醒,则处理,返回事件。
- 调用 processEvents, 最终调用 key.signalEvent(kind, name);
- key.signalEvent 最终 调用 signal 方法
- signal 方法将 key 添加到 WatchService 的一个阻塞队列中
在 WatchService 上调用 take 方法的线程将,take 将从上面的队列中返回数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14// AbstractWatchService
// signaled keys waiting to be dequeued
private final LinkedBlockingDeque<WatchKey> pendingKeys =
new LinkedBlockingDeque<WatchKey>();
public final WatchKey take()
throws InterruptedException
{
checkOpen();
WatchKey key = pendingKeys.take();
checkKey(key);
return key;
}
查询和设置文件属性
文件属性相关的包 java.nio.file.attribute
获得 FileAttributeView
1 | Files.getFileAttributeView |
Returns a file attribute view of a given type.
获得 FileStoreAttributeView
1 | // 获得 FileStore 对象 |
Files 类
This class consists exclusively of static methods that operate on files, directories, or other types of files.
Service-provider class for file systems. The methods defined by the Files class will typically delegate to an instance of this class(FileSystemProvider).
这个类提供了关于文件系统的操作接口。