File 類的使用
- java.io.File 類:文件和文件目錄路徑的抽象表示形式,與平臺無關(guān)
- File 能新建纷闺、刪除算凿、重命名文件和目錄,但不能訪問文件內(nèi)容本身犁功。如果需要訪問文件內(nèi)容本身氓轰,則需要使用輸入/輸出流
- 想要在 Java 程序中表示一個真實存在的文件或目錄,那么必須有一個 File 對 象浸卦,但是 Java 程序中的一個 File 對象署鸡,可能沒有一個真實存在的文件或目錄
- File 對象可以作為參數(shù)傳遞給流的構(gòu)造器
File 類的常用構(gòu)造器
-
public File(String pathname)
以 pathname 為路徑創(chuàng)建 File 對象
-
public File(String parent,String child)
以 parent 為父路徑,child 為子路徑創(chuàng)建 File 對象
-
public File(File parent,String child)
根據(jù)一個父 File 對象和子文件路徑創(chuàng)建 File 對象
路徑中的每級目錄之間用一個路徑分隔符隔開限嫌,windows和DOS系統(tǒng)默認(rèn)使用
\
來表示靴庆,UNIX 和 URL 使用/
來表示。Java 程序支持跨平臺運行萤皂,因此路徑分隔符要慎用撒穷。為了解決這個隱患,F(xiàn)ile 類提供了一個常量:
- public static final String separator
根據(jù)操作系統(tǒ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 類常用方法
File 類的獲取功能
public String getAbsolutePath():獲取絕對路徑
public String getPath() :獲取路徑
public String getName() :獲取名稱
public String getParent():獲取上層文件目錄路徑禽笑。若無,返回 null
public long length() :獲取文件長度(即:字節(jié)數(shù))蛤奥。不能獲取目錄的長度佳镜。
public long lastModified() :獲取最后一次的修改時間,毫秒值
public String[] list() :獲取指定目錄下的所有文件或者文件目錄的名稱數(shù)組
public File[] listFiles() :獲取指定目錄下的所有文件或者文件目錄的 File 數(shù)組
File 類的重命名功能
- public boolean renameTo(File dest):把文件重命名為指定的文件路徑
File 類的判斷功能
public boolean isDirectory():判斷是否是文件目錄
public boolean isFile() :判斷是否是文件
public boolean exists() :判斷是否存在
public boolean canRead() :判斷是否可讀
public boolean canWrite() :判斷是否可寫
public boolean isHidden() :判斷是否隱藏
File 類的創(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 類的刪除功能
- public boolean delete():刪除文件或者文件夾
Java中的刪除不走回收站娜睛。要刪除一個文件目錄,請注意該文件目錄內(nèi)不能包含文件或者文件目錄卦睹。
IO 流原理及流的分類
Java IO 原理
- I/O 是 Input/Output 的縮寫畦戒, I/O 技術(shù)是非常實用的技術(shù),用于處理設(shè)備之間的數(shù)據(jù)傳輸结序。如讀/寫文件障斋,網(wǎng)絡(luò)通訊等。Java 程序中笼痹,對于數(shù)據(jù)的輸入/輸出操作以
流(stream)
的方式進行配喳。java.io 包下提供了各種流
類和接口,用以獲取不同種類的數(shù)據(jù)凳干,并通過標(biāo)準(zhǔn)的方法輸入或輸出數(shù)據(jù)晴裹。 - 輸入input:讀取外部數(shù)據(jù)(磁盤、光盤等存儲設(shè)備的數(shù)據(jù))到程序(內(nèi)存)中救赐。
- 輸出 output:將程序(內(nèi)存)數(shù)據(jù)輸出到磁盤涧团、光盤等存儲設(shè)備中。
流的分類
按操作數(shù)據(jù)單位不同分為:字節(jié)流(8 bit)经磅,字符流(16 bit)
按數(shù)據(jù)流的流向不同分為:輸入流泌绣,輸出流
按流的角色的不同分為:節(jié)點流,處理流
FileInputStream 用于讀取非文本數(shù)據(jù)之類的原始字節(jié)流预厌。要讀取字符流阿迈,需要使用 FileReader。
FileOutputStream 用于寫出非文本數(shù)據(jù)之類的原始字節(jié)流轧叽。要寫出字符流苗沧,需要使用 FileWriter刊棕。
在寫入一個文件時,如果使用構(gòu)造器 FileOutputStream(file)待逞,則目錄下有同名文
件將被覆蓋甥角。如果使用構(gòu)造器 FileOutputStream(file,true),則目錄下的同名文件不會被覆蓋识樱,在文件內(nèi)容末尾追加內(nèi)容嗤无。在讀取文件時,必須保證該文件已存在怜庸,否則報異常当犯。
- 字節(jié)流操作字節(jié),比如:
.mp3割疾,.avi灶壶,.rmvb,mp4杈曲,.jpg,.doc胸懈,.ppt
- 字符流操作字符担扑,只能操作普通文本文件。最常見的文本文:
.txt趣钱,.java涌献,.c,.cpp
等語言的源代碼首有。尤其注意.doc, excel, ppt
這些不是文本文件
緩沖流
為了提高數(shù)據(jù)讀寫的速度燕垃,Java API 提供了帶緩沖功能的流類,在使用這些流類時井联,會創(chuàng)建一個內(nèi)部緩沖區(qū)數(shù)組卜壕,缺省使用 8192 個字節(jié)(8Kb)的緩沖區(qū)。
- 當(dāng)讀取數(shù)據(jù)時烙常,數(shù)據(jù)按塊讀入緩沖區(qū)轴捎,其后的讀操作則直接訪問緩沖區(qū)
- 當(dāng)使用 BufferedInputStream 讀取字節(jié)文件時,BufferedInputStream 會一次性從文件中讀取 8192個(8Kb)蚕脏,存在緩沖區(qū)中侦副,直到緩沖區(qū)裝滿了,才重新從文件中讀取下一個 8192 個字節(jié)數(shù)組
- 向流中寫入字節(jié)時驼鞭,不會直接寫到文件秦驯,先寫到緩沖區(qū)中直到緩沖區(qū)寫滿, BufferedOutputStream 才會把緩沖區(qū)中的數(shù)據(jù)一次性寫到文件里挣棕。使用方法 flush() 可以強制將緩沖區(qū)的內(nèi)容全部寫入輸出流
- 關(guān)閉流的順序和打開流的順序相反译隘。只要關(guān)閉最外層流即可亲桥,關(guān)閉最外層流也會相應(yīng)關(guān)閉內(nèi)層節(jié)點流
- flush() 方法的使用:手動將 buffer 中內(nèi)容寫入文件
- 如果是帶緩沖區(qū)的流對象的 close() 方法,不但會關(guān)閉流细燎,還會在關(guān)閉流之前刷新緩沖區(qū)两曼,關(guān)閉后不能再寫出
轉(zhuǎn)換流
轉(zhuǎn)換流提供了在字節(jié)流和字符流之間的轉(zhuǎn)換。Java API提供了兩個轉(zhuǎn)換流:
InputStreamReader:將 InputStream 轉(zhuǎn)換為 Reader
OutputStreamWriter:將 Writer 轉(zhuǎn)換為 OutputStream
字節(jié)流中的數(shù)據(jù)都是字符時玻驻,轉(zhuǎn)成字符流操作更高效悼凑,很多時候我們使用轉(zhuǎn)換流來處理文件亂碼問題。實現(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 語言的基本數(shù)據(jù)類型和 String 的數(shù)據(jù)户辫,可以使用數(shù)據(jù)流。為了方便地操作Java語言的基本數(shù)據(jù)類型和 String 的數(shù)據(jù)嗤锉,可以使用數(shù)據(jù)流渔欢。
- DataInputStream 和 DataOutputStream
- 分別“套接”在 InputStream 和 OutputStream 子類的流上
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ù)輸出流對象
dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dos.writeUTF("我愛北京天安門"); // 寫UTF字符串
dos.writeBoolean(false); // 寫入布爾值
dos.writeLong(1234567890L); // 寫入長整數(shù)
System.out.println("寫文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 關(guān)閉流對象
try {
if (dos != null) {
// 關(guān)閉過濾流時,會自動關(guān)閉它包裝的底層節(jié)點流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
對象流(ObjectInputStream和OjbectOutputSteam)
用于存儲和讀取基本數(shù)據(jù)類型數(shù)據(jù)或?qū)ο蟮奶幚砹魑脸馈K膹姶笾幘褪强梢园?Java 中的對象寫入到數(shù)據(jù)源中奥额,也能把對象從數(shù)據(jù)源中還原回來。
序列化:用 ObjectOutputStream 類保存基本類型數(shù)據(jù)或?qū)ο蟮臋C制访诱。
反序列化:用 ObjectInputStream 類讀取基本類型數(shù)據(jù)或?qū)ο蟮臋C制垫挨。
注意:ObjectOutputStream 和ObjectInputStream 不能序列化 static 和 transient 修飾的成員變量。
對象序列化機制允許把內(nèi)存中的 Java 對象轉(zhuǎn)換成平臺無關(guān)的二進制流触菜,從而允許把這種二進制流持久地保存在磁盤上九榔,或通過網(wǎng)絡(luò)將這種二進制流傳輸?shù)搅硪粋€網(wǎng)絡(luò)節(jié)點。當(dāng)其它程序獲取了這種二進制流涡相,就可以恢復(fù)成原來的 Java 對象哲泊。
序列化的好處在于可將任何實現(xiàn)了 Serializable 接口的對象轉(zhuǎn)化為字節(jié)數(shù)據(jù),使其在保存和傳輸時可被還原催蝗。
序列化是 RMI(Remote Method Invoke – 遠程方法調(diào)用)過程的參數(shù)和返回值都必須實現(xiàn)的機制切威,而 RMI 是 JavaEE 的基礎(chǔ)。因此序列化機制是 JavaEE 平臺的基礎(chǔ)丙号。
如果需要讓某個對象支持序列化機制牢屋,則必須讓對象所屬的類及其屬性是可序列化的,為了讓某個類是可序列化的槽袄,該類必須實現(xiàn)如下兩個接口之一烙无。否則,會拋出 NotSerializableException 異常遍尺。
Serializable
Externalizable
凡是實現(xiàn) Serializable 接口的類都有一個表示序列化版本標(biāo)識符的靜態(tài)變量:
- private static final long serialVersionUID
如果類沒有顯示定義這個靜態(tài)常量截酷,它的值是 Java 運行時環(huán)境根據(jù)類的內(nèi)部細節(jié)自動生成的。若類的實例變量做了修改乾戏,serialVersionUID 可能發(fā)生變化迂苛。故建議三热,顯式聲明。
簡單來說三幻,Java 的序列化機制是通過在運行時判斷類的 serialVersionUID 來驗證版本一致性的就漾。在進行反序列化時,JVM 會把傳來的字節(jié)流中的 serialVersionUID 與本地相應(yīng)實體類的 serialVersionUID 進行比較念搬,如果相同就認(rèn)為是一致的抑堡,可以進行反序列化,否則就會出現(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();
注意:寫一次首妖,操作 flush() 一次。
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“data.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();
隨機存取文件流(RandomAccessFile 類)的使用
RandomAccessFile 聲明在 java.io 包下爷恳,但直接繼承于 java.lang.Object 類有缆。并且它實現(xiàn)了 DataInput、DataOutput 這兩個接口温亲,也就意味著這個類既可以讀也可以寫棚壁。
構(gòu)造器
- public RandomAccessFile(File file, String mode)
- public RandomAccessFile(String name, String mode)
創(chuàng)建 RandomAccessFile 類實例需要指定一個 mode 參數(shù),該參數(shù)指定 RandomAccessFile 的訪問模式:
- r:以只讀方式打開
- rw:打開以便讀取和寫入
- rwd:打開以便讀取和寫入;同步文件內(nèi)容的更新
- rws::打開以便讀取和寫入;同步文件內(nèi)容和元數(shù)據(jù)的更新
如果模式為只讀 r口柳。則不會創(chuàng)建文件,而是會去讀取一個已經(jīng)存在的文件,如果讀取的文件不存在則會出現(xiàn)異常逆害。 如果模式為 rw 讀寫头镊。如果文件不存在則會去創(chuàng)建文件,如果存在則不會創(chuàng)建魄幕。
RandomAccessFile 對象包含一個記錄指針相艇,用以標(biāo)示當(dāng)前讀寫處的位置。 RandomAccessFile 類對象可以自由移動記錄指針:
- 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();
// 寫入文件
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
raf.seek(5);
//先讀出來
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 這個類坛芽,來實現(xiàn)一個多線程斷點下載的功能,用過下載工具的朋友們都知道翼抠,下載前都會建立兩個臨時文件咙轩,一個是與被下載文件大小相同的空文件,另一個是記錄文件指針的位置文件阴颖,每次暫停的時候活喊,都會保存上一次的指針,然后斷點下載的時候量愧,會繼續(xù)從上一次的地方下載钾菊,從而實現(xiàn)斷點下載或上傳的功能帅矗。