File 類(lèi)的使用
- java.io.File 類(lèi):文件和文件目錄路徑的抽象表示形式,與平臺(tái)無(wú)關(guān)
- File 能新建、刪除、重命名文件和目錄,但不能訪問(wèn)文件內(nèi)容本身。如果需要訪問(wèn)文件內(nèi)容本身,則需要使用輸入/輸出流
- 想要在 Java 程序中表示一個(gè)真實(shí)存在的文件或目錄,那么必須有一個(gè) File 對(duì) 象,但是 Java 程序中的一個(gè) File 對(duì)象,可能沒(méi)有一個(gè)真實(shí)存在的文件或目錄
- File 對(duì)象可以作為參數(shù)傳遞給流的構(gòu)造器
File 類(lèi)的常用構(gòu)造器
-
public File(String pathname)
以 pathname 為路徑創(chuàng)建 File 對(duì)象
-
public File(String parent,String child)
以 parent 為父路徑,child 為子路徑創(chuàng)建 File 對(duì)象
-
public File(File parent,String child)
根據(jù)一個(gè)父 File 對(duì)象和子文件路徑創(chuàng)建 File 對(duì)象
路徑中的每級(jí)目錄之間用一個(gè)路徑分隔符隔開(kāi),windows和DOS系統(tǒng)默認(rèn)使用
\來(lái)表示,UNIX 和 URL 使用/來(lái)表示。Java 程序支持跨平臺(tái)運(yùn)行,因此路徑分隔符要慎用。為了解決這個(gè)隱患,F(xiàn)ile 類(lèi)提供了一個(gè)常量:
- public static final String separator
根據(jù)操作系統(tǒng),動(dòng)態(tài)的提供分隔符。
舉例:
File file1 = new File("d:\\atguigu\\info.txt"); File file2 = new File("d:" + File.separator + "atguigu" + File.separator + "info.txt"); File file3 = new File("d:/atguigu");
File 類(lèi)常用方法
File 類(lèi)的獲取功能
public String getAbsolutePath():獲取絕對(duì)路徑
public String getPath() :獲取路徑
public String getName() :獲取名稱(chēng)
public String getParent():獲取上層文件目錄路徑。若無(wú),返回 null
public long length() :獲取文件長(zhǎng)度(即:字節(jié)數(shù))。不能獲取目錄的長(zhǎng)度。
public long lastModified() :獲取最后一次的修改時(shí)間,毫秒值
public String[] list() :獲取指定目錄下的所有文件或者文件目錄的名稱(chēng)數(shù)組
public File[] listFiles() :獲取指定目錄下的所有文件或者文件目錄的 File 數(shù)組
File 類(lèi)的重命名功能
- public boolean renameTo(File dest):把文件重命名為指定的文件路徑
File 類(lèi)的判斷功能
public boolean isDirectory():判斷是否是文件目錄
public boolean isFile() :判斷是否是文件
public boolean exists() :判斷是否存在
public boolean canRead() :判斷是否可讀
public boolean canWrite() :判斷是否可寫(xiě)
public boolean isHidden() :判斷是否隱藏
File 類(lèi)的創(chuàng)建功能
public boolean createNewFile() :創(chuàng)建文件。若文件存在,則不創(chuàng)建,返回 false
public boolean mkdir() :創(chuàng)建文件目錄。如果此文件目錄存在,就不創(chuàng)建了。如果此文件目錄的上層目錄不存在,也不創(chuàng)建。
public boolean mkdirs() :創(chuàng)建文件目錄。如果上層文件目錄不存在,一并創(chuàng)建
File 類(lèi)的刪除功能
- public boolean delete():刪除文件或者文件夾
Java中的刪除不走回收站。要?jiǎng)h除一個(gè)文件目錄,請(qǐng)注意該文件目錄內(nèi)不能包含文件或者文件目錄。
IO 流原理及流的分類(lèi)
Java IO 原理
- I/O 是 Input/Output 的縮寫(xiě), I/O 技術(shù)是非常實(shí)用的技術(shù),用于處理設(shè)備之間的數(shù)據(jù)傳輸。如讀/寫(xiě)文件,網(wǎng)絡(luò)通訊等。Java 程序中,對(duì)于數(shù)據(jù)的輸入/輸出操作以
流(stream)的方式進(jìn)行。java.io 包下提供了各種流類(lèi)和接口,用以獲取不同種類(lèi)的數(shù)據(jù),并通過(guò)標(biāo)準(zhǔn)的方法輸入或輸出數(shù)據(jù)。 - 輸入input:讀取外部數(shù)據(jù)(磁盤(pán)、光盤(pán)等存儲(chǔ)設(shè)備的數(shù)據(jù))到程序(內(nèi)存)中。
- 輸出 output:將程序(內(nèi)存)數(shù)據(jù)輸出到磁盤(pán)、光盤(pán)等存儲(chǔ)設(shè)備中。
流的分類(lèi)
按操作數(shù)據(jù)單位不同分為:字節(jié)流(8 bit),字符流(16 bit)
按數(shù)據(jù)流的流向不同分為:輸入流,輸出流
按流的角色的不同分為:節(jié)點(diǎn)流,處理流

FileInputStream 用于讀取非文本數(shù)據(jù)之類(lèi)的原始字節(jié)流。要讀取字符流,需要使用 FileReader。
FileOutputStream 用于寫(xiě)出非文本數(shù)據(jù)之類(lèi)的原始字節(jié)流。要寫(xiě)出字符流,需要使用 FileWriter。
在寫(xiě)入一個(gè)文件時(shí),如果使用構(gòu)造器 FileOutputStream(file),則目錄下有同名文
件將被覆蓋。如果使用構(gòu)造器 FileOutputStream(file,true),則目錄下的同名文件不會(huì)被覆蓋,在文件內(nèi)容末尾追加內(nèi)容。在讀取文件時(shí),必須保證該文件已存在,否則報(bào)異常。
- 字節(jié)流操作字節(jié),比如:
.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt- 字符流操作字符,只能操作普通文本文件。最常見(jiàn)的文本文:
.txt,.java,.c,.cpp等語(yǔ)言的源代碼。尤其注意.doc, excel, ppt這些不是文本文件
緩沖流
為了提高數(shù)據(jù)讀寫(xiě)的速度,Java API 提供了帶緩沖功能的流類(lèi),在使用這些流類(lèi)時(shí),會(huì)創(chuàng)建一個(gè)內(nèi)部緩沖區(qū)數(shù)組,缺省使用 8192 個(gè)字節(jié)(8Kb)的緩沖區(qū)。
- 當(dāng)讀取數(shù)據(jù)時(shí),數(shù)據(jù)按塊讀入緩沖區(qū),其后的讀操作則直接訪問(wèn)緩沖區(qū)
- 當(dāng)使用 BufferedInputStream 讀取字節(jié)文件時(shí),BufferedInputStream 會(huì)一次性從文件中讀取 8192個(gè)(8Kb),存在緩沖區(qū)中,直到緩沖區(qū)裝滿(mǎn)了,才重新從文件中讀取下一個(gè) 8192 個(gè)字節(jié)數(shù)組
- 向流中寫(xiě)入字節(jié)時(shí),不會(huì)直接寫(xiě)到文件,先寫(xiě)到緩沖區(qū)中直到緩沖區(qū)寫(xiě)滿(mǎn), BufferedOutputStream 才會(huì)把緩沖區(qū)中的數(shù)據(jù)一次性寫(xiě)到文件里。使用方法 flush() 可以強(qiáng)制將緩沖區(qū)的內(nèi)容全部寫(xiě)入輸出流
- 關(guān)閉流的順序和打開(kāi)流的順序相反。只要關(guān)閉最外層流即可,關(guān)閉最外層流也會(huì)相應(yīng)關(guān)閉內(nèi)層節(jié)點(diǎn)流
- flush() 方法的使用:手動(dòng)將 buffer 中內(nèi)容寫(xiě)入文件
- 如果是帶緩沖區(qū)的流對(duì)象的 close() 方法,不但會(huì)關(guān)閉流,還會(huì)在關(guān)閉流之前刷新緩沖區(qū),關(guān)閉后不能再寫(xiě)出
轉(zhuǎn)換流
轉(zhuǎn)換流提供了在字節(jié)流和字符流之間的轉(zhuǎn)換。Java API提供了兩個(gè)轉(zhuǎn)換流:
InputStreamReader:將 InputStream 轉(zhuǎn)換為 Reader
OutputStreamWriter:將 Writer 轉(zhuǎn)換為 OutputStream
字節(jié)流中的數(shù)據(jù)都是字符時(shí),轉(zhuǎn)成字符流操作更高效,很多時(shí)候我們使用轉(zhuǎn)換流來(lái)處理文件亂碼問(wèn)題。實(shí)現(xiàn)編碼和解碼的功能。
public void testMyInput() throws Exception {
FileInputStream fis = new FileInputStream("dbcp.txt");
FileOutputStream fos = new FileOutputStream("dbcp1.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
BufferedReader br = new BufferedReader(isr);
BufferedWriter bw = new BufferedWriter(osw);
String str = null;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
數(shù)據(jù)流
為了方便地操作 Java 語(yǔ)言的基本數(shù)據(jù)類(lèi)型和 String 的數(shù)據(jù),可以使用數(shù)據(jù)流。為了方便地操作Java語(yǔ)言的基本數(shù)據(jù)類(lèi)型和 String 的數(shù)據(jù),可以使用數(shù)據(jù)流。
- DataInputStream 和 DataOutputStream
- 分別“套接”在 InputStream 和 OutputStream 子類(lèi)的流上
DataInputStream 中的方法
- boolean readBoolean()
- byte readByte()
- char readChar()
- float readFloat()
- double readDouble()
- short readShort()
- long readLong()
- int readInt()
- String readUTF()
- void readFully(byte[] b)
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream("destData.dat"));
String info = dis.readUTF();
boolean flag = dis.readBoolean();
long time = dis.readLong();
System.out.println(info);
System.out.println(flag);
System.out.println(time);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
DataOutputStream 中的方法
將上述的方法的 read 改為相應(yīng)的 write 即可。
DataOutputStream dos = null;
try { // 創(chuàng)建連接到指定文件的數(shù)據(jù)輸出流對(duì)象
dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dos.writeUTF("我愛(ài)北京天安門(mén)"); // 寫(xiě)UTF字符串
dos.writeBoolean(false); // 寫(xiě)入布爾值
dos.writeLong(1234567890L); // 寫(xiě)入長(zhǎng)整數(shù)
System.out.println("寫(xiě)文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 關(guān)閉流對(duì)象
try {
if (dos != null) {
// 關(guān)閉過(guò)濾流時(shí),會(huì)自動(dòng)關(guān)閉它包裝的底層節(jié)點(diǎn)流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
對(duì)象流(ObjectInputStream和OjbectOutputSteam)
用于存儲(chǔ)和讀取基本數(shù)據(jù)類(lèi)型數(shù)據(jù)或?qū)ο蟮奶幚砹鳌K膹?qiáng)大之處就是可以把 Java 中的對(duì)象寫(xiě)入到數(shù)據(jù)源中,也能把對(duì)象從數(shù)據(jù)源中還原回來(lái)。
序列化:用 ObjectOutputStream 類(lèi)保存基本類(lèi)型數(shù)據(jù)或?qū)ο蟮臋C(jī)制。
反序列化:用 ObjectInputStream 類(lèi)讀取基本類(lèi)型數(shù)據(jù)或?qū)ο蟮臋C(jī)制。
注意:ObjectOutputStream 和ObjectInputStream 不能序列化 static 和 transient 修飾的成員變量。
對(duì)象序列化機(jī)制允許把內(nèi)存中的 Java 對(duì)象轉(zhuǎn)換成平臺(tái)無(wú)關(guān)的二進(jìn)制流,從而允許把這種二進(jìn)制流持久地保存在磁盤(pán)上,或通過(guò)網(wǎng)絡(luò)將這種二進(jìn)制流傳輸?shù)搅硪粋€(gè)網(wǎng)絡(luò)節(jié)點(diǎn)。當(dāng)其它程序獲取了這種二進(jìn)制流,就可以恢復(fù)成原來(lái)的 Java 對(duì)象。
序列化的好處在于可將任何實(shí)現(xiàn)了 Serializable 接口的對(duì)象轉(zhuǎn)化為字節(jié)數(shù)據(jù),使其在保存和傳輸時(shí)可被還原。
序列化是 RMI(Remote Method Invoke – 遠(yuǎn)程方法調(diào)用)過(guò)程的參數(shù)和返回值都必須實(shí)現(xiàn)的機(jī)制,而 RMI 是 JavaEE 的基礎(chǔ)。因此序列化機(jī)制是 JavaEE 平臺(tái)的基礎(chǔ)。
如果需要讓某個(gè)對(duì)象支持序列化機(jī)制,則必須讓對(duì)象所屬的類(lèi)及其屬性是可序列化的,為了讓某個(gè)類(lèi)是可序列化的,該類(lèi)必須實(shí)現(xiàn)如下兩個(gè)接口之一。否則,會(huì)拋出 NotSerializableException 異常。
Serializable
Externalizable
凡是實(shí)現(xiàn) Serializable 接口的類(lèi)都有一個(gè)表示序列化版本標(biāo)識(shí)符的靜態(tài)變量:
- private static final long serialVersionUID
如果類(lèi)沒(méi)有顯示定義這個(gè)靜態(tài)常量,它的值是 Java 運(yùn)行時(shí)環(huán)境根據(jù)類(lèi)的內(nèi)部細(xì)節(jié)自動(dòng)生成的。若類(lèi)的實(shí)例變量做了修改,serialVersionUID 可能發(fā)生變化。故建議,顯式聲明。
簡(jiǎn)單來(lái)說(shuō),Java 的序列化機(jī)制是通過(guò)在運(yùn)行時(shí)判斷類(lèi)的 serialVersionUID 來(lái)驗(yàn)證版本一致性的。在進(jìn)行反序列化時(shí),JVM 會(huì)把傳來(lái)的字節(jié)流中的 serialVersionUID 與本地相應(yīng)實(shí)體類(lèi)的 serialVersionUID 進(jìn)行比較,如果相同就認(rèn)為是一致的,可以進(jìn)行反序列化,否則就會(huì)出現(xiàn)序列化版本不一致的異常(InvalidCastException)。
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“data.txt"));
Person p = new Person("韓梅梅", 18, "中華大街", new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
注意:寫(xiě)一次,操作 flush() 一次。
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“data.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();
隨機(jī)存取文件流(RandomAccessFile 類(lèi))的使用
RandomAccessFile 聲明在 java.io 包下,但直接繼承于 java.lang.Object 類(lèi)。并且它實(shí)現(xiàn)了 DataInput、DataOutput 這兩個(gè)接口,也就意味著這個(gè)類(lèi)既可以讀也可以寫(xiě)。
構(gòu)造器
- public RandomAccessFile(File file, String mode)
- public RandomAccessFile(String name, String mode)
創(chuàng)建 RandomAccessFile 類(lèi)實(shí)例需要指定一個(gè) mode 參數(shù),該參數(shù)指定 RandomAccessFile 的訪問(wèn)模式:
- r:以只讀方式打開(kāi)
- rw:打開(kāi)以便讀取和寫(xiě)入
- rwd:打開(kāi)以便讀取和寫(xiě)入;同步文件內(nèi)容的更新
- rws::打開(kāi)以便讀取和寫(xiě)入;同步文件內(nèi)容和元數(shù)據(jù)的更新
如果模式為只讀 r。則不會(huì)創(chuàng)建文件,而是會(huì)去讀取一個(gè)已經(jīng)存在的文件,如果讀取的文件不存在則會(huì)出現(xiàn)異常。 如果模式為 rw 讀寫(xiě)。如果文件不存在則會(huì)去創(chuàng)建文件,如果存在則不會(huì)創(chuàng)建。
RandomAccessFile 對(duì)象包含一個(gè)記錄指針,用以標(biāo)示當(dāng)前讀寫(xiě)處的位置。 RandomAccessFile 類(lèi)對(duì)象可以自由移動(dòng)記錄指針:
- long getFilePointer():獲取文件記錄指針的當(dāng)前位置
- void seek(long pos):將文件記錄指針定位到 pos 位置
// 讀取文件
RandomAccessFile raf = new RandomAccessFile(“test.txt”, “rw”);
// 指針跳到角標(biāo)為5的位置
raf.seek(5);
byte [] b = new byte[1024];
int off = 0;
int len = 5;
raf.read(b, off, len);
String str = new String(b, 0, len);
System.out.println(str);
raf.close();
// 寫(xiě)入文件
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
raf.seek(5);
//先讀出來(lái)
String temp = raf.readLine();
// 指針跳到角標(biāo)為5的位置,可以理解為在角標(biāo)為5的位置插入數(shù)據(jù)
raf.seek(5);
raf.write("xyz".getBytes());
raf.write(temp.getBytes());
raf.close();
我們可以用 RandomAccessFile 這個(gè)類(lèi),來(lái)實(shí)現(xiàn)一個(gè)多線程斷點(diǎn)下載的功能,用過(guò)下載工具的朋友們都知道,下載前都會(huì)建立兩個(gè)臨時(shí)文件,一個(gè)是與被下載文件大小相同的空文件,另一個(gè)是記錄文件指針的位置文件,每次暫停的時(shí)候,都會(huì)保存上一次的指針,然后斷點(diǎn)下載的時(shí)候,會(huì)繼續(xù)從上一次的地方下載,從而實(shí)現(xiàn)斷點(diǎn)下載或上傳的功能。