【Java基礎】Java7新特性—Files類,Path類,Paths類的用法

Java7新增文件IO類

Java7中文件IO發生了很大的變化,專門引入了很多新的類:

java.nio.file.DirectoryStream;
java.nio.file.FileSystem;
java.nio.file.FileSystems;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.nio.file.attribute.FileAttribute;
java.nio.file.attribute.PosixFilePermission;
java.nio.file.attribute.PosixFilePermissions;

在java中文件或是目錄習慣用java.io.File對象來表示,但是File類有很多缺陷

  • 它的很多方法不能拋出異常

  • 它的delete方法經常莫名其妙的失敗等,舊的File類經常是程序失敗的根源。

  • 因此在Java7中有了更好的替代:java.nio.file.Pathjava.nio.file.Files

    • Path接口的名字非常恰當,就是表示路徑的,API中講Path對象可以是一個文件,一個目錄,或是一個符號鏈接,也可以是一個根目錄。 用法很簡單。創建Path並不會創建物理文件或是目錄path實例經常引用並不存在的物理對象,要真正創建文件或是目錄,需要用到Files類

    • Files類是一個非常強大的類,它提供了處理文件和目錄以及讀取文件和寫入文件的靜態方法。 可以用它創建和刪除路徑。複製文件。檢查路徑是否存在等。 此外。Files還擁有創建流對象的方法。

一.Paths

Paths類僅由靜態方法組成,通過轉換路徑字符串返回Path或URI

  • 因爲就兩個方法用來生成Path對象,以供Path和Files使用;而Path也經常由Paths來生成,又或者File類有一個toPath();方法可以使用

1.創建Paths

static Path get(String first, String... more) 
//將路徑字符串或連接到路徑字符串的字符串序列轉換爲 Path,可以get("c:/abc");或者get("c:","abc"),注意這裏可以有多個參數String... more代表n個參數,這個比較常用
static Path get(URI uri) 
//將給定的URI轉換爲Path對象

二.Path

Path就是取代File的,用於來表示文件路徑和文件。可以有多種方法來構造一個Path對象來表示一個文件路徑,或者一個文件:
- 該接口的實現是不可變且安全的,可供多個並行線程使用。

1.創建Path

Path toPath() 
//File類對象方法--返回一個java.nio.file.Path對象
abstract Path getPath(String first, String... more) 
//FileSystem對象方法--將路徑字符串或從路徑字符串連接起來的一系列字符串轉換爲 Path 。  

1.1.創建Path的三種方式

創建Path的三種方式

Path path=FileSystems.getDefault().getPath("d:/users/日記5.txt");    
//並沒有實際創建路徑,而是一個指向d:/users/日記5.txt路徑的引用

Path path=Paths.get("d:/users/日記5.txt");                                              //Paths類提供了這個快捷方法,直接通過它的靜態get方法創建path

Path path= = new File("d:/users/日記5.txt").toPath();

2.Path常用方法

Path接口沒什麼判斷方法,其實更多的判斷和操作都在Files工具類裏面

boolean isAbsolute() 
//告訴這條路是否是絕對的
boolean endsWith(Path other) 
//測試此路徑是否以給定的路徑結束
boolean endsWith(String other) 
//測試此路徑是否以給定字符串結束,如"c:/a/banana/cat"可以以"/banana/cat"結尾,但不能以"t"結尾
boolean startsWith(Path other) 
//測試此路徑是否以給定的路徑開始。  
boolean startsWith(String other) 
//測試此路徑是否以給定字符串開始,跟上面一樣規律

Path getFileName() 
//將此路徑表示的文件或目錄的名稱返回爲 Path對象,文件名或文件夾名,不含路徑
Path getName(int index) 
//返回此路徑的名稱元素作爲 Path對象。目錄中最靠近root的爲0,最遠的爲(count-1),count由下面的方法獲得
int getNameCount() 
//返回路徑中的名稱元素的數量。0則只有root
Path getParent() 
//返回 父路徑,如果此路徑沒有父返回null,如/a/b/c返回/a/b,配合下面的方法消除"."或".."
Path normalize()
//返回一個路徑,該路徑是冗餘名稱元素的消除。如消除掉"."、".."
Path getRoot() 
//返回此路徑的根組分作爲 Path對象,或 null如果該路徑不具有根組件。如返回"c:/"
Path relativize(Path other) 
//構造此路徑和給定路徑之間的相對路徑。有點難理解,p1-"Topic.txt",p2-"Demo.txt",p3-"/Java/JavaFX/Topic.txt",p4-"/Java/2011";;那麼p1和p2的結果是"../Demo.txt";;p2和p1的結果是"../Topic.txt";;p3和p4的結果是"../../2011";;p4和p3的結果是"../JavaFX/Topic.txt"
Path resolve(String other)
//將給定的路徑字符串轉換爲 Path。如"c:/a/b"和字符串"c.txt"的結果是"c:/a/b/c.txt";更像是拼接
Path resolveSibling(String other) 
//將給定的路徑字符串轉換爲 Path。如"c:/a/b.txt"和字符串"c.txt"的結果是"c:/a/c.txt";更像是替換
Path subpath(int beginIndex, int endIndex) 
//返回一個相對的 Path ,它是該路徑的名稱元素的子序列,如"d:/a/b/c.txt"參數爲(1,3)返回一個"b/c.txt"
Path toAbsolutePath() 
//返回表示此路徑的絕對路徑的 Path對象。包括盤符和文件名或文件夾名

Iterator<Path> iterator() 
//返回此路徑的名稱元素的迭代器。"c:/a/b/c.txt"的迭代器可以next出以下"a""b""c.txt"
File toFile() 
//返回表示此路徑的File對象

三.Files

Files類只包含對文件,目錄或其他類型文件進行操作的靜態方法。主要和Path接口的對象進行配合使用

1.判斷方法:

static boolean exists(Path path, LinkOption... options) 
//測試文件是否存在。  
static boolean notExists(Path path, LinkOption... options) 
//測試此路徑所在的文件是否不存在。  
static boolean isDirectory(Path path, LinkOption... options) 
//測試文件是否是目錄。  
static boolean isExecutable(Path path) 
//測試文件是否可執行。  
static boolean isHidden(Path path) 
//告知文件是否被 隱藏 。  
static boolean isReadable(Path path) 
//測試文件是否可讀。  
static boolean isRegularFile(Path path, LinkOption... options) 
//測試文件是否是具有不透明內容的常規文件。說實話,我也不太懂常規文件指的是啥
static boolean isSameFile(Path path, Path path2) 
//測試兩個路徑是否找到相同的文件。
static boolean isSymbolicLink(Path path) 
//測試文件是否是符號鏈接。//
static boolean isWritable(Path path) 
//測試文件是否可寫。  

2.刪除方法

static boolean deleteIfExists(Path path) 
//刪除文件(如果存在)。  
static void delete(Path path) 
//刪除文件。  

3.複製方法

static long copy(InputStream in, Path target, CopyOption... options) 
//將輸入流中的所有字節複製到文件。
//關於CopyOption則是一個被繼承的接口主要有枚舉類StandardCopyOption和LinkOption
//   1.StandardCopyOption
//			REPLACE_EXISTING(也就是替換覆蓋)
//          COPY_ATTRIBUTES(將源文件的文件屬性信息複製到目標文件中)
//			ATOMIC_MOVE(原子性的複製)都是字面意思
//   2.LinkOption
//			NOFOLLOW_LINKS
static long copy(Path source, OutputStream out) 
//將文件中的所有字節複製到輸出流。  
static Path copy(Path source, Path target, CopyOption... options) 
//將文件複製到目標文件。  

4.移動和重命名方法

static Path move(Path source, Path target, CopyOption... options) 
//將文件移動或重命名爲目標文件。 

5.創建文件和文件夾方法

static Path createDirectories(Path dir, FileAttribute<?>... attrs) 
//首先創建所有不存在的父目錄來創建目錄。
static Path createDirectory(Path dir, FileAttribute<?>... attrs) 
//創建一個新的目錄。  
static Path createFile(Path path, FileAttribute<?>... attrs) 
//創建一個新的和空的文件,如果該文件已存在失敗。

6.文件屬性方法

static <V extends FileAttributeView> V getFileAttributeView(Path path,<V> type, LinkOption... options) 
//返回給定類型的文件屬性視圖。指定六個視圖其中一種,上面一開始有點到。拿到的xxxAttributeView會有一個跟下面一樣名字的readAttributes方法來得到一個xxxAttributes真正的獲取操作就全是在這個xxxAttributes類的對象裏get啦
static <A extends BasicFileAttributes> A readAttributes(Path path,<A> type, LinkOption... options) 
//讀取文件的屬性作爲批量操作。指定一個xxxAttributes,得到一個實例,通過裏面的方法得到時間等基本屬性

static Object getAttribute(Path path, String attribute, LinkOption... options) 
//讀取文件屬性的值。這個 String attributes 參數的語法固定是以 view-name:comma-separated-attributes 的形式;view-name指定視圖名如basic,posix,acl等,不寫默認爲basic;有寫默認要加":";可以用"basic:*"或"*"讀取所有,又或者用"basic:size,lastModifiedTime"讀取大小和修改時間。具體還有那些屬性可以看具體指定的類,比如basic視圖就看BasicFileAttributes這個接口都有哪些方法,可以讀取哪些文件屬性。同理,下面的 String attributes 一樣是這個理
static Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options) 
//讀取一組文件屬性作爲批量操作。
static Path setAttribute(Path path, String attribute, Object value, LinkOption... options) 
//設置文件屬性的值。  

/* 下面這些也是獲取屬性的方法,不過還沒研究到是怎麼用的 */
static FileTime getLastModifiedTime(Path path, LinkOption... options) 
//返回文件的上次修改時間。  
static UserPrincipal getOwner(Path path, LinkOption... options) 
//返回文件的所有者。  
static Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options) 
//返回文件的POSIX文件權限。  
static Path setLastModifiedTime(Path path, FileTime time) 
//更新文件上次修改的時間屬性。  
static Path setOwner(Path path, UserPrincipal owner) 
//更新文件所有者。  
static Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms) 
//設置文件的POSIX權限。  
static long size(Path path) 
//返回文件的大小(以字節爲單位)。  

7.讀取、編輯文件內容方法

static BufferedReader newBufferedReader(Path path) 
//打開一個文件進行閱讀,返回一個 BufferedReader以高效的方式從文件讀取文本。  
static BufferedReader newBufferedReader(Path path, Charset cs) 
//打開一個文件進行閱讀,返回一個 BufferedReader ,可以用來以有效的方式從文件讀取文本。  
static BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options) 
//打開或創建一個寫入文件,返回一個 BufferedWriter ,可以用來以有效的方式將文本寫入文件。  
static BufferedWriter newBufferedWriter(Path path, OpenOption... options) 
//打開或創建一個寫入文件,返回一個 BufferedWriter以高效的方式寫入文件。  
static SeekableByteChannel newByteChannel(Path path, OpenOption... options) 
//打開或創建文件,返回可訪問的字節通道以訪問該文件。  
static SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) 
//打開或創建文件,返回可訪問的字節通道以訪問該文件。  
static InputStream newInputStream(Path path, OpenOption... options) 
//打開一個文件,返回輸入流以從文件中讀取。  
static OutputStream newOutputStream(Path path, OpenOption... options) 
//打開或創建文件,返回可用於向文件寫入字節的輸出流。  
static byte[] readAllBytes(Path path) 
//讀取文件中的所有字節。  
static List<String> readAllLines(Path path) 
//從文件中讀取所有行。  
static List<String> readAllLines(Path path, Charset cs) 
//從文件中讀取所有行。
static Path write(Path path, byte[] bytes, OpenOption... options) 
//將字節寫入文件。  
static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options) 
//將文本行寫入文件。  
static Path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options) 
//將文本行寫入文件。  

以上方法適用於處理中等長度的文本文件,如果要處理的文件長度比較大,或者是二進制文件,那麼還是應該使用流

8.遍歷文件列表方法

  • newDirectoryStream只是遍歷當前Path的子目錄列表,或者寫一個方法裏面遞歸調用實現遍歷到底;
  • walk則是可以通過maxDepth參數來決定遍歷的深度,後面的FileVisitOption參數可有可無;
  • list類似於newDirectoryStream,區別是walk和newDirectoryStream是遞歸的,list是非遞歸的
static DirectoryStream<Path> newDirectoryStream(Path dir) 
//打開一個目錄,返回一個DirectoryStream以遍歷目錄中的所有條目。最好用 try-with-resources 構造,可以自動關閉資源。返回的 DirectoryStream<Path> 其實可以直接使用 Iterator或者for循環 遍歷每一個 dir 下面的文件或目錄
static DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) 
//上面方法的重載,通過實現參數二(有一個 boolean accept(Path p) 方法來判斷文件是否符合需要)來達到過濾的目的。如accept方法中寫"return (Files.size(p) > 8192L);"來匹配大於8k的文件
static DirectoryStream<Path> newDirectoryStream(Path dir, String glob) 
//上面方法的重載,可以通過參數二作爲過濾匹配出對應的文件。如 newDirectoryStream(dir, "*.java") 用於遍歷目錄裏所有java後綴的文件

static Stream<Path> walk(Path start, FileVisitOption... options) 
//深度優先遍歷。返回一個 Stream ,它通過 Path根據給定的起始文件的文件樹懶惰地填充 Path 。  
static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options) 
//深度優先遍歷。返回一個 Stream ,它是通過走根據給定的起始文件的文件樹懶惰地填充 Path 。

static Stream<Path> list(Path dir) 
//返回一個懶惰的填充 Stream ,其元素是 Stream中的條目。返回的 Stream 裏封裝了一個 DirectoryStream 用於遍歷。

四.Path和Files使用

import org.junit.Test;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

public class PathAndFilesTest {
    /**
     * 創建Path
     */
    @Test
    public void createPath() throws URISyntaxException, MalformedURLException {
        //1.Paths
        Path path = Paths.get("F:/測試數據.csv");
        System.out.println(path.getFileName());
        Path path1 = Paths.get(new URI("file:///f:/測試數據.csv"));

        //2.FileSystems
        Path path2 = FileSystems.getDefault().getPath("F:/測試數據.csv");

        //3.File
        Path path3 = new File("F:/測試數據.csv").toPath();
    }


    /**
     * 創建一個空文件/文件夾
     */
    @Test
    public void create() throws IOException {
        //文件夾
        Path path = Paths.get("F:/hello");
        if (!Files.exists(path)) {//如果不存在
            Files.createDirectory(path);
            //創建多個目錄
            //Files.createDirectories(path);
        }

        //文件
        Path path1 = Paths.get("F:/helloFile.txt");
        if (!Files.exists(path1)) {//如果不存在
            Files.createFile(path1);
        }
    }

    /**
     * 文件屬性
     */
    @Test
    public void getFileProperties() throws IOException {
        Path path = Paths.get("F:/測試數據.csv");
        System.out.println(Files.getLastModifiedTime(path));//最後修改時間:2019-05-22T02:52:45.625094Z
        System.out.println(Files.getOwner(path));//擁有者:DESKTOP-GE36VVD\87772 (User)
        //System.out.println(Files.getPosixFilePermissions(path));//權限,非admin可能會報錯
        System.out.println(Files.size(path));//文件大小: 34207517
    }

    /**
     * 讀取一個文本文件
     */
    @Test
    public void readText() throws IOException {
        Path path = Paths.get("F:/test.txt");
        //通過bufferedReader讀取
        BufferedReader bufferedReader = Files.newBufferedReader(path, StandardCharsets.UTF_8);///該文件編碼是什麼newBufferedReader就必須指定什麼字符集,否則報錯

        StringBuilder sb = new StringBuilder();
        String tempString = null;
        while ((tempString = bufferedReader.readLine()) != null) {
            sb = sb.append(tempString + "\n");
        }
        System.out.println(sb);


        //通過Files方法readAllLines
        List<String> strings = Files.readAllLines(path);
        strings.forEach(System.out::println);
    }

    /**
     * 拿到文件輸入流
     *
     * @throws IOException
     */
    @Test
    public void getInputStream() throws IOException {
        Path path = Paths.get("F:/test.txt");
        InputStream inputStream = Files.newInputStream(path);

        //轉換字符流後在包裝成緩衝流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));

        StringBuilder sb = new StringBuilder();
        String tempString = null;
        while ((tempString = bufferedReader.readLine()) != null) {
            sb = sb.append(tempString + "\n");
        }
        System.out.println(sb);
    }

    /**
     * 文件寫操作
     */
    @Test
    public void writeFile() throws IOException {
        Path path = Paths.get("F:/writeFile.txt");

        //獲取寫入流
        BufferedWriter bufferedWriter = Files.newBufferedWriter(path);

        //執行寫入操作
        String str = "write file test";
        bufferedWriter.write(str);

        //關閉資源
        bufferedWriter.flush();
        bufferedWriter.close();
    }

    /**
     * 遍歷一個文件夾
     */
    @Test
    public void traverseDirectory() throws IOException {
        Path path = Paths.get("F:/test");
        Stream<Path> list = Files.list(path);
        list.forEach(p -> {
            System.out.println(p.getFileName());
        });
    }

    /**
     * 遍歷文件樹
     */
    @Test
    public void traverseTree() throws IOException {
        Path path = Paths.get("F:/test/");
        Stream<Path> walk = Files.walk(path);
        walk.forEach(path1 -> {
//      System.out.println(path1.getRoot());//根目錄
            System.out.println(path1.getFileName());//文件名
//      System.out.println(path1.getParent());//上級目錄
//      System.out.println(path1.getFileSystem());//文件系統
        });
        //還有種方式Files.walkFileTree()
    }

    /**
     * 文件複製
     */
    @Test
    public void copyFile() throws IOException {
        Path src = Paths.get("F:/測試數據.csv");
        Path dest = Paths.get("F:/test/Copy測試數據.csv");
        Files.copy(src, dest);
    }

    /**
     * 讀取權限見上面示例,設置權限
     */
    @Test
    public void writePermission() throws IOException {
        Path path = Paths.get("F:/test/導出測試數據.xlsx");
        Set<PosixFilePermission> permissionSet = new HashSet<>();
        permissionSet.add(PosixFilePermission.GROUP_WRITE);
        permissionSet.add(PosixFilePermission.OWNER_EXECUTE);
        Files.setPosixFilePermissions(path, permissionSet);
    }

/**
	 * 判斷方法
	 * @throws IOException
	 */
	@Test
	public void judge() throws IOException {
		Path path1 = Paths.get("f:\\test", "Copy測試數據.csv");
		Path path2 = Paths.get("f:\\測試數據.csv");
//		boolean exists(Path path, LinkOption … opts) : 判斷文件是否存在
		System.out.println(Files.exists(path2, LinkOption.NOFOLLOW_LINKS));//true

//		boolean isDirectory(Path path, LinkOption … opts) : 判斷是否是目錄
		//不要求此path對應的物理文件存在。
		System.out.println(Files.isDirectory(path1, LinkOption.NOFOLLOW_LINKS));//false

//		boolean isRegularFile(Path path, LinkOption … opts) : 判斷是否是文件

//		boolean isHidden(Path path) : 判斷是否是隱藏文件
		//要求此path對應的物理上的文件需要存在。纔可判斷是否隱藏。否則,拋異常。
//		System.out.println(Files.isHidden(path1));

//		boolean isReadable(Path path) : 判斷文件是否可讀
		System.out.println(Files.isReadable(path1));//true
//		boolean isWritable(Path path) : 判斷文件是否可寫
		System.out.println(Files.isWritable(path1));//true
//		boolean notExists(Path path, LinkOption … opts) : 判斷文件是否不存在
		System.out.println(Files.notExists(path1, LinkOption.NOFOLLOW_LINKS));//false
	}
}


	/**
	 * StandardOpenOption.READ:表示對應的Channel是可讀的。
	 * StandardOpenOption.WRITE:表示對應的Channel是可寫的。
	 * StandardOpenOption.CREATE:如果要寫出的文件不存在,則創建。如果存在,忽略
	 * StandardOpenOption.CREATE_NEW:如果要寫出的文件不存在,則創建。如果存在,拋異常
	 */
	@Test
	public void ioStream() throws IOException {
		Path path1 = Paths.get("f:\\test", "copyTest.txt");

//		InputStream newInputStream(Path path, OpenOption…how):獲取 InputStream 對象
		InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ);

//		OutputStream newOutputStream(Path path, OpenOption…how) : 獲取 OutputStream 對象
		OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE, StandardOpenOption.CREATE);


//		SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 獲取與指定文件的連接,how 指定打開方式。
		SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

//		DirectoryStream<Path>  newDirectoryStream(Path path) : 打開 path 指定的目錄
		Path path2 = Paths.get("f:\\test");
		DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2);
		Iterator<Path> iterator = directoryStream.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}

五.FileTime對象

表示文件時間戳屬性的值,可能會在設置文件最後更新屬性時使用到:

static FileTime fromMillis(long value) 
//返回一個 FileTime以 FileTime單位表示給定值。  
long toMillis() 
//返回以毫秒爲單位的值。
String toString() 
//返回此 FileTime的字符串表示 FileTime 。  

栗子:

/**
 * 可能你要從文件屬性中的FileTime或者到一個Date對象
 */
Path pathObj = Paths.get("C:/a/b/c.txt");
BasicFileAttributes attrs = Files.readAttributes(pathObj, BasicFileAttributes.class);
Data date = new Date(attrs.lastModifiedTime().toMillis());

/**
 * 又或者可能你要人爲地修改這個文件時間屬性,需要一個FileTime
 */
Path path = Paths.get("C:/a/b/c.txt");
long time = System.currentTimeMillis();
FileTime fileTime = FileTime.fromMillis(time);
try{
  Files.setAttribute(path, "basic:lastModifiedTime", fileTime,LinkOption.NOFOLLOW_LINKS);
}catch (IOException e) {
    System.err.println(e);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章