IO流詳解

概述

流是一組有順序的制圈,有起點(diǎn)和終點(diǎn)的字節(jié)集合,是對數(shù)據(jù)傳輸?shù)目偡Q或抽象。即數(shù)據(jù)在兩設(shè)備間的傳輸稱為流,流的本質(zhì)是數(shù)據(jù)傳輸仲翎,根據(jù)數(shù)據(jù)傳輸特性將流抽象為各種類浮入,方便更直觀的進(jìn)行數(shù)據(jù)操作牡拇。IO其實(shí)有兩類帚稠,一類是BIO(BlockingIO),一類是NIO(Non-BlockingIO)卿叽,不過我們通常說的是IO默認(rèn)指的是BIO;

正文

基礎(chǔ)知識

字符

字節(jié)是計(jì)算機(jī)中存儲數(shù)據(jù)的單元桥胞,一個(gè)8位的二進(jìn)制數(shù),是一個(gè)很具體的存儲空間考婴。

字符編碼

字符是指人們使用的記號埠戳,抽象意義上的一個(gè)符號,比如1蕉扮、2整胃、3、·#¥%喳钟。

字節(jié)

字符編碼(Character encoding)是一套法則屁使,使用該法則能夠?qū)ψ匀徽Z言的字符的一個(gè)集合(如字母表或音節(jié)表),與其他東西的一個(gè)集合進(jìn)行配對奔则。各個(gè)國家和地區(qū)所制定的不同 ANSI 編碼標(biāo)準(zhǔn)中蛮寂,都只規(guī)定了各自語言所需的字符。

常見的編碼方式

ASCII編碼:美國制定了一套字符編碼易茬,對英語字符與二進(jìn)制位之間的關(guān)系酬蹋,做了統(tǒng)一規(guī)定。這被稱為 ASCII 碼抽莱,一直沿用至今范抓。

非ASCII 編碼:英語用128個(gè)符號編碼就夠了,但是用來表示其他語言食铐,128個(gè)符號是不夠的匕垫,所以在別的國家編碼符號會比128要多,所以問題就出現(xiàn)了虐呻,不同的國家有不同的字母象泵,因此寞秃,哪怕它們都使用256個(gè)符號的編碼方式,代表的字母卻不一樣偶惠。

UTF-8編碼:UTF-8最大的一個(gè)特點(diǎn)春寿,就是它是一種變長的編碼方式。它可以使用1~4個(gè)字節(jié)表示一個(gè)符號忽孽,根據(jù)不同的符號而變化字節(jié)長度绑改。。UTF-8 就是在互聯(lián)網(wǎng)上使用最廣的一種 Unicode 的實(shí)現(xiàn)方式扒腕。其他實(shí)現(xiàn)方式還包括 UTF-16(字符用兩個(gè)字節(jié)或四個(gè)字節(jié)表示)和 UTF-32(字符用四個(gè)字節(jié)表示),不過在互聯(lián)網(wǎng)上基本不用萤悴。重復(fù)一遍瘾腰,這里的關(guān)系是,UTF-8 是 Unicode 的實(shí)現(xiàn)方式之一覆履。

聯(lián)系與區(qū)別

很多時(shí)候我們經(jīng)常提及到字符跟字節(jié)之間的關(guān)系蹋盆,這個(gè)問題的前提是基于某一種編程語言比如說Java或者C來說的,因?yàn)樽址止?jié)之間的關(guān)系跟字符編碼是有著緊密聯(lián)系的硝全,所以單獨(dú)討論字符跟字節(jié)之間的關(guān)系沒有意義栖雾,下面簡單來看一下他們在不同編碼上的的對應(yīng)關(guān)系:

語言 中文字符 英文字符
GBK 2個(gè)字節(jié) 1個(gè)字節(jié)
UTF-8 2個(gè)字節(jié) 2個(gè)字節(jié)

java語言默認(rèn)是采用Utf-8來進(jìn)行編碼的,下面用Java來測試一下:

測試GBK編碼

   public static void main(String[] args) {
    String str = "Hello_安卓";
    int byte_len = 0;
    try {
        byte_len = str.getBytes("gbk").length;
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    System.out.println("字節(jié)長度:" + byte_len);

}
字節(jié)長度:10

測試UTF-8編碼

public static void main(String[] args) {
    String str = "Hello_安卓";
    int byte_len =str.getBytes().length;
    System.out.println("字節(jié)長度:" + byte_len);

}
字節(jié)長度:12

輸出的結(jié)果伟众,跟之前的規(guī)則是一致的析藕,到這里,字節(jié)凳厢,編碼方式账胧,字符,以及他們之間的聯(lián)系基本上介紹完了先紫,理解了他們之間的關(guān)系治泥,下面的File類跟IO之間的關(guān)系也就比較好理解了。

File類

File類翻譯過來是一個(gè)文件遮精,實(shí)際上它并不是一個(gè)文件居夹,定義為Path更為合適,這個(gè)Path可以是文件的路徑也可以是文件夾的路徑本冲,因?yàn)楫?dāng)我們new File的時(shí)候准脂,只是創(chuàng)建了一個(gè)路徑,這個(gè)路徑如果創(chuàng)建成功檬洞,沒有后綴名就是文件夾意狠,有后綴名則創(chuàng)建了一個(gè)空文件。下面看一下File類的繼承關(guān)系:

File的繼承關(guān)系
構(gòu)造函數(shù)

列舉幾個(gè)常見的構(gòu)造函數(shù)

File(String pathname)
File(String parent,String child)
File(File parent,String child)

因?yàn)镴ava命名比較規(guī)范疮胖,所以很好理解环戈,有一點(diǎn)需要注意的是闷板,這個(gè)方法不能保證文件一定會創(chuàng)建成功,但是即使失敗也不會報(bào)異常院塞,所以一般我們在文件創(chuàng)建之后需要判斷一下當(dāng)前文件是否創(chuàng)建成功遮晚,調(diào)用一下 exists()方法來判斷文件是否創(chuàng)建成功,不成功則調(diào)用createNewFile(),此方法失敗會拋異常拦止,歸納起來就是:

    File file=new File("E:\\demo","a.txt");
    if (file.exists()){
        //繼續(xù)文件的操作
    }else {
        try {
            boolean result = file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

路徑

  • 相對路徑:./表示當(dāng)前路徑../表示上一級路徑
  • 絕對路徑:絕對路徑名是完整的路徑名县遣,不需要任何其他信息就可以定位自身表示的文件
路徑分隔符:
  • windows: "/" "" 都可以
  • linux/unix: "/"

注意:如果windows選擇用""做分割符的話,那么請記得替換成"\",因?yàn)镴ava中""代表轉(zhuǎn)義字符所以推薦都使用"/",也可以直接使用代碼File.separator汹族,表示跨平臺分隔符萧求。

創(chuàng)建與刪除
boolean createNewFile();//創(chuàng)建具體的文件
boolean mkdir();//創(chuàng)建單個(gè)目錄
boolean mkdirs();//創(chuàng)建多個(gè)目錄
boolean delete();//刪除File
判斷方法
boolean canRead();//判斷文件是否可讀
boolean canWrite();//判斷文件是否可寫
boolean exists();//判斷文件是否存在
boolean isDirectory();//判斷是否是目錄
boolean isFile();//判斷是否是文件
boolean isAbsolute();//判斷是否是絕對路徑
獲取方法
String getName();//返回文件或者是目錄的名稱
String getPath();//返回路徑
String getAbsolutePath();//返回絕對路徑
String getParent();//返回父目錄,如果沒有父目錄則返回null
long lastModified();//返回最后一次修改的時(shí)間
long length();//返回文件的長度
File[] listRoots();// 列出所有的根目錄(Window中就是所有系統(tǒng)的盤符)
String[] list() ;//返回一個(gè)字符串?dāng)?shù)組顶瞒,給定路徑下的文件或目錄名稱字符串
String[] list(FilenameFilter filter);//返回滿足過濾器要求的一個(gè)字符串?dāng)?shù)組
File[]  listFiles();//返回一個(gè)文件對象數(shù)組夸政,給定路徑下文件或目錄

文件過濾

File[] listFiles(FilenameFilter filter);//返回滿足過濾器要求的一個(gè)文件對象數(shù)組
其中包含了一個(gè)重要的接口FileNameFilter,該接口是個(gè)文件過濾器榴徐,包含了一個(gè)accept(File dir,String name)方法守问,該方法依次對指定File的所有子目錄或者文件進(jìn)行迭代,按照指定條件坑资,進(jìn)行過濾耗帕,過濾出滿足條件的所有文件。


 // 文件過濾
 File[] files = file.listFiles(new FilenameFilter() {
    @Override
  public boolean accept(File file, String filename) {
           return filename.endsWith(".apk");
            }
        });

file目錄下的所有子文件如果滿足后綴是.apk的條件的文件都會被過濾出來袱贮。

分類

按數(shù)據(jù)類型分

IO流分類

  • 字節(jié)流:字節(jié)流主要是操作byte類型數(shù)據(jù)
  • 字符流: 因?yàn)閿?shù)據(jù)編碼的不同仿便,而有了對字符進(jìn)行高效操作的流對象。本質(zhì)其實(shí)就是基于字節(jié)流讀取時(shí)攒巍,去查了指定的碼表探越。

區(qū)別

  • 讀寫單位不同:字節(jié)流以字節(jié)(8bit)為單位,字符流以字符為單位窑业,根據(jù)碼表映射字符钦幔,一次可能讀16位字節(jié)。
  • 處理對象不同:字節(jié)流能處理所有類型的數(shù)據(jù)(如image常柄、avi等)鲤氢,而字符流只能處理字符類型的數(shù)據(jù)。

設(shè)備上的數(shù)據(jù)無論是圖片或者視頻西潘,文字卷玉,它們都以二進(jìn)制存儲的。二進(jìn)制的最終都是以一個(gè)8位為數(shù)據(jù)單元進(jìn)行體現(xiàn)喷市,所以計(jì)算機(jī)中的最小數(shù)據(jù)單元就是字節(jié)相种。意味著,字節(jié)流可以處理設(shè)備上的所有數(shù)據(jù)品姓,所以字節(jié)流一樣可以處理字符數(shù)據(jù)寝并。

結(jié)論:只要是處理純文本數(shù)據(jù)箫措,就優(yōu)先考慮使用字符流。換句話說衬潦,能使用字符流的一定也可以使用字節(jié)流斤蔓。

按照數(shù)據(jù)流向分

IO流按照流向分

  • 輸入流:InputStream或者Reader:從文件中讀到程序中;
  • 輸出流:OutputStream或者Writer:從程序中輸出到文件中镀岛;

這里的輸入和輸出都是以程序?yàn)閰⒄瘴?/p>

按照流的角色分

IO流按照角色分

  • 節(jié)點(diǎn)流:直接與數(shù)據(jù)源相連弦牡,讀入或讀出,可以從/向一個(gè)特定的IO設(shè)備(如磁盤漂羊、網(wǎng)絡(luò))讀/寫數(shù)據(jù)的流驾锰,稱為節(jié)點(diǎn)流,節(jié)點(diǎn)流也被成為低級流走越。
  • 處理流:處理流是對一個(gè)已存在的流進(jìn)行連接或封裝椭豫,通過封裝后的流來實(shí)現(xiàn)數(shù)據(jù)讀/寫功能,處理流也被稱為高級流买喧。

當(dāng)使用處理流進(jìn)行輸入/輸出時(shí)捻悯,程序并不會直接連接到實(shí)際的數(shù)據(jù)源匆赃,沒有和實(shí)際的輸入/輸出節(jié)點(diǎn)連接淤毛。使用處理流的一個(gè)明顯好處是,只要使用相同的處理流算柳,程序就可以采用完全相同的輸入/輸出代碼來訪問不同的數(shù)據(jù)源低淡,隨著處理流所包裝節(jié)點(diǎn)流的變化,程序?qū)嶋H所訪問的數(shù)據(jù)源也相應(yīng)地發(fā)生變化瞬项。

常用的節(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)流ByteArrayOutputStream兵扬、FileOutputStream 是兩種基本的介質(zhì)流麻裳,它們分別向Byte 數(shù)組口蝠、和本地文件中寫入數(shù)據(jù)。
PipedOutputStream 是向與其它線程共用的管道中寫入數(shù)據(jù)掂器。
ObjectOutputStream和所有FilterOutputStream 的子類都是裝飾流亚皂。

常用的處理流
緩沖流:BufferedInputStrean 、BufferedOutputStream国瓮、 BufferedReader灭必、 BufferedWriter 增加緩沖功能,避免頻繁讀寫硬盤乃摹。
轉(zhuǎn)換流:InputStreamReader 禁漓、OutputStreamReader實(shí)現(xiàn)字節(jié)流和字符流之間的轉(zhuǎn)換。
數(shù)據(jù)流: DataInputStream 孵睬、DataOutputStream 等-提供將基礎(chǔ)數(shù)據(jù)類型寫入到文件中播歼,或者讀取出來。

轉(zhuǎn)換流:InputStreamReader 掰读、OutputStreamWriter 要InputStream或OutputStream作為參數(shù)秘狞,實(shí)現(xiàn)從字節(jié)流到字符流的轉(zhuǎn)換。

父類介紹

InputStream

InputStream繼承關(guān)系

InputStream 是所有的輸入字節(jié)流的父類蹈集,它是一個(gè)抽象類烁试,主要包含三個(gè)方法:

//讀取一個(gè)字節(jié)并以整數(shù)的形式返回(0~255),如果返回-1已到輸入流的末尾。 
int read() 拢肆; 
//讀取一系列字節(jié)并存儲到一個(gè)數(shù)組buffer减响,返回實(shí)際讀取的字節(jié)數(shù),如果讀取前已到輸入流的末尾返回-1郭怪。 
int read(byte[] buffer) 支示; 
//讀取length個(gè)字節(jié)并存儲到一個(gè)字節(jié)數(shù)組buffer,從off位置開始存,最多l(xiāng)en鄙才, 返回實(shí)際讀取的字節(jié)數(shù)颂鸿,如果讀取前以到輸入流的末尾返回-1。 
int read(byte[] buffer, int off, int len) 攒庵;

ByteArrayInputStream嘴纺、StringBufferInputStream、FileInputStream 是三種基本的介質(zhì)流叙甸,它們分別從Byte 數(shù)組颖医、StringBuffer、和本地文件中讀取數(shù)據(jù)裆蒸。
PipedInputStream 是從與其它線程共用的管道中讀取數(shù)據(jù)熔萧,與Piped 相關(guān)的知識后續(xù)單獨(dú)介紹。
ObjectInputStream 和所有FilterInputStream 的子類都是裝飾流(裝飾器模式的主角)

Reader

Reader繼承關(guān)系

Reader 是所有的輸入字符流的父類,它是一個(gè)抽象類佛致,主要包含三個(gè)方法:

//讀取一個(gè)字符并以整數(shù)的形式返回(0~255),如果返回-1已到輸入流的末尾贮缕。 
int read() ; 
//讀取一系列字符并存儲到一個(gè)數(shù)組buffer俺榆,返回實(shí)際讀取的字符數(shù)感昼,如果讀取前已到輸入流的末尾返回-1。 
int read(char[] cbuf) 罐脊; 
//讀取length個(gè)字符,并存儲到一個(gè)數(shù)組buffer定嗓,從off位置開始存,最多讀取len,返回實(shí)際讀取的字符數(shù)萍桌,如果讀取前以到輸入流的末尾返回-1宵溅。 
int read(char[] cbuf, int off, int len)

對比InputStream和Reader所提供的方法,就不難發(fā)現(xiàn)兩個(gè)基類的功能基本一樣的上炎,只不過讀取的數(shù)據(jù)單元不同恃逻。

在執(zhí)行完流操作后,要調(diào)用close()方法來關(guān)系輸入流藕施,因?yàn)槌绦蚶锎蜷_的IO資源不屬于內(nèi)存資源寇损,垃圾回收機(jī)制無法回收該資源,所以應(yīng)該顯式關(guān)閉文件IO資源裳食。

除此之外矛市,InputStream和Reader還支持如下方法來移動流中的指針位置:

//在此輸入流中標(biāo)記當(dāng)前的位置
//readlimit - 在標(biāo)記位置失效前可以讀取字節(jié)的最大限制。
void mark(int readlimit)
// 測試此輸入流是否支持 mark 方法
boolean markSupported()
// 跳過和丟棄此輸入流中數(shù)據(jù)的 n 個(gè)字節(jié)/字符
long skip(long n)
//將此流重新定位到最后一次對此輸入流調(diào)用 mark 方法時(shí)的位置
void reset()

OutputStream

OutputStream繼承關(guān)系

OutputStream 是所有的輸出字節(jié)流的父類胞谈,它是一個(gè)抽象類尘盼,主要包含如下四個(gè)方法:

//向輸出流中寫入一個(gè)字節(jié)數(shù)據(jù),該字節(jié)數(shù)據(jù)為參數(shù)b的低8位憨愉。 
void write(int b) ; 
//將一個(gè)字節(jié)類型的數(shù)組中的數(shù)據(jù)寫入輸出流烦绳。 
void write(byte[] b); 
//將一個(gè)字節(jié)類型的數(shù)組中的從指定位置(off)開始的,len個(gè)字節(jié)寫入到輸出流。 
void write(byte[] b, int off, int len); 
//將輸出流中緩沖的數(shù)據(jù)全部寫出到目的地配紫。 
void flush();

Writer

Writer繼承關(guān)系

Writer 是所有的輸出字符流的父類径密,它是一個(gè)抽象類,主要包含如下六個(gè)方法:

//向輸出流中寫入一個(gè)字符數(shù)據(jù),該字節(jié)數(shù)據(jù)為參數(shù)b的低16位。 
void write(int c); 
//將一個(gè)字符類型的數(shù)組中的數(shù)據(jù)寫入輸出流躺孝, 
void write(char[] cbuf) 
//將一個(gè)字符類型的數(shù)組中的從指定位置(offset)開始的,length個(gè)字符寫入到輸出流享扔。 
void write(char[] cbuf, int offset, int length); 
//將一個(gè)字符串中的字符寫入到輸出流。 
void write(String string); 
//將一個(gè)字符串從offset開始的length個(gè)字符寫入到輸出流植袍。 
void write(String string, int offset, int length); 
//將輸出流中緩沖的數(shù)據(jù)全部寫出到目的地惧眠。 
void flush()

可以看出,Writer比OutputStream多出兩個(gè)方法于个,主要是支持寫入字符和字符串類型的數(shù)據(jù)氛魁。

使用Java的IO流執(zhí)行輸出時(shí),不要忘記關(guān)閉輸出流,關(guān)閉輸出流除了可以保證流的物理資源被回收之外秀存,還能將輸出流緩沖區(qū)的數(shù)據(jù)flush到物理節(jié)點(diǎn)里(因?yàn)樵趫?zhí)行close()方法之前捶码,自動執(zhí)行輸出流的flush()方法)

IO中的一股清流——RandomAccessFIle

RandomAccessFIle繼承關(guān)系

我們發(fā)現(xiàn)RandomAccessFIle跟File類并沒有聯(lián)系,只是實(shí)現(xiàn)了DataOutput跟DataInput兩個(gè)接口或链,完全自己重新定義了一遍File的讀取操作

RandomAccessFile是Java中輸入惫恼,輸出流體系中功能最豐富的文件內(nèi)容訪問類,它提供很多方法來操作文件澳盐,包括讀寫支持祈纯,與普通的IO流相比,它最大的特別之處就是支持任意訪問的方式叼耙,程序可以直接跳到任意地方來讀寫數(shù)據(jù)盆繁。

如果我們只希望訪問文件的部分內(nèi)容,而不是把文件從頭讀到尾旬蟋,使用RandomAccessFile將會帶來更簡潔的代碼以及更好的性能油昂。

方法
方法名 作用
getFilePointer() 返回文件記錄指針的當(dāng)前位置
seek(long pos) 將文件記錄指針定位到pos的位置
功能
  • 1.讀取任意位置的數(shù)據(jù)
  • 2.追加數(shù)據(jù)
  • 3.任意位置插入數(shù)據(jù)

NIO

Java NIO是java 1.4之后新出的一套IO接口,這里的的新是相對于原有標(biāo)準(zhǔn)的Java IO和Java Networking接口倾贰。NIO提供了一種完全不同的操作方式冕碟。標(biāo)準(zhǔn)的IO編程接口是面向字節(jié)流字符流的。而NIO是面向Channel(通道)和Buffer(緩沖區(qū))的匆浙,數(shù)據(jù)總是從Channel中讀到Buffer內(nèi)安寺,或者從Buffer寫入到Channel中,Channel是需要注冊到Selector(選擇器)中去首尼。我們知道不管是NIO還是BIO在讀寫數(shù)據(jù)的過程中都有兩個(gè)操作:等待就緒和操作挑庶。舉例來說,讀函數(shù)软能,分為等待系統(tǒng)可讀和真正的讀迎捺;同理,寫函數(shù)分為等待網(wǎng)卡可以寫和真正的寫查排。NIO跟BIO的操作都是一樣的凳枝,區(qū)別在于等待就緒的這一過程,看看下面這張圖:

阻塞式IO

非阻塞式IO

NIO跟BIO的區(qū)別在于不管現(xiàn)在有沒有數(shù)據(jù)跋核,都會給調(diào)用者一個(gè)返回值岖瑰,在沒有準(zhǔn)備就緒之前,當(dāng)前線程可以進(jìn)行其他的操作砂代,而不會阻塞蹋订。

緩沖區(qū)

Buffer的實(shí)現(xiàn)關(guān)系

緩沖區(qū)實(shí)質(zhì)上就是一個(gè)數(shù)組,但它不僅僅是一個(gè)數(shù)組刻伊,緩沖區(qū)還提供了對數(shù)據(jù)的結(jié)構(gòu)化訪問露戒,而且還可以跟蹤系統(tǒng)的讀/寫進(jìn)程难礼。

通道

Channel的實(shí)現(xiàn)關(guān)系

通道用于在緩沖區(qū)和位于通道另一側(cè)的實(shí)體(文件、套接字)之間有效的傳輸數(shù)據(jù)

選擇器

Selector的實(shí)現(xiàn)關(guān)系

選擇器類Selector并沒有和通道有直接的關(guān)系玫锋,而是通過叫選擇鍵的對象SelectionKey來聯(lián)系的,而且Selector可以注冊過個(gè)key蛾茉,也就是說可以同時(shí)管理多個(gè)通道。選擇鍵代表了通道與選擇 器之間的一種注冊關(guān)系撩鹿,channel()和selector()方法分別返回注冊的通道與選擇器谦炬。

工作原理

NIO調(diào)用不會被阻塞,在IO開始的時(shí)候需要在分發(fā)器那里注冊感興趣的事件节沦,并提供相應(yīng)的處理者(event handler)键思,或者是回調(diào)函數(shù);事件分發(fā)器在適當(dāng)?shù)臅r(shí)候甫贯,會將請求的事件分發(fā)給這些handler或者回調(diào)函數(shù):如可讀數(shù)據(jù)到達(dá)吼鳞,新的套接字連接等等,在發(fā)生特定事件時(shí)叫搁,系統(tǒng)再通知我們赔桌。NIO中實(shí)現(xiàn)非阻塞I/O的核心對象就是Selector,Selector就是注冊各種I/O事件地 方渴逻,而且當(dāng)那些事件發(fā)生時(shí)疾党,就是這個(gè)對象告訴我們所發(fā)生的事件,如下圖所示:


NIO工作原理

從圖中可以看出惨奕,當(dāng)有讀或?qū)懙热魏巫缘氖录l(fā)生時(shí)雪位,可以從Selector中獲得相應(yīng)的SelectionKey,同時(shí)從 SelectionKey中可以找到發(fā)生的事件和該事件所發(fā)生的具體的SelectableChannel梨撞,以獲得客戶端發(fā)送過來的數(shù)據(jù)雹洗。

如何選擇

NIO的優(yōu)勢在于單線程管理多個(gè)連接,可以在線程數(shù)較少的情況下實(shí)現(xiàn)快速地讀取卧波,當(dāng)連接數(shù)<1000时肿,并發(fā)程度不高并沒有顯著的性能優(yōu)勢。NIO可讓您只使用一個(gè)(或幾個(gè))單線程管理多個(gè)通道(網(wǎng)絡(luò)連接或文件)幽勒,但付出的代價(jià)是解析數(shù)據(jù)可能會比從一個(gè)阻塞流中讀取數(shù)據(jù)更復(fù)雜嗜侮。如果需要管理同時(shí)打開的成千上萬個(gè)連接港令,這些連接每次只是發(fā)送少量的數(shù)據(jù)啥容,例如聊天服務(wù)器,實(shí)現(xiàn)NIO的服務(wù)器可能是一個(gè)優(yōu)勢顷霹。如果你有少量的連接使用非常高的帶寬咪惠,一次發(fā)送大量的數(shù)據(jù),也許典型的BIO服務(wù)器實(shí)現(xiàn)可能非常契合淋淀。

IO與裝飾者模式

其實(shí)裝飾者模式在IO中的運(yùn)用非常廣泛遥昧,先看一下什么是裝飾者。
裝飾者(Decorator)模式:動態(tài)將職責(zé)附加到對象上,若要擴(kuò)展功能炭臭,裝飾者提供了比繼承更具彈性的代替方案永脓。
設(shè)計(jì)原則:開閉原則(一個(gè)軟件實(shí)體如類、模塊和函數(shù)應(yīng)該對擴(kuò)展開放鞋仍,對修改關(guān)閉)常摧。
下面用圖簡單描述一下:

裝飾者模式圖解

下面解釋一下這幾個(gè)變量:

  • Component:抽象組件
  • ConcreteComponent:抽象組件的具體實(shí)現(xiàn)類
  • Decorator: 裝飾者的抽象類,繼承自Component
  • DecoratorA/B:具體的裝飾實(shí)現(xiàn)類

通過裝飾者對已有的對象進(jìn)行包裝威创,可以擴(kuò)展已有類的方法跟屬性落午,下面通過代碼來說明:

Component

public abstract class Component {
    protected String description;

    protected abstract String getDescription();

    public abstract int getAge();

}

ConcreteComponent

public class ConcreteComponent extends Component {
    public ConcreteComponent() {
        description = "ConcreteComponent";
    }

    @Override
    protected String getDescription() {
        return description;
    }

    @Override
    public int getAge() {
        return 10;
    }
}

Decorator

public abstract class Decorator extends Component {
//空實(shí)現(xiàn),具體的實(shí)現(xiàn)放在子類
}

DecoratorA/B

public class DecoratorA extends Decorator {
    private Component mComponent;

    public DecoratorA(Component component) {
        this.mComponent = component;
    }

    @Override
    protected String getDescription() {
        return mComponent.getDescription();
    }

    @Override
    public int getAge() {
        return mComponent.getAge() + 1;
    }

    public String getTime() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }

}

測試代碼:

Component component = new ConcreteComponent();
        System.out.println("裝飾前的參數(shù):" + "描述:" + component.getDescription() + "   年齡:" + component.getAge());
        DecoratorA decoratorA = new DecoratorA(component);
        System.out.println("裝飾后的參數(shù):" + "描述:" + decoratorA.getDescription() + "   年齡

使用裝飾者模式之后肚豺,不修改Description溃斋,將age+1,同時(shí)增加一個(gè)getTime方法來獲取當(dāng)前的時(shí)間

輸出結(jié)果

裝飾前的參數(shù):描述:ConcreteComponent   年齡:10
裝飾后的參數(shù):描述:ConcreteComponent   年齡:11 時(shí)間:2017-11-04 15:11:03

跟我們預(yù)期的一樣吸申,不再贅述梗劫。下面看看IO中的設(shè)計(jì)模式,以O(shè)utpuStream為例:


OutputStream中的裝飾者模式

對照著上面的裝飾者模式圖應(yīng)該很容易看出來截碴,F(xiàn)ileterOutputStream就是我們裝飾者的抽象類在跳,看一下他的構(gòu)造方法,確實(shí)裝飾了OutputStream :

  public FilterOutputStream(OutputStream out) {
        this.out = out;
    }

Android中的"path"

在開發(fā)Android的過程中,也會涉及到很多的IO操作隐岛,比如說網(wǎng)絡(luò)請求猫妙,下載圖片等,由于很多框架平時(shí)已經(jīng)幫我們封裝好了聚凹,所以平時(shí)容易忽略割坠,下面簡單分析一下Android下的存儲目錄:


Android平臺的存儲目錄

內(nèi)部存儲

data文件夾就是我們常說的內(nèi)部存儲,對于沒有root的手機(jī)來說妒牙,我們是沒有權(quán)限打開這個(gè)文件夾的但是可以訪問到彼哼,

外部存儲

外部存儲才是我們平時(shí)操作最多的,外部存儲一般就是我們上面看到的storage文件夾湘今,當(dāng)然也有可能是mnt文件夾敢朱,這個(gè)名稱不影響我們操作數(shù)據(jù)。

路徑獲取

兩種存儲方式都是通過Context類來進(jìn)行獲取的

內(nèi)部存儲

   getFilesDir();//獲取內(nèi)部存儲的File路徑
   getCacheDir();//獲取內(nèi)部存儲的Cache路徑
   getDatabasePath("demo.db");//獲取database路徑
   getSharedPreferences("demo",MODE_PRIVATE);//獲取SP

外部存儲

   getExternalCacheDir();//獲取外部存儲私有目錄
   getExternalFilesDir(Environment.DIRECTORY_DCIM);//獲取外部存儲公有目錄
      

清除緩存/清除數(shù)據(jù)

清除緩存:緩存是程序運(yùn)行時(shí)的臨時(shí)存儲空間摩瞎,它可以存放從網(wǎng)絡(luò)下載的臨時(shí)圖片拴签,從用戶的角度出發(fā)清除緩存對用戶并沒有太大的影響,但是清除緩存后用戶再次使用該APP時(shí)旗们,由于本地緩存已經(jīng)被清理蚓哩,所有的數(shù)據(jù)需要重新從網(wǎng)絡(luò)上獲取,注意:為了在清除緩存的時(shí)候能夠正常清除與應(yīng)用相關(guān)的緩存上渴,請將緩存文件存放在getCacheDir()或者 getExternalCacheDir()路徑下岸梨。
清除數(shù)據(jù):清除用戶配置喜颁,比如SharedPreferences、數(shù)據(jù)庫等等曹阔,這些數(shù)據(jù)都是在程序運(yùn)行過程中保存的用戶配置信息半开,清除數(shù)據(jù)后,下次進(jìn)入程序就和第一次進(jìn)入程序時(shí)一樣

關(guān)于權(quán)限

Android6.0以后赃份,谷歌加強(qiáng)了對用戶權(quán)限的控制稿茉,但是這個(gè)權(quán)限只是針對于外部存儲的公有目錄,對于私有目錄的芥炭,仍然可以正常訪問漓库。所以當(dāng)遇到有些手機(jī)權(quán)限很難適配的時(shí)候可以把文件存儲在外部存儲的私有目錄。

參考資料

https://tech.meituan.com/nio.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末园蝠,一起剝皮案震驚了整個(gè)濱河市渺蒿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彪薛,老刑警劉巖茂装,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異善延,居然都是意外死亡少态,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門易遣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彼妻,“玉大人,你說我怎么就攤上這事豆茫∏惹福” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵揩魂,是天一觀的道長幽邓。 經(jīng)常有香客問我,道長火脉,這世上最難降的妖魔是什么牵舵? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮倦挂,結(jié)果婚禮上畸颅,老公的妹妹穿的比我還像新娘。我一直安慰自己妒峦,他們只是感情好重斑,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肯骇,像睡著了一般窥浪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上笛丙,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天漾脂,我揣著相機(jī)與錄音,去河邊找鬼胚鸯。 笑死骨稿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姜钳。 我是一名探鬼主播坦冠,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哥桥!你這毒婦竟也來了辙浑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤拟糕,失蹤者是張志新(化名)和其女友劉穎判呕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體送滞,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侠草,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了犁嗅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赘娄。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖韭山,靈堂內(nèi)的尸體忽然破棺而出橡淑,到底是詐尸還是另有隱情,我是刑警寧澤蕊梧,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布霞赫,位于F島的核電站,受9級特大地震影響肥矢,放射性物質(zhì)發(fā)生泄漏端衰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一甘改、第九天 我趴在偏房一處隱蔽的房頂上張望旅东。 院中可真熱鬧,春花似錦十艾、人聲如沸抵代。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荤牍。三九已至案腺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間康吵,已是汗流浹背劈榨。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晦嵌,地道東北人同辣。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像惭载,于是被迫代替她去往敵國和親旱函。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

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