java i o是什么流_【Java】I/O流的操作、认识使用
重要聲明:本文章僅僅代表了作者個(gè)人對(duì)此觀點(diǎn)的理解和表述。讀者請(qǐng)查閱時(shí)持自己的意見(jiàn)進(jìn)行討論。
1、序
在 Java 編程中,I/O流的操作是必須學(xué)會(huì)的一項(xiàng)技能。無(wú)論是文件的復(fù)制粘貼,還是上傳下載,無(wú)論是文本的讀取寫(xiě)入,還是音視頻的播放,都離不開(kāi)I/O流的加持。I/O流如此重要,怎么能不了解它!本文將帶你了解在Java里,如何自如的操控各種流。
2、理解
網(wǎng)上教程千千萬(wàn),學(xué)校教材萬(wàn)千千,無(wú)數(shù)遍的使用水流來(lái)比喻Java里的I/O流。這無(wú)疑是一個(gè)非常恰當(dāng)合適的比喻,但依舊不能解開(kāi)每個(gè)人對(duì)流的疑問(wèn),當(dāng)然也包括本文,不一定能被每個(gè)人理解,但希望哪怕一個(gè)人能夠理解。
我也將使用水流來(lái)作比喻。現(xiàn)在,要將一個(gè)湖里的所有水全部轉(zhuǎn)移到另一個(gè)水庫(kù)里面,怎么辦?當(dāng)然是用抽水機(jī)啊,要么就用桶不同的挑嘛。為了能夠更好地結(jié)合程序來(lái)理解,咱們假設(shè)選擇了使用桶挑水過(guò)去的方案。
隨即便產(chǎn)生了下面圖案:
有了這張圖,對(duì)Java里I/O的理解就會(huì)更加簡(jiǎn)單一點(diǎn)。見(jiàn)名知意,為啥叫I/O流,是因?yàn)樵趈ava中,流的操作是通過(guò) 兩個(gè)類(lèi)的協(xié)同操作進(jìn)行完成的。
這兩個(gè)分別是:
I: InputStream 輸入流
O: OutputStream 輸出流
下面分別進(jìn)行簡(jiǎn)述:
(1)、InputStream 輸入流
輸入流和輸出流都是相對(duì)于程序來(lái)進(jìn)行稱呼的。脫離了程序,不能確定某個(gè)流就是輸入或輸出流,下圖中“桶”就相當(dāng)于是程序,那么輸入流是什么,一目了然:
可見(jiàn),從湖泊A 到桶里的這部分水流,就可以說(shuō)是輸入流,但你不能單純的認(rèn)為就是這一通水就是輸入流,而應(yīng)該理解為:整個(gè)湖里的水,在放入桶的這個(gè)行為過(guò)程中,這時(shí)這些水對(duì)于這個(gè)桶來(lái)說(shuō)是輸入流。
所以同樣的,在程序里,數(shù)據(jù)流在往程序里跑時(shí)這個(gè)數(shù)據(jù)流叫做輸入流,承載這個(gè)數(shù)據(jù)跑到程序里面的類(lèi),是InputStream。
(2)、OutputStream 輸出流
與輸入流相似,想必你看了上面的輸入流,你對(duì)輸出流可能也有那個(gè)概念了,只不過(guò)是反其道而行之的,如圖所示:
從桶里,往湖泊B倒水,這個(gè)行為過(guò)程中,所有的湖泊里的水,這可理解為輸出流。在程序里,數(shù)據(jù)流在往程序外跑時(shí),這個(gè)流叫做輸出流,承載這個(gè)數(shù)據(jù)跑到外面去的類(lèi),是OutputStream。
上訴的輸入流,輸出流都是對(duì)于程序來(lái)說(shuō)的,把桶比作是這個(gè)運(yùn)輸水的程序,所以在闡述是都是以“桶”為對(duì)象來(lái)闡述的。
3、代碼加入
(1)、InputStream 輸入流
首先講述InputStream輸入流的代碼應(yīng)用。InputStream是一個(gè)abstract的抽象類(lèi),抽象類(lèi)是不能直接實(shí)例化對(duì)象的,所以,Java為我們貼心的實(shí)現(xiàn)了許許多多的、用于處理各種場(chǎng)景的輸入流類(lèi),比較常用的FileInputStream可見(jiàn)名知意,它是專(zhuān)門(mén)為讀取文件而生的文件輸入流承載對(duì)象。下面是文件輸入流的使用案例:
// 為了保證程序的正常,請(qǐng)?jiān)贒盤(pán)下建立文件:file.txt,文件內(nèi)容:File Inputstream test.
// FileInputStream 在 java.io 包下,如果使用文本編輯器編寫(xiě)代碼,記得導(dǎo)包:import java.io.FileInputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 使用指定文件構(gòu)建文件來(lái)構(gòu)建文件輸入流。
FileInputStream fileInputStream = new FileInputStream("D:/file.txt");
// 就如同上圖要把一個(gè)湖泊里面的水全部傳到另一個(gè)湖泊,中間借助了一個(gè)“桶”來(lái)幫助。
// 并且,上面將“桶”比喻為整個(gè)程序。
// 現(xiàn)在要擯棄:將“桶”比喻為整個(gè)程序。這個(gè)概念了。
// 實(shí)際上“桶”只能充當(dāng)程序里的一個(gè)變量。
// 這個(gè)變量每次能容納一定容量的數(shù)據(jù)。
// 這里定義 datas 數(shù)組。是一個(gè)每次只能容納4字節(jié)的“小桶”。
byte[] datas = new byte[4];
// 但是我們不能確定是不是每次重復(fù)用這只“桶”去舀水的時(shí)候都能把桶舀滿。
// 就比如上邊圖中,當(dāng)桶兒把湖泊A里的水快舀完時(shí),有一個(gè)人提著桶去舀最后一桶水,發(fā)小并不能舀滿
// 一整桶水,他索性舀了多少就帶走多少了。
// 而在程序里,程序是是的,它必須要你告訴他每次舀了多少水,
// 特別是在最后一次沒(méi)能舀滿一“桶”水時(shí),這個(gè)標(biāo)記了舀了多少水的字段就必不可少了。
// 定義一個(gè)字段表示每次舀了多少水。
int size = 0;
// 舀水一次可能完不成,畢竟我們桶只有這么大,所以要來(lái)來(lái)回回不同的舀,即循環(huán)進(jìn)行。
// 有人可能說(shuō),你定義一個(gè)超大的桶,不就可以一次搞定了。
// 答:可以是可以,但是要明白,這個(gè)過(guò)程中是要把數(shù)據(jù)保存在你電腦的內(nèi)存條上的,假如你的文件有100GB,你又定義了一個(gè)100GB的桶
// 而你的電腦內(nèi)存一共才16GB,程序一運(yùn)行就會(huì)內(nèi)存溢出的,直接報(bào)錯(cuò)了。
// 所以桶的大小,要根據(jù)電腦內(nèi)存有多大,讀取行為發(fā)生的頻率大不大來(lái)自己權(quán)衡的一個(gè)數(shù)值。
// 下面開(kāi)始循環(huán)讀取
while (size != -1) {
// 當(dāng)文件讀取完成后, read 方法會(huì)返回 -1來(lái)表示已經(jīng)讀取完成了,所以,只要不是 -1 就一定表示數(shù)據(jù)還能繼續(xù)讀取。
// 輸入流的讀取的方法會(huì)返回當(dāng)前實(shí)際讀取了多少數(shù)據(jù)的大小,正合適告訴我們舀了多少水。
size = fileInputStream.read(datas);
// 此時(shí) datas 里面的數(shù)據(jù)就已經(jīng)是舀到的數(shù)據(jù)了。
// 拿到數(shù)據(jù)構(gòu)建字符串:
String str = new String(datas);
// 輸出結(jié)果:
System.out.print(str);
}
// 當(dāng)循環(huán)完成,也就表示讀取完成,水舀完了。
// 不要忘了最后調(diào)用輸入流的關(guān)閉方法,你要是不調(diào)用關(guān)閉方法,就如同占著茅坑不拉屎是一樣的道理。
fileInputStream.close();
}
}
運(yùn)行這個(gè)程序,最后輸出:File Inputstream test.
加上代碼中的注釋,相信理解起來(lái)非常簡(jiǎn)單。這是一個(gè)非常典型的代碼編寫(xiě)方式,所有的輸入流幾乎都是這樣的代碼流程來(lái)進(jìn)行數(shù)據(jù)的讀取。
(2)、OutputStream 輸出流
輸入流是講數(shù)據(jù)從程序的外部讀入到程序的內(nèi)部,那么輸出流就是將數(shù)據(jù)從程序的內(nèi)部輸出到程序的外部,上面使用了一個(gè)簡(jiǎn)單的程序?qū)⑼獠课募癴ile.txt”的內(nèi)容讀取到了程序內(nèi)部并顯示,現(xiàn)在,我們要在程序內(nèi)部設(shè)定好一個(gè)字符串,然后輸出到文件中,同輸入流一樣,這里也采用文件相關(guān)的“FileOutputStream”文件輸出流,來(lái)完成數(shù)據(jù)的輸出。示列代碼如下:
public class Test {
public static void main(String[] args) throws Exception {
// 準(zhǔn)備好要輸出的數(shù)據(jù)內(nèi)容。
String str = "File OutputStream test.";
// 構(gòu)建文件輸出流:
FileOutputStream fileOutputStream = new FileOutputStream("D:/outFile.txt");
// 無(wú)論是流的輸出,還是流的讀取,它們操作的數(shù)據(jù)都是 byte 的二進(jìn)制數(shù)據(jù)。
// 而定義的準(zhǔn)備用于輸出數(shù)據(jù)是字符串,所以,需要先從字符串轉(zhuǎn)換成 byte 二進(jìn)制數(shù)據(jù)。
byte[] datas = str.getBytes();
// 將數(shù)據(jù)寫(xiě)到輸出流。
fileOutputStream.write(datas);
// 這一步很重要。
// 上面一行代碼是將數(shù)據(jù)寫(xiě)入到了一個(gè)磁盤(pán)緩存區(qū),內(nèi)容其實(shí)(可能)還沒(méi)有真的被寫(xiě)入到文件里。
// 這一行代碼的調(diào)用,可以強(qiáng)制的將數(shù)據(jù)從緩存區(qū)強(qiáng)制的寫(xiě)入的文件中,所以為了保證效果,務(wù)必每次在 “關(guān)閉” 輸出流前
// 記住要調(diào)用一次該方法。
fileOutputStream.flush();
// 最后不要忘了關(guān)閉輸出流。
fileOutputStream.close();
}
}
運(yùn)行程序,會(huì)在磁盤(pán)D盤(pán)下多出一個(gè) outFile.txt 文件,打開(kāi)這個(gè)文件,就可以看到文件內(nèi)容:File OutputStream test.
你發(fā)現(xiàn)這個(gè)輸出流并沒(méi)有像輸入流那樣去循環(huán)的進(jìn)行輸出。是的,因?yàn)檫@里是為了讓咱們快速理解輸出流的使用方式,只是單獨(dú)的進(jìn)行單向輸出,而且輸出的只有幾個(gè)字,我們能確定它的大小不是很大。為什么上面輸入流讀取的文件也很小,而我們也是用循環(huán)讀取?因?yàn)槲覀冎皇窃跍y(cè)試學(xué)習(xí),知道這個(gè)文件不大,實(shí)際開(kāi)發(fā)中,你永遠(yuǎn)不知道你要讀取的文件有多大,對(duì)于我們不知道的事物,我們應(yīng)該采取兼容性最大的程序代碼方式去編寫(xiě)。所以輸入時(shí)往往使用循環(huán)來(lái)讀取。
現(xiàn)在了解了輸入流,輸出流,最有魅力的地方不是單獨(dú)使用它們,而是將它們結(jié)合起來(lái)使用,達(dá)到更加強(qiáng)悍的功能。耳熟能詳?shù)牡膹?fù)制功能,就可以通過(guò)輸入流,輸出流的相互配合完成。
4、結(jié)合使用
將輸入流,輸出流結(jié)合起來(lái),學(xué)會(huì)I/O流的一個(gè)關(guān)鍵環(huán)節(jié),任何輸出流,輸入流都可以相互結(jié)合,而不一定只能是文件的輸入流對(duì)上文件的輸出流。假如現(xiàn)在我們有一個(gè) 1GB 大小的 視頻文件《video.mp4》在D盤(pán)根目錄,要把它復(fù)制到 E盤(pán)下。示列代碼如下:
public class Test {
public static void main(String[] args) throws Exception {
// 視頻文件路徑。
String videoFile = "D:/video.mp4";
// 建立輸入流
FileInputStream fileInputStream = new FileInputStream(videoFile);
// 準(zhǔn)備一個(gè)小“桶”每次舀這么多數(shù)據(jù)。
byte[] datas = new byte[1024];
// 每次舀水的量
int size = 0;
// ---------------------------------------------------------------
// 準(zhǔn)備輸出文件路徑
String targetFile = "E:/video.pm4";
// 建立輸出流
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
// 循環(huán)的使用“小桶”舀。
while (size != -1) {
// 舀
size = fileInputStream.read(datas);
// 舀到數(shù)據(jù),往目標(biāo)倒。
// 因?yàn)槲覀兪褂?size 標(biāo)記每次舀了多少水,所以這里寫(xiě)的時(shí)候,也要講“桶里”有多少水這個(gè)信息
// 告訴寫(xiě)出的位置,這樣就能拿在桶里拿到正確的數(shù)據(jù)量。
fileOutputStream.write(datas, 0, size);
}
// 完成操作,關(guān)閉輸入流,關(guān)閉輸出流
fileInputStream.close();
fileOutputStream.close();
// 提示完成
System.out.println("復(fù)制完成!");
}
}
執(zhí)行程序,將會(huì)把D盤(pán)下的文件復(fù)制一份在E盤(pán)下,這樣就完成了文件的復(fù)制,同時(shí)對(duì)文件輸入流,輸出流有了更深刻的印象。在Java里,流的操作中心都是圍繞
read讀的方法,和write寫(xiě)的方法,無(wú)論怎么樣,只要搞清楚各個(gè)類(lèi)是用來(lái)干嘛的以及明白數(shù)據(jù)流向的方向,就能輕松的理解流的操作了。
總結(jié)
以上是生活随笔為你收集整理的java i o是什么流_【Java】I/O流的操作、认识使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一份工作,坚持多久跳槽最合适?
- 下一篇: error: implicit inst