IO問題是任何編程語言都無法回避的幌墓,可以說但壮,IO問題是整個人機交互的核心問題。在Java中常侣,提供了包名為java.io
的專門操作類庫蜡饵。
在這個包下提供了將近有80個類,這些類大致分為四組:
- 基于字節(jié)操作的
InputStream
和OutputStream
袭祟。 - 基于字符操作的IO接口:
Writer
和Reader
。 - 基于磁盤操作的IO接口:
File
- 基于網(wǎng)絡操作的IO接口:Socket
要學好Java的文件IO操作捞附,重點需要掌握:
- 2個代碼模型
- 5個類
- File
- OutputStream
- InputStream
- Reader
- Writer
- 1個接口(Serializable)
1. File類的基本使用
這個類的使用直接產(chǎn)生實例化對象即可巾乳。
1.1 File類實例化對象
如果要實例化對象,需要用到兩個構(gòu)造方法鸟召。
- public File(String pathName)
- public File(File parent,String child)胆绊,設置父路徑和子文件
1.2 File類操作文件
如果要進行文件的操作,可以使用下列方法:
- createNewfile: 根據(jù)制訂的路徑創(chuàng)建新文件
- exists(); 判斷文件是否存在
- file.delete(); 刪除文件
實例: 判斷文件是否存在欧募,存在的話就刪除压状,不存在的話就創(chuàng)建
public class FileDemo {
public static void main(String[] args) throws IOException {
File file = new File("D:\\hello.txt"); // 定義要操作的文件路徑
if (file.exists()) { // 判斷文件是否存在
System.out.println("文件已經(jīng)存在,已經(jīng)將其刪除");
file.delete(); //刪除文件
} else {
System.out.println("不存在文件,已經(jīng)創(chuàng)建了一個新的种冬!");
file.createNewFile(); //創(chuàng)建一個空文件
}
}
}
說明镣丑,如果此處操作的都是根路徑,如果是含有子路徑的話娱两,如果路徑不存在莺匠,是不會自動創(chuàng)建路徑的,會報錯
注意:路徑分隔符
由于不同的操作系統(tǒng)對路徑分隔符是不同的十兢,因此趣竣,使用路徑的時候,需要將上述指定操作文件的位置修改為如下:
File file = new File("D:"+File.separator+"hello.txt"); // 定義要操作的文件路徑
1.3 File類操作目錄
有以下常用的方法操作目錄
- 獲得父目錄的方法1: getParentFile()旱物,該方法返回的是File類型的值(推薦使用)
- 獲取父目錄的方法2: getParent()遥缕,該方法返回的是String類型的值
- 創(chuàng)建父目錄,就對獲取的父目錄的返回對象執(zhí)行: file.getParentFile().mkdirs()宵呛,該方法如果有多層路徑单匣,都可以自動創(chuàng)建。
public class FileDemo {
public static void main(String[] args) throws IOException {
/**
* 對路徑的操作
* */
File file = new File("D:"+File.separator+"hello"+File.separator+"hello.txt"); // 定義要操作的文件路徑
if(!file.getParentFile().exists()){ // 判斷父目錄是否存在
System.out.println(file.getParentFile().toString());
file.getParentFile().mkdirs(); // 默認創(chuàng)建多級父目錄
}
file.createNewFile();
}
}
1.4 File類取得文件信息
File類提供很多取得文件信息的方法烤蜕。方法有很多封孙,使用方式也都類似,直接通過一個例子記錄一下吧讽营。
范例
class MyMath {
// 保留幾位小數(shù)
public static double round(double num, int scale) {
return Math.round(num * Math.pow(10, scale)) / Math.pow(10, scale);
}
}
public class FileInfoDemo {
public static void main(String[] args) {
File file = new File("D:\\4. 暫存待整理\\截圖\\Screenshot_2016-07-17-11-21-27.png"); // 定義要操作的文件路徑
if (file.exists() && file.isFile()) { // 文件存在且是文件
System.out.println("文件大谢⒓伞:" + MyMath.round((file.length()) / 1024, 2)); // 文件大小為字節(jié)數(shù),太大了橱鹏,僅保留2位小數(shù)
System.out.println("上次修改時間:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date(file.lastModified())));
}
}
}
2. 字節(jié)流與字符流
File類本質(zhì)上不操作文件的內(nèi)容膜蠢,要進行文件內(nèi)容的處理,就需要用到字節(jié)流和字符流的概念莉兰。
字節(jié)流和字符流的區(qū)別是什么挑围?
- 字節(jié)流是指文件作為數(shù)據(jù)的二進制傳輸形式,而字符流則是在內(nèi)存中進行操作的對象的感覺糖荒。這么理解不太嚴密杉辙,但是可以簡單認為,字符流更靠近文件捶朵。
- 字節(jié)流更多的是文件在磁盤上的保存蜘矢,網(wǎng)絡的數(shù)據(jù)傳輸形式等。
- 字符更適合處理中文
不管是字節(jié)流還是字符流综看,其處理文件內(nèi)容的基本步驟都是如下的思路:
- 要根據(jù)文件的路徑創(chuàng)建File類的對象
- 根據(jù)字節(jié)流或者字符流的子類實例化父類對象
- 進行數(shù)據(jù)的讀取或者寫入操作
- 關(guān)閉流:IO操作屬于資源操作品腹,所有的資源操作都必須執(zhí)行關(guān)閉操作。
2.1 字節(jié)輸出流OutputStream
如果要通過程序進行內(nèi)容的輸出红碑,則可以使用字節(jié)輸出流OutputStream舞吭。
OutputStream是一個抽象類,實現(xiàn)了Closeable接口和Flushable接口,通過這兩個接口羡鸥,繼承了他們各自的關(guān)閉方法和刷新方法蔑穴。同樣的,由于他是一個抽象類兄春,必須使用它的子類進行實例化澎剥,那么就會需要針對不同的操作,實現(xiàn)不同的子類實例化赶舆。對于文件的操作哑姚,使用FileOutputStream進行實例化。 FileOutputStream擁有不同的構(gòu)造函數(shù)芜茵,根據(jù)構(gòu)造函數(shù)的不同叙量,可以進行文件的覆蓋寫入和文件的追加寫入設置等。
FileOutputStream的不同構(gòu)造方法
- 覆蓋寫入: FileOutputStream(File file)
- 追加寫入:+ 覆蓋寫入: FileOutputStream(File file,boolean append)
FileOutputStream的寫入方法
- 全部寫入: write(byte[] content)
- 局部寫入: write(byte[] content,int start, int len)
- 寫入單個字節(jié): write(int content)
范例:使用FileOutputStream進行文件內(nèi)容的寫入/追加等九串。
/** 文件內(nèi)容的寫入操作范例 */
public class OutputStreamDemo {
public static void main(String[] args) throws IOException {
// 1. 確定要操作的文件
File file = new File("D:\\filewrite.txt");
// 2. 實例化一個文件類型的OutputStream
OutputStream os = new FileOutputStream(file); // 覆寫
// OutputStream os = new FileOutputStream(file, true); // 追加
// 3. 寫入文件
// 3.1 全部寫入
String content = "this is a test String!\r\n";
os.write(content.getBytes());
// 3.2 部分寫入
os.write(content.getBytes(), 0, 9);
// 3.3 寫入單個字節(jié)
os.write(65);
// 4. 關(guān)閉流
os.close();
}
}
說明:
- 使用FileOutputStream進行文件操作的時候绞佩,就不在需要進行文件的創(chuàng)建,調(diào)用寫入的方法時猪钮,會進行自動的創(chuàng)建品山。(只創(chuàng)建文件,如果有多級烤低,那么目錄一定要確保是存在的)
- 一定要記得關(guān)閉文件字節(jié)流肘交。
2.2 字節(jié)輸入流InputStream
InputStream是字節(jié)輸入流,其使用方法和OutputStream類幾乎一樣扑馁。
原理是通過read方法將流對象中的數(shù)據(jù)讀入的byte數(shù)組中涯呻,之后通過操作數(shù)組實現(xiàn)讀取到的內(nèi)容的操作。
import java.io.*;
public class InputStreamDemo {
public static void main(String[] args) throws IOException {
// 1. 定位到要處理的文件
File file = new File("D:\\ProgramFiles\\kibana-5.6.2-windows-x86\\LICENSE.txt");
// 2. 使用FileInputStream類實例化InputStream對象
InputStream input = new FileInputStream(file);
// 3. 讀取流的內(nèi)容到字節(jié)數(shù)組中
byte[] content = new byte[1024];
int len = input.read(content);
System.out.println("讀取文件內(nèi)容為:【" + new String(content, 0, len) + "】");
// 4. 關(guān)閉流
input.close();
}
}
2.3 字符輸出流Writer
Writer是一種字符輸出流的處理類腻要,使用方法也和OutputStream使用類似复罐,還是使用FileWriter進行父類對象的實例化。
比較有特色的就是雄家,可以直接向文件輸出字符數(shù)組或者字符串效诅。
范例:使用Writer類進行字符輸出。
package org.liyubo.java8demos.fileops;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterDemo {
public static void main(String[] args) throws IOException {
// 1. 定位到要操作的文件
File file = new File("D:\\test\\fileops\\testwriter.txt");
if(!file.getParentFile().exists()){ // 如果父目錄不存在的話趟济,創(chuàng)建父目錄
file.getParentFile().mkdirs();
}
// 2. 創(chuàng)建Writer對象乱投,并使用FileWriter進行實例化
Writer output = new FileWriter(file);
// 3. 執(zhí)行輸出,調(diào)用輸出字符串或者
output.write("你好咙好,中國篡腌!");
output.append('H');
char[] content = new char[]{'s','v'};
output.write(content);
// 4. 關(guān)閉
output.close();
}
}
2.4 字符輸入流Reader
Reader類是一個字符輸入類褐荷,使用方法和Writer類似勾效,不同的是,其只有將字符輸入流讀入字符數(shù)組的功能,沒有讀入字符串的功能层宫。
不寫例子了杨伙,沒啥意思。
字節(jié)輸入/出流和字符輸入/出流區(qū)別
其實萌腿,字符流的核心還是因為對中文操作更好限匣,但是現(xiàn)在使用一些方法也能將中文使用字節(jié)流很好的處理了。 在正常的開發(fā)中毁菱,還是字節(jié)流使用更多一些米死。
那么,所謂的一些操作又是怎么實現(xiàn)的呢贮庞?
原因就在于峦筒,其實,從流到文件中間窗慎,還有一層就是內(nèi)存的緩沖物喷。在流關(guān)閉之前,內(nèi)容都是緩沖在內(nèi)容中的遮斥,只有通過flush()操作強制刷新峦失,內(nèi)容才會寫入到文件。因此术吗,所謂的一些操作尉辑,就是如何在內(nèi)存中對流的緩沖進行操作的問題。在內(nèi)存的緩沖區(qū)中藐翎,就可以進行字節(jié)流和字符流之間的相互轉(zhuǎn)換材蹬。
3. 轉(zhuǎn)換流
如何在緩沖區(qū)中進行字符流和字節(jié)流的互相轉(zhuǎn)換呢,此處就主要涉及兩個類:
- OutputStreamWriter: 將字節(jié)輸出流轉(zhuǎn)換成字符輸出流吝镣。
- InputStreamReader:將字節(jié)輸入流轉(zhuǎn)換成字符輸入流堤器。
范例: 使用轉(zhuǎn)換流的操作
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
// 1. 定位到要操作的文件
File file = new File("D:\\test\\fileops\\testwriter.txt");
if(!file.getParentFile().exists()){ // 如果父目錄不存在的話,創(chuàng)建父目錄
file.getParentFile().mkdirs();
}
OutputStream os = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(os);
// 3. 執(zhí)行輸出末贾,調(diào)用輸出字符串或者
writer.write("你好闸溃,中國!");
writer.append('H');
char[] content = new char[]{'s','v'};
writer.write(content);
// 4. 關(guān)閉
writer.close();
os.close();
}
}