Java I/O流(1)-- BIO

I/O的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO犁罩、異步非阻塞的AIO偿凭。

基于不同的IO抽象模型和交互方式产弹,可以對IO進(jìn)行簡單區(qū)分:

* 傳統(tǒng)的IO基于流模型實(shí)現(xiàn),交互方式是同步弯囊、阻塞的方式痰哨,也就是說胶果,在讀取輸入流或者寫入輸出流時(shí),在讀斤斧、寫動(dòng)作完成之前早抠,線程會一直阻塞在那里,它們之間的調(diào)用是可靠的線性順序折欠。也稱為BIO贝或。

* 在java1.4中引入了NIO框架,提供了Channel锐秦、Selector咪奖、Buffer等新的抽象,可以構(gòu)建多路復(fù)用的酱床、同步非阻塞IO羊赵。

* 在Java7中,NIO有了進(jìn)一步的改進(jìn)扇谣,也就是NIO2昧捷,引入了異步非阻塞IO方式,也叫AIO(Asynchronous IO)罐寨。異步IO操作基于事件和回調(diào)機(jī)制靡挥,可以簡單理解為,應(yīng)用操作直接返回鸯绿,而不會阻塞在那里跋破,當(dāng)后臺處理完成,操作系統(tǒng)會通知相應(yīng)線程進(jìn)行后續(xù)工作瓶蝴。

BIO

在JDK1.4出來之前毒返,我們建立網(wǎng)絡(luò)連接的時(shí)候采用BIO模式,需要先在服務(wù)端啟動(dòng)一個(gè)ServerSocket舷手,然后在客戶端啟動(dòng)Socket來對服務(wù)端進(jìn)行通信拧簸,默認(rèn)情況下服務(wù)端需要對每個(gè)請求建立一堆線程等待請求,而客戶端發(fā)送請求后男窟,先咨詢服務(wù)端是否有線程相應(yīng)盆赤,如果沒有則會一直等待或者遭到拒絕請求,如果有的話蝎宇,客戶端會線程會等待請求結(jié)束后才繼續(xù)執(zhí)行弟劲。

IO流的分類

根據(jù)操作對象可分為:

磁盤操作:File

字節(jié)操作:InputStream 和OutputStream

字符操作:Reader 和Writer

對象操作:Serializable

網(wǎng)絡(luò)操作:Socket

根據(jù)處理數(shù)據(jù)類型的不同分為:字符流和字節(jié)流,字節(jié)流一次讀入或讀出是8位二進(jìn)制姥芥,字符流一次讀入或讀出是16位二進(jìn)制兔乞。

根據(jù)數(shù)據(jù)流向不同分為:輸入流和輸出流,輸入流只能進(jìn)行讀操作,輸出流只能進(jìn)行寫操作庸追。

結(jié)論:只要是處理純文本數(shù)據(jù)霍骄,就優(yōu)先考慮使用字符流。 除此之外都使用字節(jié)流淡溯。

按照流是否直接與特定的地方(如磁盤读整、內(nèi)存、設(shè)備等)相連咱娶,分為節(jié)點(diǎn)流和處理流兩類:

1)節(jié)點(diǎn)流:直接與數(shù)據(jù)源相連米间,讀入或讀出。

常用節(jié)點(diǎn)流:

父 類:InputStream 膘侮、OutputStream屈糊、 Reader、Writer

文 件:FileInputStream 琼了、FileOutputStrean 逻锐、FileReader 、FileWriter 文件進(jìn)行處理的節(jié)點(diǎn)流

數(shù) 組:ByteArrayInputStream雕薪、ByteArrayOutputStream昧诱、 CharArrayReader 、CharArrayWriter對數(shù)組進(jìn)行處理的節(jié)點(diǎn)流(對應(yīng)的不再是文件所袁,而是內(nèi)存中的一個(gè)數(shù)組)

字符串:StringReader盏档、 StringWriter 對字符串進(jìn)行處理的節(jié)點(diǎn)流

管 道:PipedInputStream 、PipedOutputStream燥爷、PipedReader 妆丘、PipedWriter對管道進(jìn)行處理的節(jié)點(diǎn)流

2)處理流:直接使用節(jié)點(diǎn)流,讀寫不方便局劲,為了更快的讀寫文件,才有了處理流奶赠。處理流和節(jié)點(diǎn)流一塊使用鱼填,在節(jié)點(diǎn)流的基礎(chǔ)上,再套接一層毅戈,套接在節(jié)點(diǎn)流上的就是處理流苹丸。

常用處理流:

緩沖流:BufferedInputStrean 、BufferedOutputStream苇经、 BufferedReader赘理、BufferedWriter增加緩沖功能,避免頻繁讀寫硬盤扇单∩棠#可修飾所有從節(jié)點(diǎn)流父類繼承的對應(yīng)節(jié)點(diǎn)流。

轉(zhuǎn)換流:InputStreamReader 、OutputStreamReader實(shí)現(xiàn)字節(jié)流和字符流之間的轉(zhuǎn)換施流。

數(shù)據(jù)流:DataInputStream 响疚、DataOutputStream等-提供將基礎(chǔ)數(shù)據(jù)類型寫入到文件中,或者讀取出來瞪醋。

什么時(shí)候使用轉(zhuǎn)換流忿晕?

1)源或者目的對應(yīng)的設(shè)備是字節(jié)流,但是操作的卻是文本數(shù)據(jù)银受,可以使用轉(zhuǎn)換作為橋梁践盼。提高對文本操作的便捷。

2)一旦操作文本涉及到具體的指定編碼表時(shí)宾巍,必須使用轉(zhuǎn)換流咕幻。

IO流的交互方式是同步、阻塞的方式蜀漆,也就是說谅河,在讀取輸入流或者寫入輸出流時(shí),在讀确丢、寫動(dòng)作完成之前绷耍,線程會一直阻塞在那里,他們之間的調(diào)用是可靠的線性順序鲜侥。

BIO常見操作示例:

1)磁盤操作

File 類可以用于表示文件和目錄的信息褂始,但是它不表示文件的內(nèi)容。

遞歸地列出一個(gè)目錄下所有文件:

public static void listAllFiles(File dir) {

??? if (dir == null ||!dir.exists()) {

??????? return;

??? }

??? if (dir.isFile()) {

???????System.out.println(dir.getName());

??????? return;

??? }

??? for (File file :dir.listFiles()) {

??????? listAllFiles(file);

??? }

}

從 Java7 開始描函,可以使用 Paths 和 Files 代替 File崎苗。

2字節(jié)操作

實(shí)現(xiàn)文件復(fù)制

public static void copyFile(String src, String dist) throwsIOException {

??? FileInputStream in = newFileInputStream(src);

??? FileOutputStream out =new FileOutputStream(dist);

? ? byte[] buffer = newbyte[20 * 1024];

??? int cnt;


??? // read()最多讀取 buffer.length 個(gè)字節(jié)

??? //返回的是實(shí)際讀取的個(gè)數(shù)

??? //返回 -1 的時(shí)候表示讀到 eof,即文件尾

??? while ((cnt =in.read(buffer, 0, buffer.length)) != -1) {

??????? out.write(buffer, 0,cnt);

??? }


??? in.close();

??? out.close();

}


裝飾者模式

Java I/O 使用了裝飾者模式來實(shí)現(xiàn)舀寓。以 InputStream 為例胆数,

InputStream 是抽象組件;

FileInputStream 是 InputStream 的子類互墓,屬于具體組件必尼,提供了字節(jié)流的輸入操作;

FilterInputStream 屬于抽象裝飾者篡撵,裝飾者用于裝飾組件判莉,為組件提供額外的功能。例如 BufferedInputStream 為 FileInputStream 提供緩存的功能育谬。

實(shí)例化一個(gè)具有緩存功能的字節(jié)流對象時(shí)券盅,只需要在 FileInputStream 對象上再套一層 BufferedInputStream 對象即可。

FileInputStream fileInputStream = new FileInputStream(filePath);

BufferedInputStream bufferedInputStream = newBufferedInputStream(fileInputStream);

DataInputStream 裝飾者提供了對更多數(shù)據(jù)類型進(jìn)行輸入的操作膛檀,比如 int锰镀、double 等基本類型娘侍。

3)字符操作

編碼與解碼

編碼就是把字符轉(zhuǎn)換為字節(jié),而解碼是把字節(jié)重新組合成字符互站。

如果編碼和解碼過程使用不同的編碼方式那么就出現(xiàn)了亂碼私蕾。

GBK 編碼中,中文字符占 2 個(gè)字節(jié)胡桃,英文字符占 1 個(gè)字節(jié)踩叭;

UTF-8 編碼中,中文字符占 3 個(gè)字節(jié)翠胰,英文字符占 1 個(gè)字節(jié)容贝;

UTF-16be 編碼中,中文字符和英文字符都占 2 個(gè)字節(jié)之景。

UTF-16be 中的 be 指的是 Big Endian斤富,也就是大端。相應(yīng)地也有 UTF-16le锻狗,le 指的是 Little Endian满力,也就是小端。

Java 的內(nèi)存編碼使用雙字節(jié)編碼 UTF-16be轻纪,這不是指 Java 只支持這一種編碼方式油额,而是說 char 這種類型使用 UTF-16be 進(jìn)行編碼。char 類型占 16 位刻帚,也就是兩個(gè)字節(jié)潦嘶,Java 使用這種雙字節(jié)編碼是為了讓一個(gè)中文或者一個(gè)英文都能使用一個(gè) char 來存儲。

String 的編碼方式

String 可以看成一個(gè)字符序列崇众,可以指定一個(gè)編碼方式將它編碼為字節(jié)序列掂僵,也可以指定一個(gè)編碼方式將一個(gè)字節(jié)序列解碼為 String。

String str1 = "中文";

byte[] bytes = str1.getBytes("UTF-8");

String str2 = new String(bytes, "UTF-8");

System.out.println(str2);

在調(diào)用無參數(shù) getBytes() 方法時(shí)顷歌,默認(rèn)的編碼方式不是 UTF-16be锰蓬。雙字節(jié)編碼的好處是可以使用一個(gè) char 存儲中文和英文,而將 String 轉(zhuǎn)為 bytes[] 字節(jié)數(shù)組就不再需要這個(gè)好處眯漩,因此也就不再需要雙字節(jié)編碼互妓。getBytes() 的默認(rèn)編碼方式與平臺有關(guān),一般為 UTF-8坤塞。

byte[] bytes = str1.getBytes();

Reader Writer

不管是磁盤還是網(wǎng)絡(luò)傳輸,最小的存儲單元都是字節(jié)澈蚌,而不是字符摹芙。但是在程序中操作的通常是字符形式的數(shù)據(jù),因此需要提供對字符進(jìn)行操作的方法宛瞄。

InputStreamReader實(shí)現(xiàn)從字節(jié)流解碼成字符流浮禾;

OutputStreamWriter實(shí)現(xiàn)字符流編碼成為字節(jié)流交胚。

實(shí)現(xiàn)逐行輸出文本文件的內(nèi)容

public static void readFileContent(String filePath) throwsIOException {

??? FileReader fileReader =new FileReader(filePath);

??? BufferedReaderbufferedReader = new BufferedReader(fileReader);


??? String line;

??? while ((line =bufferedReader.readLine()) != null) {

???????System.out.println(line);

??? }


??? //裝飾者模式使得 BufferedReader 組合了一個(gè) Reader 對象

??? //在調(diào)用 BufferedReader 的 close() 方法時(shí)會去調(diào)用 Reader 的 close() 方法

??? //因此只要一個(gè) close() 調(diào)用即可

??? bufferedReader.close();

}


4)對象操作

序列化

序列化就是將一個(gè)對象轉(zhuǎn)換成字節(jié)序列,方便存儲和傳輸盈电。

序列化:ObjectOutputStream.writeObject()

反序列化:ObjectInputStream.readObject()

不會對靜態(tài)變量進(jìn)行序列化蝴簇,因?yàn)樾蛄谢皇潜4鎸ο蟮臓顟B(tài),靜態(tài)變量屬于類的狀態(tài)匆帚。


Serializable

序列化的類需要實(shí)現(xiàn) Serializable 接口熬词,它只是一個(gè)標(biāo)準(zhǔn),沒有任何方法需要實(shí)現(xiàn)吸重,但是如果不去實(shí)現(xiàn)它的話而進(jìn)行序列化互拾,會拋出異常。

public static void main(String[] args) throws IOException,ClassNotFoundException {

??? A a1 = new A(123,"abc");

? ??String objectFile = "file/a1";


??? ObjectOutputStreamobjectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile));

???objectOutputStream.writeObject(a1);

???objectOutputStream.close();


??? ObjectInputStreamobjectInputStream = new ObjectInputStream(new FileInputStream(objectFile));

??? A a2 = (A)objectInputStream.readObject();

???objectInputStream.close();

??? System.out.println(a2);

}


private static class A implements Serializable {

??? private int x;

??? private String y;


??? A(int x, String y) {

??????? this.x = x;

??????? this.y = y;

??? }


??? @Override

??? public String toString(){

??????? return "x =" + x + "? " + "y =" + y;

??? }

}


transient

transient 關(guān)鍵字可以使一些屬性不會被序列化嚎幸。

ArrayList 中存儲數(shù)據(jù)的數(shù)組 elementData 是用 transient 修飾的颜矿,因?yàn)檫@個(gè)數(shù)組是動(dòng)態(tài)擴(kuò)展的,并不是所有的空間都被使用嫉晶,因此就不需要所有的內(nèi)容都被序列化骑疆。通過重寫序列化和反序列化方法,使得可以只序列化數(shù)組中有內(nèi)容的那部分?jǐn)?shù)據(jù)替废。

private transient Object[] elementData;


5)網(wǎng)絡(luò)操作

Java中的網(wǎng)絡(luò)支持:

InetAddress:用于表示網(wǎng)絡(luò)上的硬件資源箍铭,即IP地址;

URL:統(tǒng)一資源定位符舶担;

Sockets:使用TCP協(xié)議實(shí)現(xiàn)網(wǎng)絡(luò)通信坡疼;

Datagram:使用UDP協(xié)議實(shí)現(xiàn)網(wǎng)絡(luò)通信。


InetAddress

沒有公有的構(gòu)造函數(shù)衣陶,只能通過靜態(tài)方法來創(chuàng)建實(shí)例柄瑰。

InetAddress.getByName(String host);

InetAddress.getByAddress(byte[] address);


URL

可以直接從 URL 中讀取字節(jié)流數(shù)據(jù)。

public static void main(String[] args) throws IOException {

??? URL url = newURL("http://www.baidu.com");

? ? /*字節(jié)流*/

??? InputStream is =url.openStream();

? ? /*字符流*/

??? InputStreamReader isr =new InputStreamReader(is, "utf-8");

? ? /*提供緩存功能*/

??? BufferedReader br = newBufferedReader(isr);


??? String line;

??? while ((line =br.readLine()) != null) {

??????? System.out.println(line);

??? }

? ? br.close();

}


Sockets

ServerSocket:服務(wù)器端類

Socket:客戶端類

服務(wù)器和客戶端通過 InputStream 和 OutputStream 進(jìn)行輸入輸出剪况。

Datagram

DatagramSocket:通信類

DatagramPacket:數(shù)據(jù)包類

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末教沾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子译断,更是在濱河造成了極大的恐慌唁奢,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柱衔,死亡現(xiàn)場離奇詭異夺刑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翎蹈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進(jìn)店門淮菠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荤堪,你說我怎么就攤上這事合陵∈嗯猓” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵拥知,是天一觀的道長踏拜。 經(jīng)常有香客問我,道長低剔,這世上最難降的妖魔是什么速梗? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮户侥,結(jié)果婚禮上镀琉,老公的妹妹穿的比我還像新娘。我一直安慰自己蕊唐,他們只是感情好屋摔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著替梨,像睡著了一般钓试。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上副瀑,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天弓熏,我揣著相機(jī)與錄音,去河邊找鬼糠睡。 笑死挽鞠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狈孔。 我是一名探鬼主播信认,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼均抽!你這毒婦竟也來了嫁赏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤油挥,失蹤者是張志新(化名)和其女友劉穎潦蝇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體深寥,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攘乒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惋鹅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片则酝。...
    茶點(diǎn)故事閱讀 38,629評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖负饲,靈堂內(nèi)的尸體忽然破棺而出堤魁,到底是詐尸還是另有隱情,我是刑警寧澤返十,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布妥泉,位于F島的核電站,受9級特大地震影響洞坑,放射性物質(zhì)發(fā)生泄漏盲链。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一迟杂、第九天 我趴在偏房一處隱蔽的房頂上張望刽沾。 院中可真熱鬧,春花似錦排拷、人聲如沸侧漓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽布蔗。三九已至,卻和暖如春浪腐,著一層夾襖步出監(jiān)牢的瞬間纵揍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工议街, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泽谨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓特漩,卻偏偏與公主長得像吧雹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子拾稳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評論 2 348

推薦閱讀更多精彩內(nèi)容