Java面向对象系列[v1.0.0][NIO.2]

NIO.2的文件IO和文件系统

Java7之后提供了全面的文件IO和文件系统访问支持,并且还支持异步的Channel

Path、Paths和Files核心API

NIO.2引入了一个Path接口,Path接口代表一个平台无关的平台路径,此外NIO.2还提供了Files和Paths两个工具类,其中Files包含了大量静态的工具类方法来操作文件,Paths则包含了两个返回Path的静态工厂方法

import java.io.*;
import java.net.*;
import java.nio.file.*;

public class PathTest
{
	public static void main(String[] args)
		throws Exception
	{
		// 以当前路径来创建Path对象
		Path path = Paths.get(".");
		System.out.println("path里包含的路径数量:"
			+ path.getNameCount());
		System.out.println("path的根路径:" + path.getRoot());
		// 获取path对应的绝对路径。
		Path absolutePath = path.toAbsolutePath();
		System.out.println(absolutePath);
		// 获取绝对路径的根路径
		System.out.println("absolutePath的根路径:"
			+ absolutePath.getRoot());
		// 获取绝对路径所包含的路径数量
		System.out.println("absolutePath里包含的路径数量:"
			+ absolutePath.getNameCount());
		System.out.println(absolutePath.getName(3));
		// 以多个String来构建Path对象
		Path path2 = Paths.get("g:", "publish", "codes");
		System.out.println(path2);
	}
}
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
	{
		// 复制文件
		Files.copy(Paths.get("FilesTest.java"),
			new FileOutputStream("a.txt"));
		// 判断FilesTest.java文件是否为隐藏文件
		System.out.println("FilesTest.java是否为隐藏文件:"
			+ Files.isHidden(Paths.get("FilesTest.java")));
		// 一次性读取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新增的Stream API列出当前目录下所有文件和子目录
		Files.list(Paths.get(".")).forEach(path -> System.out.println(path));
		// 使用Java 8新增的Stream API读取文件内容
		Files.lines(Paths.get("FilesTest.java"), Charset.forName("gbk"))
			.forEach(line -> System.out.println(line));
		FileStore cStore = Files.getFileStore(Paths.get("C:"));
		// 判断C盘的总空间,可用空间
		System.out.println("C:共有空间:" + cStore.getTotalSpace());
		System.out.println("C:可用空间:" + cStore.getUsableSpace());
	}
}

FileVisitor遍历文件和目录

Files类提供了如下两个方法来遍历文件和子目录:

  • walkFileTree(Path start, FileVisitor<? super Path> visitor): 遍历start路径下的所有文件和子目录
  • walkFileTree(Path start, Set< FileVisitOption > options, int maxDepth, FileVisitor<? super Path>visitor): 该方法跟上一个类似,但最多遍历maxDepth深度的文件

两个方法都需要FileVisitor参数,FileVisitor代表一个文件访问器,walkFileTree()方法会自动遍历start路径下的所有文件和子目录,遍历文件和子目录都会触发FileVisitor中相应的方法

FileVisitor中定义了如下4个方法:

  • FileVisitResult postVisitDirectory(T dir, IOException exc):访问子目录之后触发该方法
  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs):访问子目录之前触发该方法
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs):访问file文件时触发该方法
  • FileVisitResult visitFileFailed(T file, IOException exc):访问file文件失败时候触发该方法

这4个方法都返回一个FileVisitResult对象,它是一个枚举类,代表访问了之后的后续行为(FileVisitResult):

  • CONTINUE: 代表“继续访问”的后续行为
  • SKIP_SIBLINGS:代表“继续访问”的后续行为。。。。。。
  • SKIP_SUBTREE:代表“继续访问”的后续行为。。。。。。
  • TERMINATE:代表“中止访问”的后续行为。。。。。。

实际编程时,可以通过继承SimpleFileVisitor(FileVisitor的实现类)来实现自己的文件访问器

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;
			}
		});
	}
}

WatchService监控文件变化

之前的实现方式是,启动一个后台线程监控文件,每隔一段时间去遍历一下指定目录的文件,如果发现结果不一样,则判定文件发生了变化,NIO.2的Path类提供了如下方法来监听文件系统的变化

  • register(WatchService watcher, WatchEvent.Kind<?>… events):用watcher监听该path代表的目录下的文件变化,events参数指定要监听哪些类型的事件,在这个方法中,WatchService代表一个文件系统监听服务,它负责监听path代表的目录下的文件变化,一旦使用register()方法完成注册后,接下来就可以调用WatchService的如下三个方法来获取被监听目录的文件变化事件:
    • WatchKey poll():获取下一个WatchKey,如果没有WatchKey发生就立即返回null
    • WatchKey poll(long timeout, TimeUnit unit):尝试等待timeout时间去获取下一个WatchKey
    • WatchKey take(): 获取下一个WatchKey,如果没有WatchKey发生就一直等待

如果程序需要一直监控,则应该选择使用take()方法,如果只需要监控指定时间则应该使用poll()方法

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class WatchServiceTest
{
	public static void main(String[] args)
		throws Exception
	{
		// 获取文件系统的WatchService对象
		WatchService watchService = FileSystems.getDefault()
			.newWatchService();
		// 为C:盘根路径注册监听
		Paths.get("C:/").register(watchService,
			StandardWatchEventKinds.ENTRY_CREATE,
			StandardWatchEventKinds.ENTRY_MODIFY,
			StandardWatchEventKinds.ENTRY_DELETE);
		while (true)
		{
			// 获取下一个文件改动事件
			WatchKey key = watchService.take();    // ①
			for (WatchEvent<?> event : key.pollEvents())
			{
				System.out.println(event.context() +" 文件发生了 "
					+ event.kind()+ "事件!");
			}
			// 重设WatchKey
			boolean valid = key.reset();
			// 如果重设失败,退出监听
			if (!valid)
			{
				break;
			}
		}
	}
}

访问文件属性

NIO.2在java.nio.file.attribute包下提供了大量的工具类,通过这些工具类可以很简单的读写文件属性,工具类大致分为两大类:

  • XxxAttribute View:代表某种文件属性的视图
  • XxxAttributes: 代表某种文件属性的集合,程序一般通过XxxAttributeView对象来获取XxxAttributes

在这些工具类中,FileAttributeView是其他XxxAttributeView的父接口:

  • AclFileAttributeView:通过AclFileAttributeView,开发者可以为特定文件设置ACL(AccessControlList)及文件所有者属性,它的getAcl()方法返回List< AclEntry >对象,该返回值代表了该文件的权限集,通过setAcl(List)方法可以修改该文件的ACL
  • BasicFileAttributeView:它可以获取或修改文件的基本属性,包括文件的最后修改时间,最后访问时间,创建时间,大小,是否为目录,是否为符号链接等,它的readAttributes()方法返回一个BasicFileAttributes对象,对文件夹基本属性的修改是通过BasicFileAttributes对象完成的
  • DosFileAttributeView:它主要用于获取或修改文件DOS相关属性,例如文件是否只读,是否隐藏,是否为系统文件,是否是存档文件等,它的readAttributes()方法返回一个DosFileAttributes对象,对这些属性的修改其实是由DosFileAttributes对象来完成
  • FileOwnerAttributeView:它主要用于获取或修改文件的所有者,它的getOwner()方法返回一个UserPrincipal对象来代表文件所有者,也可以调用setOwner(UserPrincipal owner)方法来改变文件所有者
  • PosixFileAttributeView:它主要用于获取或修改POSIX(Portable Operatiing System Interface of INIX)属性,它的readAttributes()方法返回一个PosixFileAttributes对象,该对象可用于获取或修改文件的所有者、组所有者、访问权限信息也就是chmod干的事,这个View只在Unix、Linux等系统上有用
  • UserDefinedFileAttributeView:用于设置自定义属性
import java.io.*;
import java.util.*;
import java.nio.file.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.file.attribute.*;

public class AttributeViewTest
{
	public static void main(String[] args)
		throws Exception
	{
		// 获取将要操作的文件
		Path testPath = Paths.get("AttributeViewTest.java");
		// 获取访问基本属性的BasicFileAttributeView
		BasicFileAttributeView basicView = Files.getFileAttributeView(
			testPath, BasicFileAttributeView.class);
		// 获取访问基本属性的BasicFileAttributes
		BasicFileAttributes basicAttribs = basicView.readAttributes();
		// 访问文件的基本属性
		System.out.println("创建时间:" + new Date(basicAttribs
			.creationTime().toMillis()));
		System.out.println("最后访问时间:" + new Date(basicAttribs
			.lastAccessTime().toMillis()));
		System.out.println("最后修改时间:" + new Date(basicAttribs
			.lastModifiedTime().toMillis()));
		System.out.println("文件大小:" + basicAttribs.size());
		// 获取访问文件属主信息的FileOwnerAttributeView
		FileOwnerAttributeView ownerView = Files.getFileAttributeView(
			testPath, FileOwnerAttributeView.class);
		// 获取该文件所属的用户
		System.out.println(ownerView.getOwner());
		// 获取系统中guest对应的用户
		UserPrincipal user = FileSystems.getDefault()
			.getUserPrincipalLookupService()
			.lookupPrincipalByName("guest");
		// 修改用户
		ownerView.setOwner(user);
		// 获取访问自定义属性的FileOwnerAttributeView
		UserDefinedFileAttributeView userView = Files.getFileAttributeView(
			testPath, UserDefinedFileAttributeView.class);
		List<String> attrNames = userView.list();
		// 遍历所有的自定义属性
		for (var name : attrNames)
		{
			ByteBuffer buf = ByteBuffer.allocate(userView.size(name));
			userView.read(name, buf);
			buf.flip();
			String value = Charset.defaultCharset().decode(buf).toString();
			System.out.println(name + "--->" + value);
		}
		// 添加一个自定义属性
		userView.write("发行者", Charset.defaultCharset()
			.encode("一块大洋"));
		// 获取访问DOS属性的DosFileAttributeView
		DosFileAttributeView dosView = Files.getFileAttributeView(testPath,
			DosFileAttributeView.class);
		// 将文件设置隐藏、只读
		dosView.setHidden(true);
		dosView.setReadOnly(true);
	}
}

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/106525688