IO流(一)

簡介

Java 的標準庫 java.io 提供了 File 對象來操作文件目錄

一、File對象

1. 構造 File 對象時,既可以傳入絕對路徑,也可以傳入相對路徑。絕對路徑是以根目錄開頭的完整路徑。
File f = new File("D:\\Notepad++");

可以用 . 表示當前目錄,..表示上級目錄。

// 假設當前目錄是C:\Docs
File f1 = new File("sub\\javac"); // 絕對路徑是C:\Docs\sub\javac
File f3 = new File(".\\sub\\javac"); // 絕對路徑是C:\Docs\sub\javac
File f3 = new File("..\\sub\\javac"); // 絕對路徑是C:\sub\javac
2.File 對象有3種形式表示的路徑,一種是 getPath(),返回構造方法傳入的路徑,一種是 getAbsolutePath(),返回絕對路徑,一種是 getCanonicalPath,它和絕對路徑類似,但是返回的是規範路徑。
File f = new File("..");
System.out.println(f.getPath());  //打印:..
System.out.println(f.getAbsolutePath()); //打印:E:\IdeaProject\news1\..
System.out.println(f.getCanonicalPath()); //打印:E:\IdeaProjec

解釋: 絕對路徑可以表示成 C:\Windows\System32\..\notepad.exe,而規範路徑就是把 . (表示當前目錄)和 .. (表示上級目錄)轉換成標準的絕對路徑後的路徑:C:\Windows\notepad.exe。

3.File 對象有一個靜態變量用於表示當前平臺的系統分隔符:
// 根據當前平臺打印"\"或"/"
System.out.println(File.separator); 
4.File對象既可以表示文件,也可以表示目錄。
 File f1 = new File("C:\\Windows");
 System.out.println(f1.isFile()); //判斷該File對象是否是一個已存在的文件
 System.out.println(f1.isDirectory()); //判斷該File對象是否是一個已存在的目錄

判斷文件的權限和大小:

  • boolean canRead(): 是否可讀;
  • boolean canWrite(): 是否可寫;
  • boolean canExecute(): 是否可執行;
  • long length(): 文件字節大小。
5.創建和刪除文件
File file = new File("/path/to/file");
if (file.createNewFile()) {
    // 文件創建成功:
    // 業務代碼。。。
    if (file.delete()) {
        // 刪除文件成功:
    }
}    
//創建一個臨時文件
File f = File.createTempFile("tmp-", ".txt"); // 提供臨時文件的前綴和後綴
f.deleteOnExit(); // JVM退出時自動刪除
6.遍歷文件和目錄
File f = new File("C:\\Windows");
File[] fs1 = f.listFiles(); // 列出所有文件和子目錄
// 僅列出.exe文件,返回true表示接受該文件
File[] fs2 = f.listFiles((dir, name) -> name.endsWith(".exe"));

File對象如果表示一個目錄,可以通過以下方法創建和刪除目錄:

  • boolean mkdir(): 創建當前File對象表示的目錄;
  • boolean mkdirs(): 創建當前File對象表示的目錄,並在必要時將不存在的父目錄也創建出來;
  • boolean delete(): 刪除當前File對象表示的目錄,當前目錄必須爲空才能刪除成功。
7.Path

java 標準庫還提供了一個 Path 對象,它位於 java.nio.file 包。Path 對象和 File 對象類似,但操作更加簡單:

Path p1 = Paths.get(".", "project", "study"); // 構造一個Path對象
System.out.println(p1);
Path p2 = p1.toAbsolutePath(); // 轉換爲絕對路徑
System.out.println(p2);
Path p3 = p2.normalize(); // 轉換爲規範路徑
System.out.println(p3);
File f = p3.toFile(); // 轉換爲File對象
System.out.println(f);
for (Path p : Paths.get("..").toAbsolutePath()) { // 可以直接遍歷Path
    System.out.println("  " + p);
}

二、InputStream

InputStream 就是 Java 標準庫提供的最基本的輸入流。InputStream 並不是一個接口,而是一個抽象類,它是所有輸入流的超類。這個抽象類定義的一個最重要的方法就是 int read()

1.FileInputStream

FileInputStream 是 InputStream 的一個子類,他可以從文件流中讀取數據。

public void readFile() throws IOException {
    // 創建一個FileInputStream對象:
    InputStream input = new FileInputStream("file/readme.txt");
    for (;;) {
        int n = input.read(); // 反覆調用read()方法,直到返回-1(表示結束)
        if (n == -1) 
            break;
        System.out.println(n); // 打印byte的值
    }
    input.close(); // 關閉流
}    

讀取過程中發生了 IO 錯誤拋出了異常,InputStream 就沒法正確地關閉。因此需要用try ... finally來保證 InputStream 在無論是否發生 IO 錯誤的時候都能夠正確地關閉:

public void readFile() throws IOException {
    InputStream input = null;
    try {
        input = new FileInputStream("file/readme.txt");
        int n;
        while ((n = input.read()) != -1) { // 利用while同時讀取並判斷
            System.out.println(n);
        }
    } finally {
        if (input != null) { input.close(); }
    }
}

或者用下面這種簡化版:

public void readFile() throws IOException {
    try (InputStream input = new FileInputStream("src/readme.txt")) {
        int n;
        while ((n = input.read()) != -1) {
            System.out.println(n);
        }
    } // 編譯器在此自動爲我們寫入finally並調用close()
}

解釋: 編譯器並不會特別地爲 InputStream 加上自動關閉。編譯器只看try(resource = ...) 中的對象是否實現了 java.lang.AutoCloseable 接口,如果實現了,就自動加上finally語句並調用 close() 方法。InputStream 和 OutputStream 都實現了這個接口,因此,都可以用上面這種寫法。

2.緩衝

很多流支持一次性讀取多個字節到緩衝區,對於文件和網絡流來說,利用緩衝區一次性讀取多個字節效率往往要高很多。InputStream 提供了兩個重載方法來支持讀取多個字節:

  • int read(byte[] b): 讀取若干字節並填充到 byte[] 數組,返回讀取的字節數
  • int read(byte[] b, int off, int len): 指定 byte[] 數組的偏移量和最大填充數
try (InputStream input = new FileInputStream("file/readme.txt")) {
   // 定義1000個字節大小的緩衝區:
    byte[] buffer = new byte[1000];
    int n;
    while ((n = input.read(buffer)) != -1) { // 讀取到緩衝區
        System.out.println("read " + n + " bytes.");
    }
}
3.阻塞

read()方法是阻塞(Blocking)的,意思是每次執行 read()方法必須等待該方法返回才能執行下一行代碼,讀取IO流相比執行普通代碼,速度會慢很多。

三、OutputStream

OutputStream 是 Java 標準庫提供的最基本的輸出流。它也是抽象類,它是所有輸出流的超類。這個抽象類定義的一個最重要的方法就是 void write(int b)

1.FileOutputStream
//一次寫入一個字節
OutputStream output = new FileOutputStream("out/readme.txt");
output.write(72); // 寫入字母H
output.close();
//一次性寫入若干字節
OutputStream output = new FileOutputStream("out/readme.txt");
//或者用下面的方式保證後寫入的內容不會覆蓋前面的內容
//OutputStream output = new FileOutputStream("out/readme.txt", true);
output.write("Hello".getBytes("UTF-8")); // 寫入字符串Hello
output.close();

避免發生 IO 異常而無法關閉流的寫法:

 try (OutputStream output = new FileOutputStream("out/readme.txt")) {
   output.write("Hello".getBytes("UTF-8")); // Hello
 } // 編譯器在此自動爲我們寫入finally並調用close()

四、參考文檔

https://www.liaoxuefeng.com/wiki/1252599548343744/1255945227202752

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章