保定網(wǎng)站定制公司seo少女
文章目錄
- Java-IO流
- 文件字節(jié)流
- 文件字符流
- File類
- 緩沖流
- 轉(zhuǎn)換流
- 打印流
- 數(shù)據(jù)流
- 對象流
Java-IO流
JDK提供了一套用于IO操作的框架,為了方便我們開發(fā)者使用,就定義了一個像水流一樣,根據(jù)流的傳輸方向和讀取單位,分為字節(jié)流InputStream和OutputStream以及字符流Reader和Writer的IO框架
這里的流指的是數(shù)據(jù)流,通過流,我們就可以一直從流中讀取數(shù)據(jù),直到讀取到盡頭,或是不斷向其中寫入數(shù)據(jù),直到我們寫入完成
文件字節(jié)流
FileInputStream通過它來獲取文件的輸入流:
public static void main(String[] args) {FileInputStream inputStream = null; //定義可以先放在try外部try {inputStream = new FileInputStream("路徑");} catch (FileNotFoundException e) {e.printStackTrace();} finally {try { //建議在finally中進(jìn)行,因為關(guān)閉流是任何情況都必須要執(zhí)行的!if(inputStream != null) inputStream.close();} catch (IOException e) {e.printStackTrace();}}
}
在使用完成一個流之后,必須關(guān)閉這個流來完成對資源的釋放,否則資源會被一直占用
JDK1.7新增了try-with-resource語法,用于簡化資源釋放的寫法:
public static void main(String[] args) {//注意,這種語法只支持實現(xiàn)了AutoCloseable接口的類!try(FileInputStream inputStream = new FileInputStream("路徑")) { //直接在try()中定義要在完成之后釋放的資源} catch (IOException e) { //這里變成IOException是因為調(diào)用close()可能會出現(xiàn),而FileNotFoundException是繼承自IOException的e.printStackTrace();}//無需再編寫finally語句塊,因為在最后自動幫我們調(diào)用了close()
}
使用read
方法讀取文件內(nèi)容:
public static void main(String[] args) {//test.txt:abcdtry(FileInputStream inputStream = new FileInputStream("test.txt")) {int tmp;while ((tmp = inputStream.read()) != -1){ //通過while循環(huán)來一次性讀完內(nèi)容System.out.println((char)tmp);}}catch (IOException e){e.printStackTrace();}
}
使用available
方法能查看當(dāng)前可讀的剩余字節(jié)數(shù)量
一個一個讀取效率太低了,可以預(yù)置一個合適容量的byte[]數(shù)組來存放
public static void main(String[] args) {//test.txt:abcdtry(FileInputStream inputStream = new FileInputStream("test.txt")) {byte[] bytes = new byte[inputStream.available()]; //我們可以提前準(zhǔn)備好合適容量的byte數(shù)組來存放System.out.println(inputStream.read(bytes)); //一次性讀取全部內(nèi)容(返回值是讀取的字節(jié)數(shù))System.out.println(new String(bytes)); //通過String(byte[])構(gòu)造方法得到字符串}catch (IOException e){e.printStackTrace();}
}
控制要讀取數(shù)量:
System.out.println(inputStream.read(bytes, 1, 2)); //第二個參數(shù)是從給定數(shù)組的哪個位置開始放入內(nèi)容,第三個參數(shù)是讀取流中的字節(jié)數(shù)
一次性讀取同單個讀取一樣,當(dāng)沒有任何數(shù)據(jù)可讀時,依然會返回-1
通過skip()
方法可以跳過指定數(shù)量的字節(jié)
FileInputStream是不支持reset()
的,雖然有這個方法
write()
操作向文件里寫入內(nèi)容:
public static void main(String[] args) {try(FileOutputStream outputStream = new FileOutputStream("output.txt", true)) { //true表示開啟追加模式outputStream.write("lb".getBytes()); //現(xiàn)在只會進(jìn)行追加寫入,而不是直接替換原文件內(nèi)容outputStream.flush();}catch (IOException e){e.printStackTrace();}
}
文件拷貝:
public static void main(String[] args) {try(FileOutputStream outputStream = new FileOutputStream("output.txt");FileInputStream inputStream = new FileInputStream("test.txt")) { //可以寫入多個byte[] bytes = new byte[10]; //使用長度為10的byte[]做傳輸媒介int tmp; //存儲本地讀取字節(jié)數(shù)while ((tmp = inputStream.read(bytes)) != -1){ //直到讀取完成為止outputStream.write(bytes, 0, tmp); //寫入對應(yīng)長度的數(shù)據(jù)到輸出流}}catch (IOException e){e.printStackTrace();}
}
文件字符流
字符流不同于字節(jié),字符流是以一個具體的字符進(jìn)行讀取,因此它只適合讀純文本的文件,如果是其他類型的文件不適用:
public static void main(String[] args) {try(FileReader reader = new FileReader("test.txt")){char[] str = new char[10];reader.skip(1); //現(xiàn)在跳過的是一個字符reader.read(str);System.out.println(str); //直接讀取到char[]中}catch (IOException e){e.printStackTrace();}
}
Writer:
public static void main(String[] args) {try(FileWriter writer = new FileWriter("output.txt")){writer.getEncoding(); //支持獲取編碼(不同的文本文件可能會有不同的編碼類型)writer.write('牛');writer.append('牛'); //其實功能和write一樣writer.flush(); //刷新}catch (IOException e){e.printStackTrace();}
}
append支持像StringBuilder那樣的鏈?zhǔn)秸{(diào)用,返回的是Writer對象本身。
File類
File類是專門用于表示一個文件或文件夾,只不過它只是代表這個文件,但并不是這個文件本身。
通過File對象,可以更好地管理和操作硬盤上的文件。
public static void main(String[] args) {File file = new File("test.txt"); //直接創(chuàng)建文件對象,可以是相對路徑,也可以是絕對路徑System.out.println(file.exists()); //此文件是否存在System.out.println(file.length()); //獲取文件的大小System.out.println(file.isDirectory()); //是否為一個文件夾System.out.println(file.canRead()); //是否可讀System.out.println(file.canWrite()); //是否可寫System.out.println(file.canExecute()); //是否可執(zhí)行File file = new File("/");
System.out.println(Arrays.toString(file.list())); //快速獲取文件夾下的文件名稱列表System.out.println(f.getAbsolutePath()); //獲取文件的絕對路徑
}
直接將File作為參數(shù)傳入字節(jié)流或是字符流,讀取某個文件的內(nèi)容:
File file = new File("test.txt");
try (FileInputStream inputStream = new FileInputStream(file)){ //直接做參數(shù)System.out.println(inputStream.available());
}catch (IOException e){e.printStackTrace();
}
緩沖流
普通的文件流讀取文件數(shù)據(jù)非常便捷,但是每次都需要從外部I/O設(shè)備去獲取數(shù)據(jù),由于外部I/O設(shè)備的速度一般都達(dá)不到內(nèi)存的讀取速度,很有可能造成程序反應(yīng)遲鈍,因此性能還不夠高。
緩沖流能夠提供一個緩沖,提前將部分內(nèi)容存入內(nèi)存(緩沖區(qū))在下次讀取時,如果緩沖區(qū)中存在此數(shù)據(jù),則無需再去請求外部設(shè)備。當(dāng)向外部設(shè)備寫入數(shù)據(jù)時,也是由緩沖區(qū)處理,而不是直接向外部設(shè)備寫入。
創(chuàng)建一個緩沖字節(jié)流:
public static void main(String[] args) {try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"))){ //傳入FileInputStreamSystem.out.println((char) bufferedInputStream.read()); //操作和原來的流是一樣的}catch (IOException e){e.printStackTrace();}
}
實際上進(jìn)行I/O操作的并不是BufferedInputStream,而是我們傳入的FileInputStream,而BufferedInputStream雖然有著同樣的方法,但是進(jìn)行了一些額外的處理然后再調(diào)用FileInputStream的同名方法,這樣的寫法稱為裝飾者模式
close
方法源碼:
public void close() throws IOException {byte[] buffer;while ( (buffer = buf) != null) {if (bufUpdater.compareAndSet(this, buffer, null)) { //CAS無鎖算法,并發(fā)會用到,暫時不需要了解InputStream input = in;in = null;if (input != null)input.close();return;}// Else retry in case a new buf was CASed in fill()}
}
BufferedInputStream中還存在一個專門用于緩存的數(shù)組:
/*** The internal buffer array where the data is stored. When necessary,* it may be replaced by another array of* a different size.*/
protected volatile byte buf[];
緩沖流提供了緩沖機(jī)制,一部分內(nèi)容可以被暫時保存,BufferedInputStream支持reset()
和mark()
操作
當(dāng)調(diào)用mark()
之后,輸入流會以某種方式保留之后讀取的readlimit
數(shù)量的內(nèi)容,當(dāng)讀取的內(nèi)容數(shù)量超過readlimit
則之后的內(nèi)容不會被保留,當(dāng)調(diào)用reset()
之后,會使得當(dāng)前的讀取位置回到mark()
調(diào)用時的位置
其實mark()
后保存的讀取內(nèi)容是取readlimit
和BufferedInputStream類的緩沖區(qū)大小兩者中的最大值
public static void main(String[] args) {try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"))){bufferedInputStream.mark(1); //只保留之后的1個字符System.out.println((char) bufferedInputStream.read());System.out.println((char) bufferedInputStream.read());bufferedInputStream.reset(); //回到mark時的位置System.out.println((char) bufferedInputStream.read());System.out.println((char) bufferedInputStream.read());}catch (IOException e) {e.printStackTrace();}
}
BufferedReader構(gòu)造時需要傳入一個Reader對象:
public static void main(String[] args) {try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))){System.out.println((char) reader.read());}catch (IOException e) {e.printStackTrace();}
}
BufferedReader內(nèi)部也包含一個緩存數(shù)組:
private char cb[];
BufferedReader支持按行讀取:
public static void main(String[] args) {try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))){System.out.println(reader.readLine()); //按行讀取}catch (IOException e) {e.printStackTrace();}
}
把每一行內(nèi)容依次轉(zhuǎn)換為集合類提到的Stream流:
public static void main(String[] args) {try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))){reader.lines().limit(2).distinct().sorted().forEach(System.out::println);}catch (IOException e) {e.printStackTrace();}
}
BufferedWriter在處理純文本文件方便:
public static void main(String[] args) {try (BufferedWriter reader = new BufferedWriter(new FileWriter("output.txt"))){reader.newLine(); //使用newLine進(jìn)行換行reader.write("漢堡做滴彳亍不彳亍"); //可以直接寫入一個字符串reader.flush(); //清空緩沖區(qū)}catch (IOException e) {e.printStackTrace();}
}
轉(zhuǎn)換流
讀取的是一個字符串或是一個個字符,但是我只能往一個OutputStream里輸出,但是OutputStream又只支持byte類型,如果要往里面寫入內(nèi)容,進(jìn)行數(shù)據(jù)轉(zhuǎn)換就會很麻煩,那么能否有更加簡便的方式來做這樣的事情呢
public static void main(String[] args) {try(OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("test.txt"))){ //雖然給定的是FileOutputStream,但是現(xiàn)在支持以Writer的方式進(jìn)行寫入writer.write("lbwnb"); //以操作Writer的樣子寫入OutputStream}catch (IOException e){e.printStackTrace();}
}
只拿到了一個InputStream,但是我們希望能夠按字符的方式讀取,我們就可以使用InputStreamReader來幫助我們實現(xiàn):
public static void main(String[] args) {try(InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"))){ //雖然給定的是FileInputStream,但是現(xiàn)在支持以Reader的方式進(jìn)行讀取System.out.println((char) reader.read());}catch (IOException e){e.printStackTrace();}
}
打印流
System.out
就是一個PrintStream,PrintStream也繼承自FilterOutputStream類因此依然是裝飾我們傳入的輸出流,但是它存在自動刷新機(jī)制。
PrintStream也永遠(yuǎn)不會拋出異常,而是使用內(nèi)部檢查機(jī)制checkError()
方法進(jìn)行錯誤檢查。
它能夠格式化任意的類型,將它們以字符串的形式寫入到輸出流。
System.out
也是PrintStream,不過默認(rèn)是向控制臺打印,也可以讓它向文件中打印:
public static void main(String[] args) {try(PrintStream stream = new PrintStream(new FileOutputStream("test.txt"))){stream.println("lbwnb"); //其實System.out就是一個PrintStream}catch (IOException e){e.printStackTrace();}
}
println
方法就是PrintStream中的方法,它會直接打印基本數(shù)據(jù)類型或是調(diào)用對象的toString()
方法得到一個字符串,并將字符串轉(zhuǎn)換為字符,放入緩沖區(qū)再經(jīng)過轉(zhuǎn)換流輸出到給定的輸出流上。
Scanner使用的是系統(tǒng)提供的輸入流,也可以使用Scanner來掃描其他的輸入流:
public static void main(String[] args) throws FileNotFoundException {Scanner scanner = new Scanner(new FileInputStream("秘制小漢堡.txt")); //將文件內(nèi)容作為輸入流進(jìn)行掃描
}
數(shù)據(jù)流
數(shù)據(jù)流DataInputStream也是FilterInputStream的子類,同樣采用裝飾者模式,最大的不同是它支持基本數(shù)據(jù)類型的直接讀取:
public static void main(String[] args) {try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream("test.txt"))){System.out.println(dataInputStream.readBoolean()); //直接將數(shù)據(jù)讀取為任意基本數(shù)據(jù)類型}catch (IOException e) {e.printStackTrace();}
}
用于寫入基本數(shù)據(jù)類型:
public static void main(String[] args) {try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("output.txt"))){dataOutputStream.writeBoolean(false);}catch (IOException e) {e.printStackTrace();}
}
寫入的是二進(jìn)制數(shù)據(jù),并不是寫入的字符串,使用DataInputStream可以讀取,一般他們是配合一起使用的。
對象流
ObjectOutputStream不僅支持基本數(shù)據(jù)類型,通過對對象的序列化操作,以某種格式保存對象,來支持對象類型的IO,它不是繼承自FilterInputStream的。
public static void main(String[] args) {try (ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("output.txt"));ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("output.txt"))){People people = new People("lbw");outputStream.writeObject(people);outputStream.flush();people = (People) inputStream.readObject();System.out.println(people.name);}catch (IOException | ClassNotFoundException e) {e.printStackTrace();}
}static class People implements Serializable{ //必須實現(xiàn)Serializable接口才能被序列化String name;public People(String name){this.name = name;}
}
后續(xù)的操作中,有可能會使得這個類的一些結(jié)構(gòu)發(fā)生變化,而原來保存的數(shù)據(jù)只適用于之前版本的這個類,因此我們需要一種方法來區(qū)分類的不同版本:
static class People implements Serializable{private static final long serialVersionUID = 123456; //在序列化時,會被自動添加這個屬性,它代表當(dāng)前類的版本,我們也可以手動指定版本。String name;public People(String name){this.name = name;}
}
當(dāng)發(fā)生版本不匹配時,會無法反序列化為對象
如果我們不希望某些屬性參與到序列化中進(jìn)行保存,我們可以添加transient
關(guān)鍵字
static class People implements Serializable{private static final long serialVersionUID = 1234567;transient String name;public People(String name){this.name = name;}
}
ialVersionUID = 123456; //在序列化時,會被自動添加這個屬性,它代表當(dāng)前類的版本,我們也可以手動指定版本。
String name;public People(String name){this.name = name;
}
}
當(dāng)發(fā)生版本不匹配時,會無法反序列化為對象如果我們不希望某些屬性參與到序列化中進(jìn)行保存,我們可以添加`transient`關(guān)鍵字~~~java
static class People implements Serializable{private static final long serialVersionUID = 1234567;transient String name;public People(String name){this.name = name;}
}
transient關(guān)鍵字,使得某些屬性不參與序列化,取消這些不必要保存的屬性,可以節(jié)省數(shù)據(jù)空間占用以及減少序列化時間。