裝飾者模式
因?yàn)閖ava的IO是基于裝飾者模式設(shè)計(jì)的幻妓,所以要了解掌握IO 必須要先清楚什么事裝飾者模式(裝飾者模式也稱為包裝模式蹦误,其使用一種對(duì)客戶端透明的方式動(dòng)態(tài)的擴(kuò)展對(duì)象功能。裝飾者模式也是繼承關(guān)系的替代方案之一肉津。裝飾者模式是結(jié)構(gòu)型設(shè)計(jì)模式强胰。重點(diǎn)也在裝飾二字)
- 裝飾者模式定義: 裝飾者模式是指在不必改變?cè)愇募褪褂美^承的前提下,動(dòng)態(tài)的擴(kuò)展一個(gè)對(duì)象的功能妹沙,通過創(chuàng)建一個(gè)包裝對(duì)象偶洋,也就是裝飾者來包裹真實(shí)對(duì)象。
- 具體實(shí)例:
//1.首先抽象出一個(gè) 人 的類 用來表示一個(gè)人的穿著
public abstract class Person {
/**
* 一個(gè)人的衣著(風(fēng)格距糖,飾品)
*/
public abstract void wear();
}
//2.在定義一個(gè)具體的人 韓梅梅 并實(shí)現(xiàn)穿著 函數(shù)
public class HanMeiMei extends Person {
@Override
public void wear() {
System.out.println("一身素雅的連衣裙");
}
}
//3.定義裝飾者 ,抽象出裝飾者的父類
//裝飾器的抽象父類
public class PersonDecorator extends Person {
private Person person;
//這是重點(diǎn)玄窝,裝飾器必須持有一個(gè)被裝飾者對(duì)象。
public PersonDecorator(Person person) {
this.person = person;
}
@Override
public void wear() {
person.wear();
}
}
//4. 實(shí)現(xiàn)一個(gè)具體的裝飾者
public class ConcretePersonDecorator extends PersonDecorator {
public ConcretePersonDecorator(Person person) {
super(person);
}
@Override
public void wear() {
headWear();
// 拓展時(shí)肾筐,前后都可以加方法
super.wear();
shoes();
}
// 裝飾者方法 哆料,頭飾的穿著
private void headWear() {
System.out.println("穿戴頭飾,帶了了簪子 和 耳釘");
}
// 裝飾者方法 吗铐,鞋子的穿著
private void shoes() {
System.out.println("穿了一雙 米色的 小高跟东亦!");
}
}
裝飾者無非兩點(diǎn),一:擁有被裝飾者對(duì)象唬渗,二:透明的拓展原本的方法(wear())
- 使用
HanMeiMei meiMei = new HanMeiMei();
ConcretePersonDecorator decorator = new ConcretePersonDecorator(meiMei);
decorator.wear();
- 結(jié)果
穿戴頭飾典阵,帶了了簪子 和 耳釘
一身素雅的連衣裙
穿了一雙 米色的 小高跟!
- 裝飾者模式和代理模式的區(qū)別
裝飾者模式和代理模式很像镊逝,有的人容易混淆壮啊,其實(shí)區(qū)別的他們很簡單。這兩個(gè)模式他們都會(huì)擁有一個(gè)對(duì)象撑蒜。但是代理模式更強(qiáng)調(diào)的是代理二字歹啼。代理它的功能玄渗,而不是擴(kuò)展 ,不對(duì)功能進(jìn)行增強(qiáng)狸眼。裝飾者模式則重點(diǎn)在裝飾二字上藤树。給原有的對(duì)象添加(裝飾)新的功能。
IO簡介
- 數(shù)據(jù)流是一組有序拓萌,有起點(diǎn)和終點(diǎn)的字節(jié)的數(shù)據(jù)序列岁钓。包括輸入流和輸出流。
- ?流序列中的數(shù)據(jù)既可以是未經(jīng)加工的原始二進(jìn)制數(shù)據(jù)微王,也可以是經(jīng)一定編碼處理后符合某種格式規(guī)定的特定數(shù)據(jù)屡限。因此Java中的流分為兩種:
-1 字節(jié)流: 數(shù)據(jù)流中最小的數(shù)據(jù)單元是字節(jié)
-2 字符流: 數(shù)據(jù)流中最小的數(shù)據(jù)單元是字符, Java中的字符是Unicode編碼炕倘,一個(gè)字符占用兩個(gè)字節(jié)钧大。 - Java.io包中最重要的就是5個(gè)類和一個(gè)接口。5個(gè)類指的是File激才、OutputStream拓型、InputStream额嘿、Writer瘸恼、Reader;一個(gè)接口指的是Serializable册养。掌握了這些就掌握了Java I/O的精髓了东帅。
- Java I/O主要包括如下3層次:
- 流式部分——最主要的部分。如:OutputStream球拦、InputStream靠闭、Writer、Reader等
- 非流式部分——如:File類坎炼、RandomAccessFile類和FileDescriptor等類
-
其他——文件讀取部分的與安全相關(guān)的類愧膀,如:SerializablePermission類,以及與本地操作系統(tǒng)相關(guān)的文件系統(tǒng)的類谣光,如:FileSystem類和Win32FileSystem類和WinNTFileSystem類檩淋。
IO詳細(xì)介紹
在Android 平臺(tái),從應(yīng)用的角度出發(fā)萄金,我們最需要關(guān)注和研究的就是 字節(jié)流(Stream)字符流(Reader/Writer)和 File/ RandomAccessFile蟀悦。當(dāng)我們需要的時(shí)候再深入研究也未嘗不是一件好事。關(guān)于字符和字節(jié)氧敢,例如文本文件日戈,XML這些都是用字符流來讀取和寫入。而如RAR孙乖,EXE文件浙炼,圖片等非文本份氧,則用字節(jié)流來讀取和寫入。面對(duì)如此復(fù)雜的類關(guān)系弯屈,有一個(gè)點(diǎn)是我們必須要首先掌握的半火,那就是設(shè)計(jì)模式中的裝飾者模式,學(xué)會(huì)并理解裝飾者模式是搞懂流必備的前提條件季俩,這也是在文章開頭就先簡紹裝飾者模式的原因钮糖。
字節(jié)流
從流的整個(gè)發(fā)展歷史,出現(xiàn)的各種類之間的關(guān)系看酌住,都是沿用了修飾模式店归,就是一個(gè)類的功能可以用來修飾其他類,然后組合成為一個(gè)比較復(fù)雜的流酪我。比如說:
DataOutputStream out = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(file)));
從上面的代碼塊中大家不難看出這些類的關(guān)系:為了向文件中寫入數(shù)據(jù)消痛,首先需要?jiǎng)?chuàng)建一個(gè)FileOutputStream,然后為了提升訪問的效率都哭,所以將它發(fā)送給具備緩存功能的BufferedOutput-Stream,而為了實(shí)現(xiàn)與機(jī)器類型無關(guān)的java基本類型數(shù)據(jù)的輸出秩伞,所以,我們將緩存的流傳遞給了DataOutputStream欺矫。從上面的關(guān)系纱新, 可以看到 其根本目的都是為outputSteam添加額外的功能。而這種額外功能的添加就是采用了裝飾模式來構(gòu)建的代碼. 因此穆趴,學(xué)習(xí)流脸爱,必須要學(xué)好裝飾模式。
-
下面的圖是一個(gè)關(guān)于字節(jié)流的圖譜未妹,這張圖譜比較全面的概況了我們字節(jié)流中間的各個(gè)類以及他們之間的關(guān)系簿废。
字節(jié)流 類之間的關(guān)系
OutputStream -> FileOutputStream/FilterOutputStream ->DataOutputStream->bufferedOutputStream
- 從學(xué)習(xí)的角度來,我們應(yīng)該先掌握FilterOutputStream, 以及FileOutputStream络它,這兩個(gè)類是基本的類族檬,從繼承關(guān)系可以不難發(fā)現(xiàn)他們都是對(duì) abstract 類 OutputStream的拓展,是它的子類化戳。然而单料,伴隨著 對(duì) Stream流的功能的拓展,所以就出現(xiàn)了 DataOutputStream迂烁,(將java中的基礎(chǔ)數(shù)據(jù)類型寫入數(shù)據(jù)字節(jié)輸出流中看尼、保存在存儲(chǔ)介質(zhì)中光羞、然后可以用DataOutputStream從存儲(chǔ)介質(zhì)中讀取到程序中還原成java基礎(chǔ)類型)焦履。這里多提一句赡勘、DataOutputStream纪岁、FilterOutputStream三個(gè)類的關(guān)系的這種設(shè)計(jì)既使用了裝飾器模式 避免了類的爆炸式增長弱左。
- 為了提升Stream的執(zhí)行效率勃救,所以出現(xiàn)了bufferedOutputStream永部。bufferedOutputStream就是將本地添加了一個(gè)緩存的數(shù)組依鸥。在使用bufferedOutputStream之前每次從磁盤讀入數(shù)據(jù)的時(shí)候都是需要訪問多少byte數(shù)據(jù)就向磁盤中讀多少個(gè)byte的數(shù)據(jù),而出現(xiàn)bufferedOutputSteam之后兆览,策略就改了屈溉,會(huì)先讀取整個(gè)緩存空間相應(yīng)大小的數(shù)據(jù),這樣就是從磁盤讀取了一塊比較大的數(shù)據(jù)抬探,然后緩存起來子巾,從而減少了對(duì)磁盤的訪問的次數(shù)以達(dá)到提升性能的目的。
- 另外一方面小压,我們知道了outputStream(輸出流)的發(fā)展歷史后线梗,我們便可以知道如何使用outpuSteam了,同樣的方法怠益,我們可以運(yùn)用到inputStream中來仪搔,這樣對(duì)稱的解釋就出現(xiàn)到了inputStream相關(guān)的中來了,于是蜻牢,我們對(duì)整個(gè)字節(jié)流就有了全方位的理解烤咧,所以這樣子我們就不會(huì)感覺到流的復(fù)雜了。這個(gè)時(shí)候?qū)τ谄渌囊恍┳止?jié)流的使用(byteArrayOutputStream/PipeOutputStream/ObjectOutputStream)的學(xué)習(xí)就自需要在使用的時(shí)候看看API即可抢呆。
字符流
下圖則是一個(gè)關(guān)于字符流的圖譜煮嫌,這張圖譜比較全面的概況了我們字符流中間的各個(gè)類以及他們之間的關(guān)系。
字符流的學(xué)習(xí)和字節(jié)流的學(xué)習(xí)是一樣的镀娶,它和字節(jié)流有著同樣的發(fā)展過程立膛,只是,字節(jié)流面向的是我們未知或者即使知道了他們的編碼格式也意義不大的文件(png梯码,exe, zip)的時(shí)候是采用字節(jié),而面對(duì)一些我們知道文件構(gòu)造我們就能夠搞懂它的意義的文件(json好啰,xml轩娶,txt)等文件的時(shí)候我們還是需要以字符的形式來讀取,所以就出現(xiàn)了字符流框往。reader 和 Stream最大的區(qū)別我認(rèn)為是它包含了一個(gè)readline()接口 (在BufferedWriter這個(gè)類中)鳄抒, 這個(gè)接口標(biāo)明了,一行數(shù)據(jù)的意義椰弊,這也是可以理解的许溅,因?yàn)樽杂凶址啪邆湫械母拍睿喾醋止?jié)流中的行也就是一個(gè)字節(jié)符號(hào)秉版。
字符流的類間關(guān)系
-
Writer- >FilterWriter->BufferedWriter->OutputStreamWriter->FileWriter->其他
同時(shí)類比著學(xué)習(xí)Reader相關(guān)的類贤重。 -
FilterWriter/FilterReader
? 字符過濾輸出流、與FilterOutputStream功能一樣清焕、只是簡單重寫了父類的方法并蝗、目的是為所有裝飾類提供標(biāo)準(zhǔn)和基本的方法祭犯、要求子類必須實(shí)現(xiàn)核心方法、和擁有自己的特色滚停。這里FilterWriter沒有子類沃粗、可能其意義只是提供一個(gè)接口、留著以后的擴(kuò)展..... 本身是一個(gè)抽象類键畴。 -
BufferedWriter/BufferedReader
BufferedWriter是 Writer類的一個(gè)子類最盅。他的功能是為傳入的底層字符輸出流提供緩存功能、同樣當(dāng)使用底層字符輸出流向目的地中寫入字符或者字符數(shù)組時(shí)起惕、每寫入一次就要打開一次到目的地的連接檩禾、這樣頻繁的訪問不但效率低下、也有可能會(huì)對(duì)存儲(chǔ)介質(zhì)造成一定的破壞疤祭、比如當(dāng)我們向磁盤中不斷的寫入字節(jié)時(shí)盼产、夸張一點(diǎn)、將一個(gè)非常大單位是G的字節(jié)數(shù)據(jù)寫入到磁盤的指定文件中的勺馆,每寫入一個(gè)字節(jié)就要打開一次到這個(gè)磁盤的通道戏售,這個(gè)結(jié)果無疑是恐怖的。而當(dāng)我們使用BufferedWriter將底層字符輸出流 比如FileReader包裝一下之后草穆、我們可以在程序中先將要寫入到文件中的字符寫入到BufferedWriter的內(nèi)置緩存空間中灌灾、然后當(dāng)達(dá)到一定數(shù)量時(shí)、一次性寫入FileReader流中悲柱,此時(shí)FileReader就可以打開一次通道锋喜,將這個(gè)數(shù)據(jù)塊寫入到文件中,這樣做雖然不可能達(dá)到一次訪問就將所有數(shù)據(jù)寫入磁盤中的效果豌鸡、但也大大提高了效率和減少了磁盤的訪問量嘿般! -
OutputStreamWriter/InputStreamReader
? 輸入字符轉(zhuǎn)換流、是輸入字節(jié)流轉(zhuǎn)向輸入字符流的橋梁涯冠、用于將輸入字節(jié)流轉(zhuǎn)換成輸入字符流炉奴、通過指定的或者默認(rèn)的編碼將從底層讀取的字節(jié)轉(zhuǎn)換成字符返回到程序中、與OutputStreamWriter一樣蛇更、本質(zhì)也是使用其內(nèi)部的一個(gè)類來完成所有工作:StreamDecoder瞻赶、使用默認(rèn)或者指定的編碼將字節(jié)轉(zhuǎn)換成字符;OutputStreamWriter/ InputStreamReader只是對(duì)StreamEncoder/StreamDecoder進(jìn)行了封裝,isr內(nèi)部所有方法核心都是調(diào)用StreamDecoder來完成的派任,InputStreamReader只是對(duì)StreamDecoder進(jìn)行了封裝砸逊、使得我們可以直接使用讀取方法、而不用關(guān)心內(nèi)部實(shí)現(xiàn)掌逛。 - OutputStreamWriter师逸、InputStreamReader分別為InputStream、OutputStream的低級(jí)輸入輸出流提供將字節(jié)轉(zhuǎn)換成字符的橋梁颤诀、他們只是外邊的一個(gè)門面字旭、真正的核心是下面兩個(gè)類:
- OutputStreamWriter中的StreamEncoder:
1对湃、使用指定的或者默認(rèn)的編碼集將字符轉(zhuǎn)碼為字節(jié)
2、調(diào)用StreamEncoder自身實(shí)現(xiàn)的寫入方法將轉(zhuǎn)碼后的字節(jié)寫入到底層字節(jié)輸出流中遗淳。- InputStreamReader中的StreamDecoder:
1拍柒、使用指定的或者默認(rèn)的編碼集將字節(jié)解碼為字符
2、調(diào)用StreamDecoder自身實(shí)現(xiàn)的讀取方法將解碼后的字符讀取到程序中屈暗。
在理解這兩個(gè)流的時(shí)候要注意:java——io中只有將字節(jié)轉(zhuǎn)換成字符的類拆讯、沒有將字符轉(zhuǎn)換成字節(jié)的類、原因很簡單——字符流的存在本來就像對(duì)字節(jié)流進(jìn)行了裝飾养叛、加工處理以便更方便的去使用种呐、在使用這兩個(gè)流的時(shí)候要注意:由于這兩個(gè)流要頻繁的對(duì)讀取或者寫入的字節(jié)或者字符進(jìn)行轉(zhuǎn)碼、解碼和與底層流的源和目的地進(jìn)行交互弃甥、所以使用的時(shí)候要使用BufferedWriter爽室、BufferedReader進(jìn)行包裝、以達(dá)到最高效率淆攻、和保護(hù)存儲(chǔ)介質(zhì)阔墩。
-
FileReader/FileWriter
FileReader和FileWriter 繼承于InputStreamReader/OutputStreamWriter。
從源碼可以發(fā)現(xiàn)FileWriter 文件字符輸出流瓶珊、主要用于將字符寫入到指定的打開的文件中啸箫、其本質(zhì)是通過傳入的文件名、文件伞芹、或者文件描述符來創(chuàng)建FileOutputStream忘苛、然后使用OutputStreamWriter使用默認(rèn)編碼將FileOutputStream轉(zhuǎn)換成Writer(這個(gè)Writer就是FileWriter)。如果使用這個(gè)類的話唱较、最好使用BufferedWriter包裝一下扎唾、高端大氣上檔次、低調(diào)奢華有內(nèi)涵绊汹!
FileReader 文件字符輸入流稽屏、用于將文件內(nèi)容以字符形式讀取出來、一般用于讀取字符形式的文件內(nèi)容西乖、也可以讀取字節(jié)形式、但是因?yàn)镕ileReader內(nèi)部也是通過傳入的參數(shù)構(gòu)造InputStreamReader坛增、并且只能使用默認(rèn)編碼获雕、所以我們無法控制編碼問題、這樣的話就很容易造成亂碼收捣。所以讀取字節(jié)形式的文件還是使用字節(jié)流來操作的好届案、同樣在使用此流的時(shí)候用BufferedReader包裝一下、就算沖著BufferedReader的readLine()方法去的也要使用這個(gè)包裝類罢艾、不說他還能提高效率楣颠、保護(hù)存儲(chǔ)介質(zhì)尽纽。
字節(jié)流與字符流的關(guān)系
字節(jié)流與字符流的區(qū)別
字節(jié)流和字符流使用是非常相似的,那么除了操作代碼的不同之外童漩,還有哪些不同呢弄贿?
- 字節(jié)流在操作的時(shí)候本身是不會(huì)用到緩沖區(qū)(內(nèi)存)的,是與文件本身直接操作的矫膨,而字符流在操作的時(shí)候是要使用到緩沖區(qū)的 差凹,字節(jié)流在操作文件時(shí),即使不關(guān)閉資源(close方法)侧馅,文件也能輸出危尿,但是如果字符流不使用close方法的話,則不會(huì)輸出任何內(nèi)容馁痴,說明字符流用的是緩沖區(qū)谊娇,并且可以使用flush方法強(qiáng)制進(jìn)行刷新緩沖區(qū),這時(shí)才能在不close的情況下輸出內(nèi)容罗晕。
開發(fā)中究竟用字節(jié)流好還是用字符流好呢济欢?
在所有的硬盤上保存文件或進(jìn)行傳輸?shù)臅r(shí)候都是以字節(jié)的方法進(jìn)行的,包括圖片也是按字節(jié)完成攀例,而字符是只有在內(nèi)存中才會(huì)形成的船逮,所以使用字節(jié)的操作是最多的。
如果要java程序?qū)崿F(xiàn)一個(gè)拷貝功能粤铭,應(yīng)該選用字節(jié)流進(jìn)行操作(可能拷貝的是圖片)挖胃,并且采用邊讀邊寫的方式(節(jié)省內(nèi)存)。
字節(jié)流與字符流的轉(zhuǎn)換
雖然Java支持字節(jié)流和字符流梆惯,但有時(shí)需要在字節(jié)流和字符流兩者之間轉(zhuǎn)換酱鸭。InputStreamReader和OutputStreamWriter,這兩個(gè)為類是字節(jié)流和字符流之間相互轉(zhuǎn)換的類垛吗。
InputSreamReader用于將一個(gè)字節(jié)流中的字節(jié)解碼成字符:
有兩個(gè)構(gòu)造方法:
InputStreamReader(InputStream in);
功能:用默認(rèn)字符集創(chuàng)建一個(gè)InputStreamReader對(duì)象
InputStreamReader(InputStream in,String CharsetName);
功能:接收已指定字符集名的字符串凹髓,并用該字符創(chuàng)建對(duì)象
OutputStreamWriter用于將寫入的字符編碼成字節(jié)后寫入一個(gè)字節(jié)流。
同樣有兩個(gè)構(gòu)造方法:
OutputStreamWriter(OutputStream out);
功能:用默認(rèn)字符集創(chuàng)建一個(gè)OutputStreamWriter對(duì)象怯屉;
OutputStreamWriter(OutputStream out,String CharSetName);
功能:接收已指定字符集名的字符串蔚舀,并用該字符集創(chuàng)建OutputStreamWrite對(duì)象
為了避免頻繁的轉(zhuǎn)換字節(jié)流和字符流,對(duì)以上兩個(gè)類進(jìn)行了封裝锨络。1) BufferedWriter類封裝了OutputStreamWriter類赌躺;2) BufferedReader類封裝了InputStreamReader類;
封裝格式:
BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in= new BufferedReader(new InputStreamReader(System.in);
利用下面的語句羡儿,可以從控制臺(tái)讀取一行字符串:
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String line=in.readLine();
File操作RandomAccessFile
為什么需要RandomAccessFile 礼患? RandomAccessFile 可以對(duì)文件進(jìn)行分段操作,利用RandomAccessFIle 就可以實(shí)現(xiàn)多線程下載,斷點(diǎn)續(xù)傳等功能缅叠。
-
構(gòu)造方法: RandomAccessFile raf = newRandomAccessFile(File file, String mode);
其中參數(shù) mode 的值可選 "r":可讀悄泥,"w" :可寫,"rw":可讀性肤粱; - 成員方法: seek(int index);可以將指針移動(dòng)到某個(gè)位置開始讀寫弹囚。 setLength(long len);給寫入文件預(yù)留空間(用來構(gòu)建一個(gè)設(shè)置大小的空文件 來 存儲(chǔ)東西)。
RandomAccessFile 特點(diǎn)和優(yōu)勢(shì)
-
既可以讀也可以寫
RandomAccessFile不屬于InputStream和OutputStream類系的它是一個(gè)完全獨(dú)立的類狼犯,所有方法(絕大多數(shù)都只屬于它自己)都是自己從頭開始規(guī)定的,這里面包含讀寫兩種操作 -
可以指定位置進(jìn)行讀寫
RandomAccessFile能在文件里面前后移動(dòng)余寥,在文件里移動(dòng)用的seek( ),所以它的行為與其它的I/O類有些根本性的不同∶跎總而言之宋舷,它是一個(gè)直接繼承Object的,獨(dú)立的類瓢姻。只有RandomAccessFile才有seek搜尋方法祝蝠,而這個(gè)方法也只適用于文件.
IO的一些實(shí)例代碼
- 工具類
public class FileUtil {
public static File getFile(String fleName) {
File directory = new File("src/text/");//目錄
if(!directory.exists()){
boolean mkdirs = directory.mkdirs();
if(!mkdirs)return null;
}
File file = new File(directory.getPath()+"/"+fleName);
if(!file.exists()){
boolean newFile = false;
try {
newFile = file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(!newFile) return null;
}
return file;
}
public static void clean(File file){
try {
FileWriter fileWriter =new FileWriter(file);
fileWriter.write("");
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- DataOutputStream 和 DataInputStream
public class DataStreamTest {
public static void main(String[] args) {
File file = FileUtil.getFile("DataStream.txt");
testDataOutputStream(file);
testDataInputStream(file);
}
private static File getFile(){
File file = new File("src/text/DataStream.txt");
boolean success;
if(!file.exists()){
try {
success = file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
success = false;
}
}else {
success = true;
}
if(success){
return file;
}else {
return null;
}
}
private static void testDataOutputStream(File file) {
try {
DataOutputStream outputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
outputStream.writeInt(123);
outputStream.writeBoolean(true);
outputStream.writeLong(21384329483248L);
outputStream.writeChar(0x23231);
outputStream.writeUTF("你是咕咕雞么");
outputStream.writeInt(456);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
System.out.println("操作錯(cuò)誤:"+e.getMessage());
}
}
private static void testDataInputStream(File file) {
try {
//使用DataInputStream讀取文件中數(shù)據(jù)的時(shí)候,需要和DataOutputStream中使用的寫入方法對(duì)應(yīng)幻碱,否則讀出的數(shù)據(jù)就是錯(cuò)誤的
DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
System.out.println(inputStream.readInt());
System.out.println(inputStream.readBoolean());
System.out.println(inputStream.readLong());
System.out.println(inputStream.readChar());
System.out.println(inputStream.readUTF());
System.out.println(inputStream.readInt());
} catch (Exception e) {
e.printStackTrace();
System.out.println("操作錯(cuò)誤:" + e.getMessage());
}
}
}
- OutputStreamWriter
public class OutputStreamWriterTest {
private final static String STRING = "I like android very mach === 這是優(yōu)美的漢字 ";
public static void main(String[] args) {
test();
}
private static void test() {
File file = FileUtil.getFile("OutputStreamWriter.txt");
if(file == null) return;
FileUtil.clean(file);//清空文件
try {
// true, 設(shè)置內(nèi)容可以追加
FileOutputStream fos = new FileOutputStream(file, true);
//todo 是否有一個(gè)封裝好的writer绎狭? FileWriter 封裝了FileOutputStream
OutputStreamWriter oswDef = new OutputStreamWriter(fos);
BufferedWriter bwDef = new BufferedWriter(oswDef);
bwDef.write(STRING);
bwDef.newLine();//寫一個(gè)換行符
bwDef.flush();
// bwDef.close(); //不能在這里進(jìn)行流的關(guān)閉操作,因?yàn)闀?huì)同時(shí)關(guān)閉 fos 這樣下面的fos就不能操作了 要報(bào)錯(cuò)
System.out.println("默認(rèn)編碼方式:"+oswDef.getEncoding());
OutputStreamWriter oswGBK = new OutputStreamWriter(fos, "GBK");
BufferedWriter bwGBK = new BufferedWriter(oswGBK);
bwGBK.write(STRING + "----->GBK");
bwGBK.newLine();
bwGBK.flush();
System.out.println("指定編碼方式GBK: " + oswGBK.getEncoding());
OutputStreamWriter oswUTF8= new OutputStreamWriter(fos, StandardCharsets.UTF_8);
BufferedWriter bwUTF8 = new BufferedWriter(oswUTF8);
bwUTF8.write(STRING + "----->UTF_8");
bwUTF8.newLine();
bwUTF8.flush();
System.out.println("指定編碼方式UTF-8: " + oswUTF8.getEncoding());
bwDef.close();
bwGBK.close();
bwUTF8.close();
}catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
結(jié)果:
I like android very mach === 這是優(yōu)美的漢字
I like android very mach === ????????????? ----->GBK
I like android very mach === 這是優(yōu)美的漢字 ----->UTF_8
- InputStreamReader
public class InputStreamReaderTest {
public static void main(String[] args) {
File file = FileUtil.getFile("OutputStreamWriter.txt");
if(file == null) return;
try {
FileInputStream inputStream1 = new FileInputStream(file);
FileInputStream inputStream2 = new FileInputStream(file);
FileInputStream inputStream3 = new FileInputStream(file);
test(inputStream1,null);//使用默認(rèn)編碼方式
test(inputStream2,"GBK");//使用 GBK 編碼方式
test(inputStream3, StandardCharsets.UTF_8.toString());//使用UTF-8編碼方式
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//使用默認(rèn)的編碼方式
private static void test(InputStream is,String charsetName){
try{
InputStreamReader isr;
if(charsetName == null){
isr = new InputStreamReader(is);
}else {
isr = new InputStreamReader(is,charsetName);
}
BufferedReader br = new BufferedReader(isr);
String string;
System.out.println("編碼方式: " + isr.getEncoding());
while ((string = br.readLine()) != null) {
System.out.println(string);
}
System.out.println("====================================================== the end !");
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
結(jié)果:
編碼方式: UTF8
I like android very mach === 這是優(yōu)美的漢字
I like android very mach === ????????????? ----->GBK
I like android very mach === 這是優(yōu)美的漢字 ----->UTF_8
====================================================== the end !
編碼方式: GBK
I like android very mach === 榪欐槸浼樼編鐨勬眽瀛?
I like android very mach === 這是優(yōu)美的漢字 ----->GBK
I like android very mach === 榪欐槸浼樼編鐨勬眽瀛? ----->UTF_8
====================================================== the end !
編碼方式: UTF8
I like android very mach === 這是優(yōu)美的漢字
I like android very mach === ????????????? ----->GBK
I like android very mach === 這是優(yōu)美的漢字 ----->UTF_8
====================================================== the end !
- BufferedOutputStream 和 BufferedInputStream
public class BufferedStreamTest {
private static final byte[] byteArray = {
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
};
public static void main(String[] args) {
File file = FileUtil.getFile("bufferedOutputStream.txt");
testOutput(file);
testInput(file);
}
private static void testOutput(File file) {
if (file == null) return;
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bos.write(byteArray[0]);
bos.write(byteArray, 1, 25);
bos.write(10);//換行符的ascii碼值
bos.write(byteArray, 26, 26);
//加上換行符 總共寫入 53個(gè)字節(jié) 褥傍,一個(gè)字母儡嘶、數(shù)字 占一個(gè)字節(jié)
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void testInput(File file) {
if (file == null) return;
try {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file));
System.out.println("文件的總字節(jié)數(shù):" + bin.available());
//打印前10個(gè)字節(jié) ,bin.available() 刻度字節(jié)的位數(shù) bin.read() 讀取一個(gè)字節(jié)
for (int i = 0; i < 5; i++) {
if (bin.available() >= 0) {
System.out.println(byteToString((byte) bin.read()) + "==可讀字節(jié)數(shù)===>" + bin.available());
}
}
System.out.println("=====================分隔線============================");
//判斷輸入流是否支持 mark() 和 reset() 方法
if (bin.markSupported()) {
//mark()參數(shù)readlimit:表示標(biāo)記位置無效之前要讀取的字節(jié)數(shù)
//調(diào)用mark(int readlimit)方法后讀取多少字節(jié)標(biāo)記才失效恍风,
// 是取readlimit和BufferedInputStream類的緩沖區(qū)大小兩者中的最大值蹦狂,而并非完全由readlimit確定
bin.mark(10);
byte[] b = new byte[1024];
int n1 = bin.read(b, 0, b.length);
printByteValue(b);
System.out.println("剩余的有效字節(jié)數(shù) : " + n1);//為什么是48 因?yàn)檫M(jìn)行了mark標(biāo)記
// reset() 將流重新定位在此輸入流上最后一次調(diào)用mark方法的位置 (這里是 下標(biāo)為5的地方,因?yàn)閙ark前讀取了5次)
bin.reset();
}
System.out.println(byteToString((byte) bin.read()));
System.out.println("=====================分隔線============================");
//skip 參數(shù)表示要跳過的字節(jié)數(shù)朋贬,返回值表示實(shí)際跳過的字節(jié)數(shù)凯楔,(可能剩余字節(jié)數(shù)小于需要跳過的字節(jié)數(shù))
long skip = bin.skip(1);
System.out.println("實(shí)際跳過字節(jié)數(shù):" + skip);
System.out.println(byteToString((byte) bin.read()));
System.out.println("=====================分隔線============================");
byte[] b = new byte[1024];
int n2 = bin.read(b,0, b.length);
//只有使用 bin.read() 才會(huì)減少剩余有效字節(jié)數(shù)
System.out.println("剩余的有效字節(jié)數(shù) : " + n2);
printByteValue(b);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String byteToString(byte b) {
byte[] bArray = ;
return new String(bArray);
}
private static void printByteValue(byte[] buf) {
for (byte b : buf) {
if (b != 0) {
System.out.print(byteToString(b) + " ");
}
}
}
}
結(jié)果:
文件的總字節(jié)數(shù):53
A==可讀字節(jié)數(shù)===>52
B==可讀字節(jié)數(shù)===>51
C==可讀字節(jié)數(shù)===>50
D==可讀字節(jié)數(shù)===>49
E==可讀字節(jié)數(shù)===>48
=====================分隔線============================
F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z 剩余的有效字節(jié)數(shù) : 48
F
=====================分隔線============================
實(shí)際跳過字節(jié)數(shù):1
H
=====================分隔線============================
剩余的有效字節(jié)數(shù) : 45
I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
- BufferedWriter 和 BufferedReader
public class BufferedWriteReaderTest {
public static void main(String[] args) throws IOException {
File srcFile = FileUtil.getFile("BufferedReader.txt");
File dstFile = FileUtil.getFile("BufferedWrite.txt");
if(srcFile == null || dstFile == null)return;
BufferedWriter bw = new BufferedWriter(new FileWriter(dstFile));
BufferedReader br = new BufferedReader(new FileReader(srcFile));
char[] string = new char[1024]; // 請(qǐng)注意此處不是byte锦募,而是char
while ((br.read(string)) != -1) {
bw.write(string);
}
br.close();
bw.flush();
bw.close();
}
}
適用于將一個(gè)文件的字符串內(nèi)容拷貝到另一個(gè)文件中摆屯。
- RandomAccessFile
public class RandomAccessFileTest {
private static final String FILE_NAME = "rafFile";
public static void main(String[] args) {
testWriter();
testReader();
}
/**
* 向文件中寫入內(nèi)容
*/
private static void testWriter() {
File file = FileUtil.getFile(FILE_NAME);
if (file == null) return;
FileUtil.clean(file);//清空文件避免干擾
try {
//第二個(gè)參數(shù) mode: "r"表示可讀,"w"表示可寫 ,"rw"表示文件可讀可寫
RandomAccessFile rsfWriter = new RandomAccessFile(file, "rw");
//seek() 不會(huì)改變文件大小糠亩、但是他會(huì)將下一個(gè)字符的寫入位置標(biāo)識(shí)為10000虐骑、
//也就是說此后只要寫入內(nèi)容、就是從10001開始存赎线、
rsfWriter.seek(10000);
printFileLength(rsfWriter); //文件長度為: 0 富弦,因?yàn)槲募€沒有寫入東西
printSplitLine();
//每個(gè)漢子占3個(gè)字節(jié)、寫入字符串的時(shí)候會(huì)有一個(gè)記錄寫入字符串長度的2個(gè)字節(jié)
rsfWriter.writeUTF("我是一只安卓小菜鳥");
printFileLength(rsfWriter);// length:10029 point:10029
printSplitLine();
//會(huì)改變文件大小氛驮、只是把文件的size改變、
//并沒有改變下一個(gè)要寫入的內(nèi)容的位置济似、
rsfWriter.setLength(10100);
//每個(gè)字符占兩個(gè)字節(jié)
rsfWriter.writeChars("abcde");
printFileLength(rsfWriter); //length: 10100 point:10039
printSplitLine();
//再從“文件指針”為5000的地方插一個(gè)長度為100矫废、內(nèi)容全是'a'的字符數(shù)組
//這里file長依然是10100盏缤、因?yàn)樗菑摹拔募羔槨睘?000的地方覆蓋后面
//的200個(gè)字節(jié)、下標(biāo)并沒有超過文件長度
rsfWriter.seek(5000);
char[] cbuf = new char[100];
for (int i = 0; i < cbuf.length; i++) {
cbuf[i] = 'a';
rsfWriter.writeChar(cbuf[i]);
}
printFileLength(rsfWriter); //length: 10100 point:5200
printSplitLine();
//這里file長依然是10100 , 因?yàn)樗菑摹拔募羔槨睘?000的地方覆蓋后面
//的100個(gè)字節(jié)蓖扑、下標(biāo)并沒有超過文件長度
byte[] bbuf = new byte[100];
Arrays.fill(bbuf, (byte) 0x41);
rsfWriter.seek(1000);
rsfWriter.writeBytes(new String(bbuf));
printFileLength(rsfWriter);
printSplitLine();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 讀取文件中內(nèi)容
* 這里我們要清楚現(xiàn)在文件中有什么內(nèi)容唉铜、而且還要清楚這些內(nèi)容起始字節(jié)下標(biāo)、長度
*/
private static void testReader() {
/*
簡要說明rafFile文件中的內(nèi)容
0-1000:空白
1000-1100:為大寫字母A,(0X41)
5000-5200:為100個(gè)字符 'a'
10000-10029:我是一只安卓小菜鳥 (包含2個(gè)文件長度)
10030-10039: abcde
*/
System.out.println();
System.out.println("===================這是讀取分割線====================");
System.out.println();
File file = FileUtil.getFile(FILE_NAME);
if (file == null) return;
try {
RandomAccessFile rsfReader = new RandomAccessFile(file, "r");
//可按照自己想讀取的東西所在的位置律杠、長度來讀取
//讀取"我是一只安卓小菜鳥"
rsfReader.seek(10000);
System.out.println(rsfReader.readUTF());
printSplitLine();
//讀取字符'abcde'
byte[] bbuf1 = new byte[10];
rsfReader.seek(10030);
rsfReader.read(bbuf1);
System.out.println(new String(bbuf1));
printSplitLine();
//讀取100個(gè)字符a
rsfReader.seek(5000);
byte[] bbuf2 = new byte[200];
rsfReader.read(bbuf2);
System.out.println(new String(bbuf2));
printSplitLine();
//讀取100個(gè)A
rsfReader.seek(1000);
byte[] bbuf3 = new byte[100];
rsfReader.read(bbuf3);
System.out.println(new String(bbuf3));
printSplitLine();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打印文件長度
*
* @param rsfWriter 指向文件的隨機(jī)文件流
*/
private static void printFileLength(RandomAccessFile rsfWriter) {
try {
System.out.println("文件長度: " + rsfWriter.length() + " <============> 文件指針位置(當(dāng)前偏移量): " + rsfWriter.getFilePointer());
} catch (IOException e) {
e.printStackTrace();
}
}
private static void printSplitLine() {
System.out.println();
System.out.println("====================我是一條帥氣的分割線=========================");
System.out.println();
}
}
結(jié)果: