一、File類的介紹與使用
存儲在程序中的數據是暫時的,當程序終止時它們就會丟失。為了能夠永久的保存程序中創建的數據,需要將它們存儲到磁盤或其它永久存儲設備的文件中。這樣,這些文件其后可以被其它程序傳送和讀取。
在文件系統中,每個文件都存放在一個目錄下。絕對文件名(absolute file name)是由它的完整路徑和文件名組成的(例如,D:/home/Welcome.java,這里D:/home稱為改文件的目錄路徑)。相對文件名是相對於當前工作目錄的。對於相對文件名而言,完整目錄被忽略(例如,Welcome.java是一個相對文件名,如果當前工作目錄為D:/home,絕對文件名將是D:/home/Welcome.java)。
File類提供了文件和目錄路徑名的抽象表示。包含許多獲取文件屬性的方法,以及重命名和刪除文件和目錄的方法。(File類不包含讀寫文件內容的方法)
File(pathname: String) 為指定的路徑名創建一個File對象。路徑名可能是一個目錄或者一個文件
File(parent: String, child: String) 在目錄parent下創建一個子路徑的File對象。子路徑可能是一個目錄或者一個文件
File(parent: File, child: String) 在目錄parent下創建一個子路徑的File對象(目錄parent是一個File對象)。子路徑可能是一個目錄或者一個文件
exists(): boolean File對象代表的文件是否存在
canRead(): boolean File對象代表的文件存在且可讀,返回true
canWrite(): boolean File對象代表的文件存在且可寫,返回true
isDirectory(): boolean File對象代表的文件是否是一個目錄
isFile(): boolean File對象代表的文件是否是一個文件
isAbsolute(): boolean File對象代表的文件是否采用絕對路徑名創建
isHidden(): boolean File對象代表的文件是否隱藏
getAbsolutePath(): String 獲取File對象代表的文件的絕對路徑
getCanonicalPath(): String 和getAbsolutePath()相同。從路徑中去掉冗余的名字(比如'.'和'..'),將盤符轉換為標准的大寫形式
getName(): String 獲取文件名
getPath(): String 獲取文件完整的目錄和文件名
getParent(): String 獲取文件的完整父目錄
lastModified(): long 返回文件最后修改時間
length(): long 獲取文件大小。如果不存在或者是一個目錄,返回0
listFile(): File[] 返回一個目錄下面的文件
delete(): boolean 刪除文件,刪除成功返回true。(如果目錄不為空,不能成功刪除)
renameTo(dest: File): boolean 重命名為目標文件的名稱,成功返回true
mkdir(): boolean 創建File對象代表的目錄
mkdirs(): boolean 和mkdir()相同,父目錄不存在的情況下,將和父目錄一起創建
public class FileDemo { public static void main(String[] args) { File file = new File("D:/home/images/sufei.jpg");//采用文件的絕對名稱或者相對名稱創建文件對象 //文件是否存在 System.out.println("文件是否存在:" + file.exists()); if(file.exists()) { System.out.println("該文件是否可讀:" + file.canRead()); System.out.println("該文件是否可寫:" + file.canWrite()); System.out.println("該文件是否可執行:" + file.canExecute()); System.out.println("是否為目錄:" + file.isDirectory()); System.out.println("是否為文件:" + file.isFile()); System.out.println("是否為隱藏文件:" + file.isHidden()); System.out.println("是否為絕對文件:" + file.isAbsolute()); System.out.println("文件名稱:" + file.getName()); System.out.println("絕對文件路徑:" + file.getAbsolutePath()); System.out.println("getParent:" + file.getParent()); System.out.println("文件大小:" + file.length() + " bytes"); /* * long time = file.lastModified(); * Date date = new Date(time); * Instant instant = date.toInstant(); * LocalDateTime time2 = LocalDateTime.ofInstant(instant,ZoneId.systemDefault()); */ System.out.println("文件的最近修改時間:" + LocalDateTime.ofInstant(new Date(file.lastModified()).toInstant(), ZoneId.systemDefault())); } File f = new File("D:\\home\\images"); if(f.isDirectory()) { //得到目錄下所有的文件 File[] files = f.listFiles(); for(File f1 : files) System.out.println(f1.getName()); } File file2 = new File("D:/home/images/demo.txt"); if(!file2.exists()) { file2.mkdirs(); File dest = new File("D:/home/images/peppa"); file2.renameTo(dest);//文件重命名 file2.deleteOnExit(); }else { file2.deleteOnExit(); } } }
File對象封裝了文件或路徑屬性,但是不包含從/向文件讀/寫數據的方法。為了進行I/O操作,需要使用正確的Java I/O類創建對象。java有許多用於各種目的的I/O類。通常,可以將它們分為輸入流和輸出流。輸入流包含讀取數據的方法,而輸出流包含寫數據的方法。
使用PrintWriter寫數據
基於上圖分析,Writer作為字符輸出流的抽象基類,使用一個具體的實現子類PrintWriter來向文本文件中寫數據。PrintWriter可以創建一個文件並向文件寫入數據並提供了方便的打印輸出等方法
package edu.uestc.monster.io; import java.io.FileNotFoundException; import java.io.PrintWriter; public class WriteData { public static void main(String[] args) { // 將程序中的數據寫入到文件中,需要 使用輸出流,而當前寫入的是文本數據,采用字符輸出流 /* * PrintWriter可以創建一個文件並向文件寫入數據 * 可以很方便的打印輸出,類似於print(),println() */ PrintWriter writer = null; try { writer = new PrintWriter("score.txt"); writer.print("小豬peppa ");//輸出不換行 writer.println(90);//輸出完畢后換行 writer.print("小羊蘇西 "); writer.println(85); //writer.flush();//將流中的數據刷新到文件 //流是資源(涉及文件),不是內存資源,所以需要手動釋放資源 writer.close();//釋放資源,其中包含了writer.flush() } catch (FileNotFoundException e) { e.printStackTrace(); } finally { //在finally里確保資源的釋放 writer.close(); } } }
使用try-with-resource自動關閉資源
程序猿經常會忘記關閉資源,jdk提供了try-with-resource自動關閉資源
package edu.uestc.monster.io; import java.io.File; import java.io.IOException; import java.io.PrintWriter; public class WriteDataWithAutoClose { public static void main(String[] args) { /* * try(聲明和創建資源){ * .... * } * 在()中可以聲明多個資源,每個資源必須是Closeable類型,其中包含了close()方法 */ try(var writer = new PrintWriter(new File("students.txt"))){ writer.print("peppa "); writer.print("female "); writer.println(5); writer.print("emily "); writer.print("female "); writer.println(5); } catch (IOException e) { e.printStackTrace(); } } }
讀取文本數據
字符輸入流的抽象基類為Reader,使用具體的子類FileReader來讀取文本數據,FileReader並沒有提供新的方法,所有方法都來自於Reader,基於底層的字符讀取
package edu.uestc.monster.io; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.Scanner; public class ReadData { public static void main(String[] args) { read3(); } /** * 使用Reader的具體的節點流FileReader每次讀取一個字符 */ public static void read1() { try(Reader reader = new FileReader("students.txt")){ //System.out.println((char)reader.read());//讀取一個字符,讀到文件的末尾,返回-1 int len = -1; while((len = reader.read()) != -1) { System.out.print((char)len); } }catch (IOException e) { e.printStackTrace(); } } /** * 使用Reader的具體的節點流FileReader每次字符 * reader.read(buff):將字符讀取到緩沖數組buff中,返回的是讀取的字符的長度,讀到末尾返回-1 */ public static void read2() { try(Reader reader = new FileReader("students.txt")){ char[] buff = new char[20];//一次讀取20個字符 int len = -1; while((len = reader.read(buff)) != -1) { System.out.print(new String(buff,0,len)); } }catch (IOException e) { e.printStackTrace(); } } }
使用Scanner讀數據
在前面的使用中,java.util.Scanner類用來從控制台讀取字符串和基本數據類型。Scanner可以將輸入分為由空白字符分割的標記。為了從鍵盤讀取數據,需要為System.in創建一個Scanner:
Scanner input = new Scanner(System.in);
為了從文件中讀取,為文件創建一個Scanner:
Scanner input = new Scanner(new File(fileName));
public static void read3() { try(Scanner input = new Scanner(new File("students.txt"))){ //如果可以按照標記(空格)還有可以讀取的內容,一直讀 while(input.hasNext()) { //System.out.print(input.next() + " "); System.out.println(input.nextLine()); } }catch (IOException e) { e.printStackTrace(); } }
字節流
一個統一碼由兩個字節組成,writeChar(char c)方法將字符c的統一碼寫入輸出流。writeChars(String s)方法將字符串中每個字符統一碼的低字節寫入到輸出流中。統一碼的高字節被丟棄。wirteBytes方法適用於由ASCII碼字符構成的字符串,因為ASCII碼僅存儲統一碼的低字節。如果一個字符串包含非ASCII碼的字符,必須使用wirteBytes寫入這個字符串。
writeUTF(String s)方法將兩個字節的長度信息寫入輸出流,后面緊跟的是字符串s中的每個字符的改進版UTF-8的形式。UTF-8是一種編碼方案,允許系統同時操作統一碼和ASCII碼。ASCII碼是統一碼(Unicode)的子集。java使用統一碼。由於許多應用只需要ASCII字符集,所以將一個字節(8位)的ASCII表示為兩個字節(16位)的Unicode是很浪費的。UTF-8的修改版方案分別使用1字節,2字節或3字節來存儲字符。如果一個字符編碼值小於等於0x7F
,它由一個字節表示;如果一個字符編碼大於0x7F小於
0x7FF
的范圍內,那么它由兩個字節表示;如果一個字符編碼值大於等於0x7FF,那么它由三個字節表示。
writeUTF(String s)方法將字符串轉化成UTF-8格式的一串字節。然后將它們寫入到輸出流。readUTF()方法讀取一個使用writeUTF方法寫入的字符串。UTF-8具有存儲一個ASCII碼就節省一個字節的優勢。如果一個字符串的大多數字符都是普通的ASCII字符,采用UTF-8格式存儲更加高效。