本篇內(nèi)容介紹了“Java7的NIO.2特性怎么使用”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
成都創(chuàng)新互聯(lián)十年專注成都高端網(wǎng)站建設(shè)定制開發(fā)服務(wù),為客戶提供專業(yè)的成都網(wǎng)站制作,成都網(wǎng)頁設(shè)計(jì),成都網(wǎng)站設(shè)計(jì)服務(wù);成都創(chuàng)新互聯(lián)服務(wù)內(nèi)容包含成都網(wǎng)站建設(shè),微信小程序定制開發(fā),軟件開發(fā),網(wǎng)絡(luò)營銷推廣,網(wǎng)絡(luò)運(yùn)營服務(wù)及企業(yè)形象設(shè)計(jì);成都創(chuàng)新互聯(lián)擁有眾多專業(yè)的高端網(wǎng)站制作開發(fā)團(tuán)隊(duì),資深的高端網(wǎng)頁設(shè)計(jì)團(tuán)隊(duì)及經(jīng)驗(yàn)豐富的架構(gòu)師高端網(wǎng)站策劃團(tuán)隊(duì);我們始終堅(jiān)持從客戶的角度出發(fā),為客戶量身訂造網(wǎng)絡(luò)營銷方案,解決網(wǎng)絡(luò)營銷疑問。
在 Java 7 中,加強(qiáng)了文件操作相關(guān)功能,也就是新的 java.nio.file 包里的內(nèi)容,它提供了諸如文件路徑抽象、文件目錄流、目錄樹、文件屬性和變化監(jiān)視服務(wù)等功能,可以大幅度提高我們對于文件的操作。
在 Java 7 之前對文件路徑的操作都是以字符串的操作,使用時(shí)你需要把一個(gè)字符串直接扔進(jìn)去,直接使用字符串操作是低效的,比如你要拼接父路徑和子目錄,你只能進(jìn)行字符串的拼接。而且拼接這個(gè)本身操作就丟失了它作為文件路徑的含義。另外使用字符串進(jìn)行各種路徑操作很有可能由于拼寫錯(cuò)誤而出現(xiàn)各種問題。
Java 7 的到來讓這一切變的不一樣了,它提供了 Path 接口用來表示路徑的抽象,然后提供了一系列對于路徑的操作方法,讓這一切變得如此簡單。
為了方便的創(chuàng)建 Path 對象,又提供了Paths 工具類,如何使用讓我們先睹為快。
一切都從 Path path = Paths.get("/Users/darcy/java/");
獲取一個(gè) Path 對象開始。
Path path = Paths.get("/Users/darcy/java/"); System.out.println("完整路徑:" + path.toString()); Path pathParent = path.getParent(); System.out.println("父級路徑:" + pathParent.toString()); Path pathRoot = path.getRoot(); System.out.println("根目錄:" + pathRoot.toString()); int pathNameCount = path.getNameCount(); System.out.println("目錄深度:" + pathNameCount); Path pathIndex3 = path.getName(2); System.out.println("第三級目錄:" + pathIndex3); Path subPath = path.subpath(1, 3); System.out.println("第1級目錄到第三級目錄(包左不包右):" + subPath.toString()); // resolveSibling 從當(dāng)前目錄父目錄開始拼接目錄 Path pathResolveSibling = path.resolveSibling("PathDemo.java"); System.out.println("父目錄開始拼接參數(shù):" + pathResolveSibling.toString()); // resolve 把當(dāng)前路徑當(dāng)作父路徑,參數(shù)作為子目錄或者文件 Path pathResolve = Paths.get("/Users/darcy/java/").resolve("PathDem.java"); System.out.println("當(dāng)前目錄拼接后的目錄:" + pathResolve.toString()); // 參數(shù)路徑相對于主體路徑的相對路徑 Path path2 = Paths.get("/Users/darcy/"); Path path3 = Paths.get("/Users/darcy/java/PathDemo.java"); Path path4 = path2.relativize(path3); System.out.println("相對路徑:" + path4.toString()); /* 輸出結(jié)果 完整路徑:/Users/darcy/java 父級路徑:/Users/darcy 根目錄:/ 目錄深度:3 第三級目錄:java 第1級目錄到第三級目錄(包左不包右):darcy/java 父目錄開始拼接參數(shù):/Users/darcy/PathDemo.java 當(dāng)前目錄拼接后的目錄:/Users/darcy/java/PathDem.java 相對路徑:java/PathDemo.java */
可以看到上面代碼里除了創(chuàng)建 Path 對象時(shí)輸入了一次路徑,后續(xù)的操作都是使用 Path 中的方法進(jìn)行操作的,在此之前你可能需要各種字符串截取拼接,十分繁瑣。
還記得初學(xué) Java IO 時(shí),文件復(fù)制有多種寫法,但是不管是哪一種,寫起來都需要不少的代碼,而且還需要考慮復(fù)制時(shí)的性能。讀取文件那就更不用說了,定義各種讀取和接收變量,各種驗(yàn)證?,F(xiàn)在不一樣了,不僅文件操作非常方便,而且像文件復(fù)制和讀取等常用操作都可以一行搞定。
使用過于簡單,直接代碼。
// 如果文件不存在,則創(chuàng)建一個(gè)文件 Path path = Paths.get("test.txt"); Path pathBackup = Paths.get("test_bak.txt"); Path pathLink = Paths.get("test.txt.link"); Path pathDir = Paths.get("dir"); // 已存在則刪除 Files.deleteIfExists(path); Files.deleteIfExists(pathBackup); Files.deleteIfExists(pathLink); Files.deleteIfExists(pathDir); // 創(chuàng)建文件寫入內(nèi)容 Path file = Files.createFile(path); Files.write(path, "關(guān)注公眾號:未讀代碼".getBytes()); Files.write(path, System.lineSeparator().getBytes(), StandardOpenOption.APPEND); Files.write(path, "歡迎加我微信:wn8398".getBytes(), StandardOpenOption.APPEND); System.out.println("創(chuàng)建文件:" + file.toString()); // 創(chuàng)建文件鏈接 pathLink = Files.createLink(pathLink, path); System.out.println("創(chuàng)建文件:" + pathLink.toString()); // 創(chuàng)建目錄 Path directory = Files.createDirectory(pathDir); System.out.println("創(chuàng)建目錄:" + directory.toString()); // 文件復(fù)制 Files.copy(path, pathBackup); System.out.println("復(fù)制文件: " + path + " --> " + pathBackup); // 讀取文件 Listlines = Files.readAllLines(pathBackup); for (String line : lines) { System.out.println("文件讀?。? + line); }
上面展示了 Files 類的文件創(chuàng)建、刪除、寫入、拷貝、讀取的寫法,都是只有一行代碼。
和路徑操作類似,Java 7 也提供了文件屬性的抽象,增加了一系列文件屬性的操作工具類。這部分代碼在 java.nio.file.attribute
包內(nèi)。它抽象出了一個(gè) AttributeView 作為所有屬性視圖的父接口,然后用它的子類 Fi leAttributeView 表示文件視圖,用子類 FileOwnerAttributeView 表示文件所有者的屬性視圖。前者屬性如文件的創(chuàng)建時(shí)間、修改時(shí)間、是否目錄等信息,后者則包含文件的相關(guān)信息。為了兼容不同的操作系統(tǒng),Java 7 還提供了不同實(shí)現(xiàn),如 DosFileAttributeView 視圖,很明顯他是為 Windows 操作系統(tǒng)準(zhǔn)備的。
使用起來過于簡單,直接代碼奉上。
Path path = Paths.get("/Users/darcy/git/jdk-feature/README.md"); BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class); BasicFileAttributes basicFileAttributes = fileAttributeView.readAttributes(); FileTime creationTime = basicFileAttributes.creationTime(); FileTime lastModifiedTime = basicFileAttributes.lastModifiedTime(); FileTime lastAccessTime = basicFileAttributes.lastAccessTime(); System.out.println("創(chuàng)建時(shí)間:" + creationTime); System.out.println("上次修改時(shí)間:" + lastModifiedTime); System.out.println("上次訪問時(shí)間:" + lastAccessTime); boolean directory = basicFileAttributes.isDirectory(); boolean regularFile = basicFileAttributes.isRegularFile(); boolean symbolicLink = basicFileAttributes.isSymbolicLink(); System.out.println("是否目錄:" + directory); System.out.println("是否普通文件:" + regularFile); System.out.println("是否符號鏈接:" + symbolicLink); long size = basicFileAttributes.size(); System.out.println("文件大?。? + size); PosixFileAttributeView linuxFileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class); UserPrincipal owner = linuxFileAttributeView.getOwner(); System.out.println("文件歸屬用戶:" + owner.getName());
示例代碼運(yùn)行后得到如下輸出。
創(chuàng)建時(shí)間:2020-09-06T13:35:14Z 上次修改時(shí)間:2020-09-06T13:35:14.649261371Z 上次訪問時(shí)間:2020-09-06T13:35:14.680968254Z 是否目錄:false 是否普通文件:true 是否符號鏈接:false 文件大?。?636 文件歸屬用戶:darcy
在 Java 7 之前遍歷文件目錄和文件,你應(yīng)該會選擇 File 類的 listFiles 方法。
// 文件直接遍歷,不會遍歷子目錄 String pathString = "/Users/darcy/project/mylab/src/main/java/com/wdbyte/java"; File file = new File(pathString); File[] listFiles = file.listFiles(); for (File tempFile : listFiles) { System.out.println("file list: " + tempFile.getAbsolutePath()); }
這種遍歷方式看起來也是十分優(yōu)雅的,可是這種方式在面對大量文件時(shí),效率會變的很低。所以 Java 7 也對此進(jìn)行了改進(jìn),引入了 DirectoryStream文件列表流。它可以進(jìn)行漸進(jìn)式的文件遍歷,每次讀取一定數(shù)量,降低遍歷時(shí)的性能開銷,但是 DirectoryStream 遍歷時(shí)只會遍歷它的直接目錄和文件,不會遞歸的遍歷子目錄。下面是它的遍歷寫法。
String pathString = "/Users/darcy/project/mylab/src/main/java/com/wdbyte/java"; // Path 直接遍歷方式,不會遍歷子目錄 try (DirectoryStreamdirectoryStream = Files.newDirectoryStream(Paths.get(pathString))) { for (Path pathTemp : directoryStream) { System.out.println("DirectoryStream: " + pathTemp); } } // Path 直接遍歷方式 - 篩選 .class 文件 try (DirectoryStream directoryStream = Files.newDirectoryStream(Paths.get(pathString), "*.java")) { for (Path pathTemp : directoryStream) { System.out.println("DirectoryStream file type is class : " + pathTemp); } }
這里擴(kuò)展一下,在 Java 8 中對 Files 類進(jìn)行了增強(qiáng),引入了 Java 8 的 Lambda 表達(dá)式,增加了 walk 方法,遍歷文件也有異曲同工之妙(下面的例子中用到了 Lambda 表達(dá)式)。
// 遍歷所有目錄和子目錄 StreampathStream = Files.walk(Paths.get("/Users/darcy/project/mylab/src/main/java/com/wdbyte")); pathStream.forEach(pathTemp -> { System.out.println("Stream: " + pathTemp.toString()); }); // 遍歷所有目錄和子目錄 - 篩選 java 文件 pathStream = Files.walk(Paths.get("/Users/darcy/project/mylab/src/main/java/com/wdbyte")); pathStream .filter(pathTemp -> pathTemp.toString().endsWith(".java")) .forEach(pathTemp -> { System.out.println("Stream filter java: " + pathTemp.toString()); });
文件監(jiān)視,也就是可以動態(tài)的監(jiān)測指定目錄的文件或者內(nèi)容的變化,應(yīng)用場景很多,比如熱部署時(shí)檢查 class 文件是否更新,或者每當(dāng)有文件進(jìn)來時(shí)就進(jìn)行操作。在這之前你只能通過循環(huán)調(diào)用 listFiles 并與上次的調(diào)用結(jié)果對比才可以找到文件的變化,而現(xiàn)在可以通過通知的方式進(jìn)行反應(yīng)式的邏輯處理,一切變的簡單了。
被監(jiān)視的對象要實(shí)現(xiàn) Watchable 接口,然后通過 register 方法注冊到監(jiān)視服務(wù) WatchService 接口的實(shí)現(xiàn),同時(shí)指定要監(jiān)視的事件類型。
// 創(chuàng)建 StandardWatchEventKinds.ENTRY_CREATE, // 刪除 StandardWatchEventKinds.ENTRY_DELETE, // 更新 StandardWatchEventKinds.ENTRY_MODIFY
具體怎么使用呢?通過下面這個(gè)例子看下代碼如何實(shí)現(xiàn),下面的代碼對文件夾 /Users/darcy/test
進(jìn)行監(jiān)測,注冊的感興趣事件是創(chuàng)建、刪除、更新操作。
WatchService watchService = FileSystems.getDefault().newWatchService(); Path path = Paths.get("/Users/darcy/test"); path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); while (true) { WatchKey watchKey = watchService.take(); // 獲取事件類型 for (WatchEvent> pollEvent : watchKey.pollEvents()) { // 具體的事件上下文信息 Path tempPath = (Path)pollEvent.context(); Kind> kind = pollEvent.kind(); if (kind.name().equals(StandardWatchEventKinds.ENTRY_CREATE.name())) { System.out.println("創(chuàng)建了一個(gè)文件:" + tempPath.toString()); } if (kind.name().equals(StandardWatchEventKinds.ENTRY_DELETE.name())) { System.out.println("刪除了一個(gè)文件:" + tempPath.toString()); } if (kind.name().equals(StandardWatchEventKinds.ENTRY_MODIFY.name())) { System.out.println("修改了一個(gè)文件:" + tempPath.toString()); } } // 事件處理完畢后要進(jìn)行 reset 才能繼續(xù)監(jiān)聽事件 watchKey.reset(); // 取消監(jiān)視 // watchKey.cancel(); }
注冊事件監(jiān)聽后,通過一個(gè)循環(huán),調(diào)用 take() 方法獲取事件結(jié)果,得到事件后再判斷事件類型進(jìn)行日志輸出。我啟動后進(jìn)行了簡單測試,下面是日志輸出。
# 下面是我的操作 ? test pwd /Users/darcy/test ? test touch test.txt # 創(chuàng)建文件 ? test vim test.txt # 修改文件 ? test rm test.txt # 刪除文件 # 得到的日志輸出 創(chuàng)建了一個(gè)文件:test.txt 創(chuàng)建了一個(gè)文件:.test.txt.swp 修改了一個(gè)文件:test.txt 刪除了一個(gè)文件:.test.txt.swp 刪除了一個(gè)文件:test.txt
因?yàn)槭褂?vim 編輯,所以有臨時(shí)的 swp 文件生成和自動刪除,也被監(jiān)測到了。
“Java7的NIO.2特性怎么使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!