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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章