Java之路:檔案操作類——File
要把程式所處理的資料在不同的記憶體容器(記憶體或外存)進行傳輸,例如將記憶體的資料寫到外存(某個檔案中),就要用到I/O(輸入/輸出)技術。Java提供的I/O操作可以把資料儲存到多種型別的檔案中。
大多數的應用程式都需要與外部的輸入/輸出裝置I/O(Input/Output)進行資料交換。在Java中,所有的I/O機制都是基於資料“流”方式進行輸入/輸出。
這些“資料流”可視為同一臺計算機不同裝置或網路中不同計算機之間流動的資料序列。如同水管裡的水流一樣,在水管的一端一點一滴地供水,而在水管的另一端看到的是一股連續不斷的水流。
Java把這些不同來源和目標的資料統一抽象為 “資料流”
。當Java程式需要讀取資料時,就會開啟一個通向資料來源的流,這個資料來源可以是檔案、記憶體,也可以是網路連線。
當Java程式需要寫入資料時,也會開啟一個通向目的地的流。
流為操作各種物理裝置提供了一致的介面。通過開啟操作將流關聯到檔案,通過關閉流操作將流和檔案解除關聯。
這些流序列中的資料通常有兩種形式:文字流和二進位制流。
文字流每一個位元組存放一個ASCII碼,代表一個字元(而對於Unicode編碼來說,每兩個位元組表示一個字元)。
使用文字流時,可能會發生一些字元轉換。例如,在Windows作業系統中,當輸出換行字元的時候,它可以被轉換為回車和換行序列。
二進位制流,也稱位元組流,它是把資料按其記憶體中儲存的以位元組形式“原封不動”地輸出或儲存。
兩者的區別與聯絡可以用下面的例子(以ASCII碼為例)來說明。例如,有一個整型數12345,其在記憶體當中僅需要2個位元組,由於系統為整型資料分配4個位元組,所以其高位兩個位元組均為0,而按文字流形式輸出則佔用5個位元組,分別是“12345”這5個字元對應的ASCII碼,如圖下所示:
文字流形式與字元一一對應,因而便於對字元進行逐個處理,也便於輸出顯示,但一般佔用較多的記憶體空間,且花費較多的轉化時間(二進位制形式與編碼之間的轉換)。
需要注意的是,在Java中使用的是Unicode編碼,這是一種定長編碼,每個字元都是2位元組,因此在儲存ASCII碼時會額外浪費一個位元組的空間。
二進位制形式輸出數值,可以節省外存空間和轉化時間,但一個位元組並不對應一個字元,不能直接輸出字元形式。
一般來講,對於純文字資訊(比如說字串),以文字形式儲存較佳;而對於數值資訊,則用二進位制形式較好。
I/O流的優勢在於簡單易用,缺點是效率較低。
Java的I/O流提供了讀寫資料的標準方法。Java語言中定義了許多類專門負責各種方式的輸入/輸出,這些類都被放在java.io包中。
在Java類庫中,有關I/O操作的內容非常龐大:有標準輸入/輸出、檔案的操作、網路上的資料流、字串流和物件流等。
一、檔案操作類——File
包java.io中定義的大多數類是對資料實施流式操作的,但File類例外,它用於處理檔案和檔案系統。也就是說,File類沒有指定資料怎樣從檔案讀取或向檔案儲存,它僅僅描述了檔案本身的屬性。
在java.io包之中,File類是唯一一個與檔案本身有關的操作類。 它定義了一些與平臺無關的方法來操作檔案,通過呼叫File類提供的各種方法,能夠完成建立、刪除檔案,重新命名檔案,判斷檔案的讀寫許可權及檔案是否存在,設定和查詢檔案建立時間、許可權等操作。File類除了對檔案操作外,還可以將目錄當作檔案進行處理——Java中的目錄當成File物件對待。
如果要想使用File類進行操作,那麼就必須設定一個要操作檔案的路徑。 下面的三個構造方法可以用來生成File物件:
在這裡,“directoryPath”表示的是檔案的路徑名,filename 是檔名,而dirObj 是一個指定目錄的File物件。
//建立指定檔案或目錄路徑的File物件
File(String directoryPath)
//建立由File物件和指定檔名的File物件
File(String directoryPath,String filename)
//建立指定檔案目錄路徑和檔名的File物件
File(File dirObj,String filename)
下面的例子分別用上面的3個構造方法建立了三個檔案物件:F1,F2和F3。
F1是由僅有一個目錄路徑引數的構造方法生成的。
F2是由兩個引數——路徑和檔名的構造方法生成的。
F3的引數包括指向檔案F1的路徑及檔名。事實上,F3和F2指向相同的檔案——在根目錄(/)下的檔案abc.txt。
File F1 = new File("/");
File F2 = new File("/","abc.txt");
File F3 = new File(F1," abc.txt");
Java 能正確處理UNIX和Windows/DOS約定路徑分隔符。如果在Windows版本的Java下用斜線(/),路徑處理依然正確。請注意:如果在Windows/DOS下使用反斜線(\)來作為路徑分隔符,那麼就需要在字串內使用它的轉義序列(即兩個反斜線“\”)。Java約定是用UNIX和URL風格的斜線“/”來作路徑分隔符。
File類中定義了很多獲取File物件標準屬性的方法。例如getName( )用於返回檔名,getParent( )返回父目錄名;exists( )方法在檔案存在的情況下返回true,反之返回false。但File類的方法是不對稱的,意思是說雖然存在可以驗證一個簡單檔案物件屬性的很多方法,但是沒有相應的方法來改變這些屬性。下表給出了部分常用的File類方法:
方法 | 功能 |
---|---|
boolean canRead() | 測試應用程式是否能從指定的檔案中進行讀取 |
boolean canWrite() | 測試應用程式是否能寫當前檔案 |
boolean delete() | 刪除當前物件指定的檔案 |
boolean equals(Object obj) | 比較該物件和指定物件 |
boolean exists() | 測試當前File是否存在 |
String getAbsolutePath() | 返回由該物件表示的檔案的絕對路徑名 |
String getCanonicalPath() | 返回當前File物件的路徑名的規範格式 |
String getName() | 返回表示當前物件的檔名 |
String getParent() | 返回當前File物件路徑名的父路徑名,如果此名沒有父路徑則為null |
String getPath | 返回當前物件的路徑名 |
boolean isAbsolute() | 測試當前File物件表示 的檔案是否是一個絕對路徑名 |
boolean isDirectory() | 測試當前File物件表示的檔案是否是一個路徑 |
boolean isFile() | 測試當前File物件表示的檔案是否是一個“普通”檔案 |
boolean lastModified() | 返回當前File物件表示的檔案最後修改的時間 |
long length() | 返回當前File物件表示的檔案長度 |
String list() | 返回當前File物件指定的路徑檔案列表 |
String list(Filename Filter) | 返回當前File物件指定的目錄中滿足指定過濾器的檔案列表 |
boolean mkdir() | 建立一個目錄,它的路徑名由當前File物件指定 |
boolean mkdirs() | 建立一個目錄,它的路徑由當前File物件指定,包括任一必須的父路徑 |
boolean renameTo(File file) | 將當前File物件指定的檔案更名為給定引數File指定的路徑名 |
下面看個示例:
package com.xy.io;
import java.io.File;
public class FileDemo1 {
public static void main(String[] args) {
File f = new File("C:\\Users\\XY\\Desktop\\FileDemo1.txt");
if(f.exists()) {
f.delete();
}
else {
try {
f.createNewFile();
}
catch(Exception e) {
e.printStackTrace();
}
}
// getName()方法,獲得檔名
System.out.println("檔名:" + f.getName());
// getPath()方法,獲得檔案路徑
System.out.println("檔案路徑:" + f.getPath());
// getAbsolutePath()方法,
System.out.println("絕對路徑:" + f.getAbsolutePath());
// getParent()
System.out.println("父資料夾名:" + f.getParent());
// exists()
System.out.println(f.exists() ? "檔案存在" : "檔案不存在");
// canRead()
System.out.println(f.canRead() ? "檔案可讀" : "檔案不可讀");
// canWrite()
System.out.println(f.canWrite() ? "檔案可寫" : "檔案不可寫");
// isDirectory()
System.out.println((f.isDirectory() ? "是" : "不是") + "目錄");
// isFile()
System.out.println(f.isFile() ? "是檔案" : "不是檔案");
// isAbsolute()
System.out.println((f.isAbsolute() ? "是" : "不是") + "絕對路徑");
// lastModified()
System.out.println("檔案最後修改時間:" + f.lastModified());
// length()
System.out.println("檔案大小:" + f.length() + "Bytes");
}
}
【結果】
路徑的分隔符用兩個“\”表示轉義字元,這一句完全可用下面的語句代替。
File f = new File("c:/1.txt") ;
在File類中還有許多的方法,沒有必要去死記這些用法,只要記住在需要的時候去查Java的API手冊就可以了。
File類只能對檔案進行一些簡單操作,如讀取檔案的屬性以及建立、刪除和更名等,但並不支援檔案內容的讀/寫。如果想對檔案進行實施讀寫操作,就必須通過輸入/輸出流來達到這一目的。
以上的程式完成了檔案的基本操作,但是在本操作之中可以發現如下的問題:
問題一:在進行操作的時候出現了延遲,因為檔案的管理肯定還是由作業系統完成的,那麼程式通過JVM(Java虛擬機器)與作業系統進行操作,多了一層操作,所以勢必會產生一定的延遲。
問題二:在Windows之中路徑的分隔符使用“\”,而在Linux中分隔符使用“/”,而現在Java程式如果要想讓其具備可移植性,就必須考慮分隔符的問題,所以為了解決這樣的困難,在File類中提供了一個常量:
public static final String separator。
File file = new File("c:" + File.separator + "1.txt"); // 要定義的操作檔案路徑
在日後的開發之中,只要遇見路徑分隔符的問題,都可用separator常量來解決。
問題三:以上的程式是直接在d盤的根路徑下建立的新檔案,如果說現在有目錄的時候就發現無法直接建立檔案了,因為檔案目錄不存在,要想建立檔案之前首先要先建立目錄。
建立一級目錄:public boolean mkdir();
建立多級目錄:public boolean mkdirs();
而如果要想建立目錄應該是根據給定路徑的父路徑才可以建立,所以要想取得父路徑可以使用如下方法。
取得父路徑:public File getParentFile();
程式碼如下所示:建立檔案
package com.xy.io;
import java.io.File;
public class FileDemo2 {
public static void main(String[] args) throws Exception {
File file = new File("d:" + File.separator +
"mytest" + File.separator +
"demo" + File.separator +
"mldn.txt"); // 要操作檔案的路徑
if(!file.getParentFile().exists()) { // 父路徑不存在
file.getParentFile().mkdirs(); // 建立目錄
}
System.out.println(file.createNewFile());
}
}
【結果】