在我的前面幾篇文章中,我們介紹了流的概念,使用流我們可以任意讀取寫入文件中的內容,而對于文件的操作來說,更多的是對文件的存儲進行操作,創建文件再磁盤上,移動文件到指定位置上,更改文件的文件名等。這些操作更多的是和操作系統以及文件系統打交道。首先我們看File類。
一個File類對象可以存放的是目錄,也可以是文件。實際上在java 7開始,引進了Files類,集合Path接口實現了對整個文件操作的分離,Files類操作文件,Path操作路徑。這篇文章先介紹File類。
一、File的構造方法
public File(String pathname)
public File(String parent, String child)
public File(File parent, String child)
主要有三個構造方法,第一個構造方法允許傳入一個表示路徑的字符串,可以是絕對路徑也可以是相對路徑。內部調用文件系統類的方法為File對象中的實例域初始化。第二個構造方法允許傳入兩個字符串,由命名可以看出必然是可以拼接的,當然如果parent為空的話,自然是以child為路徑初始化參數,這就等于第一種方式了。第三種構造方法,傳入了一個File類的對象作為parent,其實在內部還是將此parent.path的路徑值拿出來,執行的是和第二種構造方法一樣的操作。
二、文件名和文件路徑操作
我們知道,File對象既可以存文件也可以存路徑,那么對他們的相關操作有如下幾種:
public String getName()
public String getPath()
public boolean isAbsolute()
public String getParent()
public File getParentFile()
public String getAbsolutePath()
public File getAbsoluteFile()
public String getCanonicalPath()
public File getCanonicalFile()
主要有上述幾種對文件名和文件路徑的操作。getName很簡單,就是獲取該文件對象的文件名,內部是怎么實現的呢?我們看看代碼:
public String getName() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) return path.substring(prefixLength);
return path.substring(index + 1);
}
separatorChar 表示路徑分隔符,在windows中,一般默認使用“\”,作為文件分隔符,Linux系統中使用“/”。prefixLength表示文件前綴名長度。(也就是最后一個路徑分隔符前面的所有字符串的長度),此處index拿到最后一個路徑分隔符的索引,截取此位置后面的字符串作為結果返回。這樣無論File對象中存放的是什么,都可以拿到它的name。
public class Test_InputOrOutput {
public static void main(String[] args) throws IOException{
File f = new File("C://Users/directory");
System.out.println(f.getName());
}
}
//即使File對象中存放的是路徑,依然返回路徑名directory
接下來看看getPath這個方法。getPath就一條語句,返回該對象的私有成員path,這是當初調用構造方法傳入的pathName。isAbsolute方法調用文件系統類的方法判斷當前pathName是否是絕對路徑,getAbsolutePath獲取當前File對象的絕對路徑,相對于當前項目根目錄的位置來指定的。
getParent方法返回當前文件對象的父目錄路徑。內部是這樣實現的:
public String getParent() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) {
if ((prefixLength > 0) && (path.length() > prefixLength))
return path.substring(0, prefixLength);
return null;
}
return path.substring(0, index);
}
和getName方法類似,都是首先找到默認路徑分隔符的最后一次出現的位置,然后通過一系列判斷處理了各種意外的情況,例如:
File f = new File("directory");
System.out.println(f.getParent()); //輸出:null
File f = new File("directory/");
System.out.println(f.getParent()); //輸出:null
File f = new File("a/directory");
System.out.println(f.getParent()); //輸出:a
只有路徑正常的時候才會執行:return path.substring(0, prefixLength);,返回對應的父目錄的路徑。getParentFile方法返回父目錄對應的File對象。
public File getParentFile() {
String p = this.getParent();
if (p == null) return null;
return new File(p, this.prefixLength);
}
從getParentFile的源代碼中,我們可以看到,它調用了getParent方法獲取父目錄路徑,但是如果該路徑是空的的話就返回null,否則根據此路徑構建一個新的File對象并返回。像下面這種情況就不能正確獲取getParentFile:
File f = new File("directory");
File parent = f.getParentFile();
System.out.println(parent.getPath());
//拋出空指針異常,說明parent為null
對于這種情況難道就沒有辦法了嗎?其實可以組合getAbsoluteFile方法和getParentFile方法,來獲取父目錄的File對象。
File f = new File("directory");
File parent = f.getAbsoluteFile().getParentFile();
System.out.println(parent.getPath());
三、文件的信息
在我們的文件操作中,File對象中既可以存放文件有可以存放路徑信息,那我們怎么區分他們呢?其實在File中還封裝了一些判斷文件信息的方法。
public boolean canRead()
public boolean canWrite()
public boolean exists()
public boolean isDirectory()
public boolean isFile()
public boolean isHidden()
public long lastModified() //最后一次修改的時間
從方法的命名大家就可以看出來每個方法所代表的含義。(世界上最好的注釋就是沒有注釋,單命名就已經讓人理解其作用)
四、操作文件
最后是文件的操作,真正意義上的對文件在磁盤上的存儲方式進行操作。java中的File對象被創建出來之后,并不意味著在磁盤上已經創建了對應的文件,真正想要在磁盤上創建文件需要調用createNewFile方法。不用看實現代碼,這么底層的操作肯定調用的是文件系統類中的方法。
使用createNewFile創建文件成功返回true,失敗返回false。如果文件已經存在則不會去創建他,但是返回false。
文件的刪除主要有兩個方法。delete和deleteOnExit,前者會刪除文件或者目錄返回boolean,后者標記一下,等到虛擬機退出時候進行實際刪除操作。需要注意的是如果將要刪除的文件目錄不為空就不能完成刪除操作。
File f = new File("a");
f.mkdir();
File d = new File("a/a.txt");
d.createNewFile();
System.out.println(f.delete());
//輸出結果:false
所以在我們將要刪除一個目錄的時候就需要清空其中的所有內容。
五、目錄操作
最后說說目錄操作,其實在我們上面的代碼中也已經稍有涉及了。創建一個目錄有兩種方法:
public boolean mkdir()
public boolean mkdirs()
這兩個操作有一定的區別,比如我要在c盤根目錄下創建一個名為a的文件夾,new File("C://a").mkdir();或者new File("C://a").mkdirs();。浪著的效果是一樣的。但是如果我要在c盤的b文件夾中創建一個d目錄,其中b文件夾不存在。mkdir方法就無法完成任務了,因為中間有個文件夾b沒有被創建。而mkdirs會將b文件夾也一起創建了。這就是它倆的區別。
關于目錄操作的最后一個就是目錄的遍歷。File中也提供了很多方法。
public String[] list()
public File[] listFiles()
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)
public File[] listFiles(FileFilter filter)
調用list返回目錄下的所有文件的name:
File f = new File("f:/360");
String[] list = f.list();
for (String s : list){
System.out.println(s);
}
//這是在我的f盤下的360文件夾中的所有文件名
輸出結果:
360defender
360sdSetup.exe
360zip
毋庸置疑,listFiles返回的是這些文件名構成的File對象數組。我們重點看剩下的幾個方法,他們都傳入了一個參數,這個參數其實就是過濾器,用來對目錄下的文件進行進一步的篩選。他們兩個都是函數式接口,只有一個方法。該方法返回true表示遍歷中的當前的目錄或者文件可以作為結果返回到結果集中。
public interface FileFiter{
boolean accept(File pathName);
}
public interface FilenameFiter{
boolean accept(File dir,String name);
}
針對上述介紹,演示一個實例,使用的還是我的f盤下的360文件夾。
public static void main(String[] args) throws IOException{
File f = new File("f:/360");
File[] s = f.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory() ? true : false;
}
});
for (File name : s){
System.out.println(name.getName());
}
}
上述代碼過濾篩選出,360文件夾中所有目錄的文件,輸出他們的名字。
本篇文章結束,主要介紹了File類的一些使用情況,實際上java 7 中引進了Files類和Path接口實現了分離File類的作用,下篇文章我們一起探討,本文若有不當之處,希望指出!