java i o不会的地方_Java I/O 好复杂,傻傻分不清楚,别担心,我们有线索了。。。...
IO 類(lèi)圖
小帥最近在學(xué)Java的IO類(lèi)庫(kù),這么多類(lèi)看得小帥人頭昏眼花,常常是學(xué)了這個(gè)類(lèi),忘了那個(gè)類(lèi),再過(guò)一陣子就全忘了。。。
每次用到的時(shí)候,小帥都要重新讀文檔,看代碼,如此循環(huán),身心疲憊。
小帥沒(méi)辦法只好向好朋友小會(huì)求助:IO類(lèi)庫(kù)太復(fù)雜了,我毫無(wú)頭緒,能不能幫我梳理一下?
小會(huì)想了一下,說(shuō)道:總體來(lái)說(shuō)IO類(lèi)庫(kù)分為兩大類(lèi):字節(jié)流和字符流,字節(jié)流是按字節(jié)讀取數(shù)據(jù),字符流是按字符讀取數(shù)據(jù)。
小帥不解:所有的數(shù)據(jù)在計(jì)算機(jī)中都是二進(jìn)制表示的,都用字節(jié)來(lái)讀取不就行了嗎?
為什么還要加個(gè)字符流,我用字節(jié)讀出來(lái),再轉(zhuǎn)成字符不行嗎?
小會(huì)說(shuō):Java中的字符都是用Unicode表示的,即對(duì)應(yīng)一個(gè)數(shù)字,也就是碼點(diǎn)。
如果我們用二進(jìn)制的字節(jié)流讀出來(lái)是無(wú)法看懂的,我們需要用對(duì)應(yīng)的編碼格式(比如:UTF-8,UFT-16等)轉(zhuǎn)換成我們可讀的字符。
字符流就是專(zhuān)門(mén)用來(lái)讀寫(xiě)人們可讀的字符的,這樣會(huì)方便很多,一步到位,不用再手工轉(zhuǎn)換成字符了。
我把IO類(lèi)都放在一張圖里,這樣看上去就清爽了:
小帥:還是好多類(lèi)啊。。。小會(huì):別急,我們往下看。
看個(gè)例子
我們看一下用字節(jié)流,把int數(shù)據(jù)1到9,寫(xiě)進(jìn)txt文件的例子:
FileOutputStream outputStream = new FileOutputStream("text.txt");DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
for(int i = 0; i < 10; i++) {
dataOutputStream.writeInt(i);
}
dataOutputStream.close();
text.txt文件內(nèi)容:
里面保存的都是二進(jìn)制數(shù)據(jù),但是我們是用int的數(shù)據(jù)類(lèi)型寫(xiě)進(jìn)去的(dataOutputStream.writeInt(i);),而不是以二進(jìn)制的格式寫(xiě)進(jìn)去的。
小帥疑惑:為什么要用 DataOutputStream寫(xiě)入int數(shù)據(jù)呢?
我直接用 FileOutputStream 不能寫(xiě)嗎?
DataOutputStream有什么作用呢?
小會(huì)微微一笑:如果不用DataOutputStream也可以,不過(guò)要自己拼成int數(shù)據(jù)類(lèi)型的格式,一個(gè)int類(lèi)型占四個(gè)字節(jié)。
比如1用二進(jìn)制表示就是 0000 0000 0000 0000 0000 0000 0000 0001,用十六進(jìn)制表示就是 00 00 00 01。
我們?cè)囈幌掠?FileOutputStream 寫(xiě)入int數(shù)字 0,1,2:
FileOutputStream outputStream = new FileOutputStream("text.txt");
// int 0
outputStream.write(0);
outputStream.write(0);
outputStream.write(0);
outputStream.write(0);
// int 1
outputStream.write(0);
outputStream.write(0);
outputStream.write(0);
outputStream.write(1);
// int 2
outputStream.write(0);
outputStream.write(0);
outputStream.write(0);
outputStream.write(2);
outputStream.close();
寫(xiě)入結(jié)果:
如果用DataOutputStream 寫(xiě)就是:dataOutputStream.writeInt(0),dataOutputStream.writeInt(1),dataOutputStream.writeInt(2),這樣是不簡(jiǎn)單很多呢?
小帥似乎有點(diǎn)懂了:我知道了,DataOutputStream 是對(duì) FileOutputStream 類(lèi)功能的增強(qiáng),讓FileOutputStream 類(lèi)更加強(qiáng)大,起到了裝飾的作用。
小會(huì)開(kāi)心道:你說(shuō)到重點(diǎn)了,IO類(lèi)看似凌亂,其實(shí)有一個(gè)精巧的設(shè)計(jì)模式,貫穿其中,把這么多類(lèi)有序的組織起來(lái)了。
這個(gè)設(shè)計(jì)模式是理解IO類(lèi)的鑰匙,你知道是哪一個(gè)設(shè)計(jì)模式嗎?
裝飾者模式?小帥疑惑道。
裝飾者模式
是的,就是裝飾者模式,我以前寫(xiě)過(guò)一篇介紹裝飾者模式的文章,可以點(diǎn)開(kāi)看看:
裝飾模式--小美的生日蛋糕
。
裝飾者模式的類(lèi)圖:
OutputStream家族類(lèi):
這里的FilterOutputStream類(lèi)就是裝飾模式中的抽象裝飾類(lèi)。
它的子類(lèi)BufferedOutputStream,DataOutputStream,PrintStream就是具體的裝飾類(lèi),起到了功能增強(qiáng)的作用。
它們本身并沒(méi)有實(shí)現(xiàn)寫(xiě)數(shù)據(jù)的功能。
看下FilterOutputStream的代碼:
寫(xiě)數(shù)據(jù)的功能是靠被修飾的類(lèi)實(shí)現(xiàn)的,這里的OutputStream out 是要從外面?zhèn)鬟M(jìn)來(lái)的:
DataOutputStream的writeInt方法實(shí)現(xiàn)了功能的增強(qiáng),可以直接寫(xiě)int類(lèi)型的數(shù)據(jù):
BufferedOutputStream類(lèi)實(shí)現(xiàn)了緩存的功能增強(qiáng):
也就是說(shuō)裝飾類(lèi)是給主類(lèi)錦上添花,主類(lèi)是錦,裝飾類(lèi)是花,花不能代替錦,主要的功能還得靠“錦”實(shí)現(xiàn)的。
清晰起來(lái)了
同理我們來(lái)看看其他流:
InputStream家族類(lèi):
Writer家族類(lèi):
小帥一眼看出了問(wèn)題:奇怪,FilterWriter裝飾類(lèi)怎么沒(méi)有子類(lèi)呢?是不是Writer家族沒(méi)有用裝飾模式呢?
小會(huì)微微一笑:不是的,其實(shí)還是用了裝飾模式,只是實(shí)現(xiàn)的方式有點(diǎn)不一樣,例如OutputStreamWriter類(lèi):
其實(shí)是對(duì)OutputStream類(lèi)的裝飾,換句話(huà)說(shuō)字符流的底層其實(shí)是調(diào)用了字節(jié)流。
這也很容易理解,因?yàn)橛?jì)算機(jī)只能處理二進(jìn)制數(shù)據(jù),本質(zhì)上還是通過(guò)字節(jié)流實(shí)現(xiàn)的。
Reader家族類(lèi):
FilterReader充當(dāng)了抽象裝飾類(lèi),PushbackReader是具體的裝飾類(lèi)。
同樣的InputStreamReader類(lèi)其實(shí)也實(shí)現(xiàn)了裝飾模式:
再看個(gè)例子
public static void main(String[] args) throws IOException {
try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("把酒問(wèn)月·故人賈淳令予問(wèn)之.txt")))) {
writer.write("青天有月來(lái)幾時(shí)?我今停杯一問(wèn)之。");
writer.newLine();
writer.write("人攀明月不可得,月行卻與人相隨。");
writer.newLine();
writer.write("皎如飛鏡臨丹闕,綠煙滅盡清輝發(fā)。");
writer.newLine();
writer.write("但見(jiàn)宵從海上來(lái),寧知曉向云間沒(méi)。");
writer.newLine();
writer.write("白兔搗藥秋復(fù)春,嫦娥孤棲與誰(shuí)鄰。");
writer.newLine();
writer.write("今人不見(jiàn)古時(shí)月,今月曾經(jīng)照古人。");
writer.newLine();
writer.write("古人今人若流水,共看明月皆如此。");
writer.newLine();
writer.write("唯愿當(dāng)歌對(duì)酒時(shí),月光長(zhǎng)照金樽里。");
}
try(BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("把酒問(wèn)月·故人賈淳令予問(wèn)之.txt")))) {
String line;
while((line = reader.readLine()) != null) {
System.out.println(line);
}
}}
輸出:
青天有月來(lái)幾時(shí)?我今停杯一問(wèn)之。
人攀明月不可得,月行卻與人相隨。
皎如飛鏡臨丹闕,綠煙滅盡清輝發(fā)。
但見(jiàn)宵從海上來(lái),寧知曉向云間沒(méi)。
白兔搗藥秋復(fù)春,嫦娥孤棲與誰(shuí)鄰。
今人不見(jiàn)古時(shí)月,今月曾經(jīng)照古人。
古人今人若流水,共看明月皆如此。
唯愿當(dāng)歌對(duì)酒時(shí),月光長(zhǎng)照金樽里。
OutputStreamWriter增強(qiáng)了FileOutputStream,讓它擁有了直接寫(xiě)字符的能力,BufferedWriter增強(qiáng)了OutputStreamWriter,讓它擁有了緩存的能力。
同樣的,InputStreamReader增強(qiáng)了FileInputStream,讓它擁有了直接讀字符的能力,BufferedReader增強(qiáng)InputStreamReader,讓它擁有了緩存的能力。
最后的話(huà)
Java的IO類(lèi)庫(kù)以前我也看得一臉懵逼,總是覺(jué)得太繁瑣,太難記了。后來(lái)學(xué)了裝飾者模式才知道,要搞懂Java的IO類(lèi)庫(kù),其實(shí)重點(diǎn)是要搞懂裝飾者模式。
如果不懂裝飾者模式,看多少次也不會(huì)理解為什么要這么設(shè)計(jì)。
當(dāng)一把鎖被鎖上的時(shí)候,你一直盯著鎖看是沒(méi)有用的,因?yàn)殍€匙肯定不是插在鎖上,一定要去別的地方找鑰匙啊。
總結(jié)
以上是生活随笔為你收集整理的java i o不会的地方_Java I/O 好复杂,傻傻分不清楚,别担心,我们有线索了。。。...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一般打胎要多少钱啊,都有什么价位的呢
- 下一篇: java 1 11 111_456756