Java 学习之路 之 Java 7的NIO.2(七十一)

2024-03-05 16:48
文章标签 java 学习 七十一 nio.2

本文主要是介绍Java 学习之路 之 Java 7的NIO.2(七十一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Java 7 对原有的 NIO 进行了重大改进,改进主要包括如下两方面的内容。

提供了全面的文件 IO 和文件系统访问支持。

基于异步 Channel 的 IO。

第一个改进表现为 Java 7 新增的 java.nio.file 包及各个子包;第二个改进表现为 Java 7 在 java.nio.channels 包下增加了多个以 Asynchronous 开头的 Channel 接口和类。Java 7 把这种改进称为 NIO.2。

1,Path、Paths 和 Files 核心 API

早期的 Java 只提供了一个 File 类来访问文件系统,但 File 类的功能比较有限,它不能利用特定文件系统的特性,File 所提供的方法的性能也不高。而且,其大多数方法在出错时仅返回失败,并不会提供异常信息。

NIO.2 为了弥补这种不足,引入了一个 Path 接口,Path 接口代表一个平台无关的平台路径。除此之外,NIO.2 还提供了 Files、Paths 两个工具类,其中 Files 包含了大量静态的工具方法来操作文件;Paths 则包含了两个返回 Path 的静态工厂方法。

Files 和 Paths 两个工具类非常符合 Java 一贯的命名风格,比如前面介绍的操作数据的工具类为 Arrays,操作集合的工具类为 Collections,这种一致的命名风格可以让杜泽快速了解这些工具类的用途。

下面程序简单示范了 Path 接口的功能和用法。

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

从上面程序可以看出,Paths 提供了 get(String first, String... more) 方法来获取 Path 对象,Paths 会将给定的多个字符串连缀成路径,比如 Paths.get("g:", "publish", "codes") 就返回 g:\publish\codes 路径。

上面程序中第 8,9,11,15,18,21 行代码简单示范了 Path 接口的常用方法,读者可能对 getNameCount() 方法感到有点困惑,在此简要说明一下:它会返回 Path 路径所包含的路径名的数量,例如 g:\publish\codes 调用该方法就会返回 3。

Files 类是一个操作文件的工具类,它提供了大量的工具方法,下面程序简单示范了 Files 类的用法。

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"));FileStore cStore = Files.getFileStore(Paths.get("C:"));// 判断C盘的总空间,可用空间System.out.println("C:共有空间:" + cStore.getTotalSpace());System.out.println("C:可用空间:" + cStore.getUsableSpace());}
}

上面程序中简单示范了 Files 工具类的用法。从上面程序不难看出,Files 类时一个高度封装的工具类,它提供了大量的工具方法来完成文件复制、读取文件内容、写入文件内容等功能——这些原本需要程序员通过 IO 操作才能完成的功能,现在 Files 类只要一个工具方法即可。

读者应该熟练掌握 Files 工具类的用法,它所包含的工具方法可以大大地简化文件 IO。

2,使用 FileVisitor 遍历文件和目录

在以前的 Java 版本中,如果程序要遍历指定目录下的所有文件和子目录,则只能使用递归进行遍历,但这种方式不仅复杂,而且灵活性也不高。

有了 Files 工具类的帮助,现在可以用更优雅的方式来遍历文件和子目录。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, IOExccption exc):访问子目录之后触发该方法。

FileVisitResult preⅥsitDirectory(T dir, BasircFileAttributes attrs):访问子目录之前触发该方法。

FileVisitResult visitFile(T file, BasicFileAttributes attrs):访问 file 文件时触发该方法。

FileVisitResult visitFileFailed(T file, IOException exc):访问 file 文件失败时触发该方法。

上面 4 个方法都返回一个 FiieVisitResult 对象,它是一个枚举类,代表了访问之后的后续行为。FileVisitResult 定义了如下几种后续行为。

CONTINUE:代表 “继续访问” 的后续行为。

SKIP_SIBLINGS:代表 “继续访问” 的后续行为,但不访问该文件或目录的兄弟文件或目录。

SKIP_SUBTREE:代表 “继续访问” 的后续行为,但不访问该文件或目录的子目录树。

TERMINATE:代表 “中止访问” 的后续行为。

实际编程时没必要为 FileVisitor 的 4 个方法都提供实现,可以通过继承 SimpleFileⅥsitor(FiIeVisitor 的实现类)来实现自己的 “文件访问器”,这样就根据需要、选择性地重写指定方法了。

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>(){// 访问文件时候触发该方法@Overridepublic 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;}// 开始访问目录时触发该方法@Overridepublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException{System.out.println("正在访问:" + dir + " 路径");return FileVisitResult.CONTINUE;}});}
}

上面程序中使用了 Files 工具类的 walkFileTree() 方法来遍历 g:\publish\codes\15 目录下的所有文件和子目录,如果找到的文件以 “FileVisitor.java” 结尾,则程序停止遍历——这就实现了对指定目录进行搜索,直到找到指定文件为止。

3,使用 WatchService 监控文件变化

在以前的 Java 版本中,如果程序需要监控文件的变化,则可以考虑启动一条后台线程,这条后台线程每隔一段时间去 “遍历” 一次指定目录的文件,如果发现此次遍历结果与上次遍历结果不同,则认为文件发生了变化。但这种方式不仅十分烦琐,而且性能也不好。

NIO.2 的 Path 类提供了如下一个方法来监听文件系统的变化。

register(WatchService watcher, WatchEvent.Kind<?>… events):用 watcher 监听该 path 代表的目录下的文件变化。events 参数指定要监听哪些类型的事件。

在这个方法中 WatchService 代表一个文件系统监听服务,它负责监听 path 代表的目录下的文件变化。一旦使用 register() 方法完成注册之后,接下来就可调用 WatchService 的如下 3 个方法来获取被监听目录的文件变化事件。

WatchKey poll():获取下一个 WatchKey,如果没有 WatchKey 发生就立即返回 null。

WatchKey poll(long timeout,TimeUnit unit):尝试等待 timeout 时间去获取下一个 WatchKey。

WatchKey take():获取下一个 WatchKey,如果没有 WatchKey 发生就一直等待。

如果程序需要一直监控,则应该选择使用 take() 方法;如果程序只需要监控指定时间,则可考虑使用 poll() 方法。下面程序示范了使用 WatchService 来监控 C:盘根路径下文件的变化。

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()+ "事件!");}// 重设WatchKeyboolean valid = key.reset();// 如果重设失败,退出监听if (!valid) {break;}}}
}

上面程序使用了一个死循环重复获取 C: 盘根路径下文件的变化,程序在①号代码处试图获取下一个 WatchKey,如果没有发生就等待。因此 C: 盘根路径下每次文件的变化都会被该程序监听到。

运行该程序,然后在 C: 盘下新建一个文件,再删除该文件,将看到如图 15.22 所示的输出。


从图 15.22 不难看出,通过使用 WatchService 可以非常优雅地监控指定目录下文件的变化,至于文件发生变化后,程序应该进行哪些处理,这就取决于程序的业务需要了。

4,访问文件属性

早期的 Java 提供的 File 类可以访问一些简单的文件属性,比如文件大小、修改时间、文件是否隐藏、是文件还是目录等。如果程序需要获取或修改更多的文件属性,则必须利用运行所在平台的特定代码来实现,这是一件非常困难的事情。

Java 7 的 NIO.2 在 java.nio.file.attribute 包下提供了大量的工具类,通过这些工具类,开发者可以非常简单地读取、修改文件属性。这些工具类主要分为如下两类。

XxxAttributeView:代表某种文件属性的 “视图”。

XxxAttributes:代表某种文件属性的 “集合”,程序一般通过 XxxAttributeView 对象来获取 XxxAttributes。

在这些工具类中.FileAttributeView 是其他 XxxAttributeView 的父接口,下面简单介绍一下这些 XxxAttributeView。

AcIFileAttributeⅥew:通过 AcIFileAttributeView,开发者可以为特定文件设置 ACL(Access Control List)及文件所有者属性。它的 getAcl() 方法返回 List<AcIEntry> 对象,该返回值代表了该文件的权限集。通过 setAcI(List) 方法可以修改该文件的 ACL。

BasicFileAttributeView:它可以获取或修改文件的基本属性,包括文件的最后修改时间、最后访问时间、创建时间、大小、是否为目录、是否为符号链接等。它的 readAttributes() 方法返回一个 BasicFileAttributes 对象,对文件夹基本属性的修改是通过 BasicFileAttributes 对象完成的。

DosFileAttributeView:它主要用于获取或修改文件 DOS 相关属性,比如文件是否只读、是否隐藏、是否为系统文件、是否是存档文件等。它的 readAttributes() 方法返回一个 DosFileAttributes 对象,对这些属性的修改其实是由 DosFileAttributes 对象来完成的。

FileOwnerAttributeVlew:它主要用于获取或修改文件的所有者。它的 getOwner() 方法返回一个 UserPrincipal 对象来代表文件所有者;也可调用 setOwner(UserPrincipal owner) 方法来改变文件的所有者。

PosixFileAttributeView:它主要用于获取或修改 POSIX(Portable Operating System Interface of INIX)属性,它的 readAttributes() 方法返回一个 PosixFileAttributes 对象,该对象可用于获取或修改文件的所有者、组所有者、访问权限信息(就是 UNIX 的 chmod 命令负责干的事情)。这个 View 只在 UNIX、Linux 等系统上有用。

UserDefinedFileAttributeView:它可以让开发者为文件设置一些自定义属性。

下面程序示范了如何读取、修改文件的属性。

public class AttributeViewTest
{public static void main(String[] args)throws Exception{// 获取将要操作的文件Path testPath = Paths.get("AttributeViewTest.java");// 获取访问基本属性的BasicFileAttributeViewBasicFileAttributeView basicView = Files.getFileAttributeView(testPath , BasicFileAttributeView.class);// 获取访问基本属性的BasicFileAttributesBasicFileAttributes 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());// 获取访问文件属主信息的FileOwnerAttributeViewFileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath, FileOwnerAttributeView.class);// 获取该文件所属的用户System.out.println(ownerView.getOwner());// 获取系统中guest对应的用户UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName("guest");// 修改用户ownerView.setOwner(user);// 获取访问自定义属性的FileOwnerAttributeViewUserDefinedFileAttributeView userView = Files.getFileAttributeView(testPath, UserDefinedFileAttributeView.class);List<String> attrNames = userView.list();// 遍历所有的自定义属性for (String 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("疯狂Java联盟"));// 获取访问DOS属性的DosFileAttributeViewDosFileAttributeView dosView = Files.getFileAttributeView(testPath, DosFileAttributeView.class);// 将文件设置隐藏、只读dosView.setHidden(true);dosView.setReadOnly(true);}
}

上面程序中分别访问了 4 种不同类型的文件属性,关于读取、修改文件属性的说明,程序中的代码已有详细说明,因此不再过多地解释。第二次运行该程序(记住第一次运行后

 AttributeViewTest.java 文件变成隐藏、只读文件,因此第二次运行之前一定要先取消只读属性),将看到如图 15.23 所示的输出。


这篇关于Java 学习之路 之 Java 7的NIO.2(七十一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/777152

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06