详细领略Java的输入流和输出流
狼北前言
說到j(luò)ava的輸入流和輸出流,IO流分為兩種的(字節(jié)流和字符流),那么我們首先就得了解什么是java的字節(jié)流和字符流,然后我才來了解怎么操作這些流來寫入文件中和寫出文件。所以我這篇文章分為兩大部分,第一部分是“IO流”,第二部分是“操作io流”。廢話不多說讓猿猴帶你一起來領(lǐng)略這里面的一二。(對(duì)博主感興趣可以加博主的抖音號(hào)langbei57048)
Java的IO流
什么是流
Java中的流是對(duì)字節(jié)序列的抽象,我們可以想象有一個(gè)水管,只不過現(xiàn)在流動(dòng)在水管中的不再是水,而是字節(jié)序列。和水流一樣,Java中的流也具有一個(gè)“流動(dòng)的方向”,通常可以從中讀入一個(gè)字節(jié)序列的對(duì)象被稱為輸入流;能夠向其寫入一個(gè)字節(jié)序列的對(duì)象被稱為輸出流。
字節(jié)流
Java中的字節(jié)流處理的最基本單位為單個(gè)字節(jié)(字節(jié)規(guī)定一個(gè)字節(jié)由八個(gè)二進(jìn)制位構(gòu)成)它通常用來處理二進(jìn)制數(shù)據(jù)。Java中最基本的兩個(gè)字節(jié)流類是InputStream和OutputStream,它們分別代表了最基本的輸入字節(jié)流和輸出字節(jié)流。
字符流
Java中的字符流處理的最基本的單元是Unicode碼元(大小2字節(jié)),它通常用來處理文本數(shù)據(jù)。所謂Unicode碼元,也就是一個(gè)Unicode代碼單元,范圍是0x0000~0xFFFF。在以上范圍內(nèi)的每個(gè)數(shù)字都與一個(gè)字符相對(duì)應(yīng),Java中的String類型默認(rèn)就把字符以Unicode規(guī)則編碼而后存儲(chǔ)在內(nèi)存中。然而與存儲(chǔ)在內(nèi)存中不同,存儲(chǔ)在磁盤上的數(shù)據(jù)通常有著各種各樣的編碼方式。使用不同的編碼方式,相同的字符會(huì)有不同的二進(jìn)制表示。實(shí)際上字符流是這樣工作的:
輸出字符流:把要寫入文件的字符序列(實(shí)際上是Unicode碼元序列)轉(zhuǎn)為指定編碼方式下的字節(jié)序列,然后再寫入到文件中;
輸入字符流:把要讀取的字節(jié)序列按指定編碼方式解碼為相應(yīng)字符序列(實(shí)際上是Unicode碼元序列)從而可以存在內(nèi)存中。
字符流與字節(jié)流的區(qū)別
經(jīng)過以上的描述,我們可以知道字節(jié)流與字符流之間主要的區(qū)別體現(xiàn)在以下幾個(gè)方面:
字節(jié)流操作(讀寫)的基本單元為字節(jié);字符流操作的基本單元為Unicode碼元。
字節(jié)流默認(rèn)不使用緩沖區(qū);字符流使用緩沖區(qū)。
字節(jié)流通常用于處理二進(jìn)制數(shù)據(jù),實(shí)際上它可以處理任意類型的數(shù)據(jù),但它不支持直接寫入或讀取Unicode碼元;字符流通常處理文本數(shù)據(jù),它支持寫入及讀取Unicode碼元。
只是讀寫文件,和文件內(nèi)容無關(guān)的,一般選擇字節(jié)流。
字節(jié)流使用場(chǎng)景
字節(jié)流適合所有類型文件的數(shù)據(jù)傳輸,因?yàn)橛?jì)算機(jī)字節(jié)(Byte)是電腦中表示信息含義的最小單位,因?yàn)樵谕ǔG闆r下一個(gè)ACSII碼就是一個(gè)字節(jié)的空間來存放。
字符流使用場(chǎng)景
字符流只能夠處理純文本數(shù)據(jù)(文本文件),其他類型數(shù)據(jù)不行,但是字符流處理文本要比字節(jié)流處理文本要方便。
字符流按字符讀數(shù)據(jù):一次讀兩個(gè)字節(jié),返回了這兩個(gè)字節(jié)所對(duì)應(yīng)的字符的int型數(shù)值(編碼)。寫入文件時(shí)把這兩個(gè)字節(jié)的內(nèi)容解碼成這個(gè)字符在Unicode碼下對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)寫入。即把原始文件中的二進(jìn)制數(shù)據(jù)以字符形式讀出,再將字符以二進(jìn)制形式寫入,所以得到的文件以字符方式存儲(chǔ)。而圖片的數(shù)據(jù)是按字節(jié)存儲(chǔ)的,所以打開圖片時(shí)解碼出錯(cuò)了!字節(jié)流按字節(jié)讀數(shù)據(jù):而字節(jié)不需要編碼、解碼,只有字節(jié)與字符之間轉(zhuǎn)換時(shí)才需要編碼、解碼!所以可以正常讀取圖片數(shù)據(jù)。
操作IO流
我們?cè)谶M(jìn)行Android java 開發(fā)的時(shí)候,經(jīng)常會(huì)遇到各種IO流操作。IO流操作一般分為兩類:字符流和字節(jié)流。以“Reader”結(jié)尾都是字符流,操作的都是字符型的數(shù)據(jù);以“Stream”結(jié)尾的都是字節(jié)流,操作的都是byte數(shù)據(jù)。現(xiàn)將各種常見IO流總結(jié)如下:
一、字節(jié)流
1.InputStream 和 OutputStream
InputStream 和 OutputStream為各種輸入輸出字節(jié)流的基類,所有字節(jié)流都繼承這兩個(gè)基類。
2.FileInputStream 和 FileOutputStream
這兩個(gè)從字面意思很容易理解,是對(duì)文件的字節(jié)流操作,也會(huì)最常見的IO操作流。
都是有兩個(gè)構(gòu)造函數(shù):
輸入流 FileinputStream(File file或者String filePath)
輸出流FileoutputStream(File file或者String filePath)
方法:
①read() :從輸入流中一次讀取一個(gè)字節(jié),讀完一個(gè)字節(jié)自動(dòng)讀取數(shù)據(jù)的下一個(gè)字節(jié),返回0到255范圍內(nèi)的int字節(jié)值。如果因?yàn)橐呀?jīng)到達(dá)流末尾而沒有可用的字節(jié),則返回*-1*。在輸入數(shù)據(jù)可用、檢測(cè)到流末尾或者拋出異常前,此方法一直阻塞。
理解read()讀取的字節(jié)為什么返回int型變量
1、方法解釋中的-1相當(dāng)于是數(shù)據(jù)字典告訴調(diào)用者文件已到底,可以結(jié)束讀取了,這里的-1是Int型 那么當(dāng)文件未到底時(shí),我們讀取的是字節(jié),若返回byte類型,那么勢(shì)必造成同一方法返回類型不同的情況這是不允許的2、我們讀取的字節(jié)實(shí)際是由8位二進(jìn)制組成,二進(jìn)制文件不利于直觀查看,可以轉(zhuǎn)成常用的十進(jìn)制進(jìn)行展示,因此需要把讀取的字節(jié)從二進(jìn)制轉(zhuǎn)成十進(jìn)制整數(shù),故返回int型3、 因此結(jié)合以上3點(diǎn),保證返回類型一致以及直觀查看的情況,因此該方法雖然讀取的是字節(jié)但返回int型②read(byte[] b) :從輸入流中讀取一定數(shù)量的字節(jié),并將其讀取存儲(chǔ)在緩沖區(qū)數(shù)組 b 中。以整數(shù)形式返回實(shí)際讀取的字節(jié)數(shù)。在輸入數(shù)據(jù)可用、檢測(cè)到文件末尾或者拋出異常前,此方法一直阻塞。如果因?yàn)榱魑挥谖募┪捕鴽]有可用的字節(jié),則返回值 -1;每次讀取的字節(jié)數(shù)是緩沖區(qū)數(shù)組的最大儲(chǔ)存字節(jié)數(shù)(new byte[2] 數(shù)組最多儲(chǔ)存2個(gè)字節(jié),那么read(byte)每一次只能讀到2字節(jié),并且數(shù)組是滿的狀態(tài)),下一次調(diào)用讀取的字節(jié)存入這個(gè)數(shù)組會(huì)替代掉上一次數(shù)組的內(nèi)容(返回的是讀了多少個(gè)字節(jié)而讀到的字節(jié)內(nèi)容儲(chǔ)存到了b緩沖區(qū)中)
eg:我們文本文件儲(chǔ)存的是123456
//注意下面我是簡(jiǎn)單寫法,拋出異常那些都沒寫File file=new File("D://text.txt");FileInputStream fileInputStream=new FileInputStream(file); //獲取文件輸入流byte[] b=new byte[2]; //new一個(gè)字節(jié)數(shù)數(shù)據(jù)來當(dāng)緩沖區(qū),數(shù)組大小為2個(gè)字節(jié) int i=0;while (n!=-1) //當(dāng)n不等于-1,則代表沒到末尾{ n=fileInputStream.read(b);//讀取到緩存區(qū)中返回每一次實(shí)際讀取到字節(jié)數(shù)組中的字節(jié)數(shù) System.out.println(n); //在文件內(nèi)容字節(jié)足夠時(shí)讀到的就是2個(gè)字節(jié),但是結(jié)尾不夠2 個(gè)字節(jié)了,那么讀到的就是剩下的字節(jié)數(shù)System.out.println(Arrays.toString(b)); //讀取后的字節(jié)數(shù)組內(nèi)容 i++;System.out.println("執(zhí)行次數(shù):"+i); }System.out.println(new String(b));}? 結(jié)果是:read(b)因?yàn)槲覀冊(cè)O(shè)置b數(shù)組大小是2個(gè)字節(jié),所以每次讀出都是倆2字節(jié),但最后不夠2個(gè)字節(jié)(只剩下一個(gè)字節(jié)),所以read(b)返回讀到的字節(jié)數(shù)是1,但是緩存區(qū)的數(shù)組是【最后一個(gè)字節(jié),前一次留下來的】,因?yàn)橹皇R粋€(gè)字節(jié)只能替代b數(shù)據(jù)的第一項(xiàng)
③read(byte[] bytes,int off ,int len) 參數(shù)的byte照樣是緩存區(qū)數(shù)組,off是讀取字節(jié)的開始位置,len是每次讀取的的字節(jié)數(shù)
那么這個(gè)和上面read(b)的區(qū)別就是他指定了每一次讀出多少個(gè)字節(jié),而不再用每一次讀滿一個(gè)緩存數(shù)組的形式,并且這一個(gè)可以指定讀取字節(jié)開始的位置。返回的也是實(shí)際每次讀取到的字節(jié)數(shù)而不是字節(jié),字節(jié)都儲(chǔ)存在緩存數(shù)組里。(比如,new bety[10], read(bety,0,9),那么每次讀進(jìn)數(shù)組的是9個(gè)字節(jié),那么每次數(shù)據(jù)就會(huì)剩下一個(gè)字節(jié)項(xiàng),那么下一次用read讀,讀到的第一個(gè)字節(jié)就會(huì)放在數(shù)組的最后一個(gè)位置,而剩下的8個(gè)字節(jié)替換掉上次的前8個(gè)字節(jié),想想就復(fù)雜 )。
我們常用的是read(b)接下來我們來完整的演示使用過程吧!
/* * 以字節(jié)為單位讀取文件,常用于讀二進(jìn)制文件,如圖片、聲音、影像等文件。*/public static void readFileByBytes(String inFile, String outFile) {File file = new File(fileName); //①寫好文件路徑try {byte[] tempbytes = new byte[100]; //②創(chuàng)建數(shù)組當(dāng)緩存區(qū)int byteread = 0;InputStream in = new FileInputStream(inFile); //③獲取文件輸入流OutputStream out = new FileOutputStream(outFile); //④獲取打開文件while ((byteread = in.read(tempbytes)) != -1) {//⑤每次讀取100個(gè)字節(jié)進(jìn)入緩存數(shù)組out.write(tempbytes, 0, byteread); //⑥立即從緩沖區(qū)中寫入文件,每次寫入的字節(jié)數(shù)必須和實(shí)際讀到的字節(jié)數(shù)相同}} catch (Exception e1) {e1.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e1) {}try {out.close();} catch (IOException e1) {}}}}3.BufferedInputStream 和 BufferedOutputStream
BufferedInputStream是帶緩沖區(qū)的輸入流,它繼承于FilterInputStream。默認(rèn)緩沖區(qū)大小是8M,能夠減少訪問磁盤的次數(shù),提高文件讀取性能。
BufferedOutputStream是帶緩沖區(qū)的輸出流,它繼承于FilterOutputStream,能夠提高文件的寫入效率。
它們提供的“緩沖功能”本質(zhì)上是通過一個(gè)內(nèi)部緩沖區(qū)數(shù)組實(shí)現(xiàn)的。例如,在新建某輸入流對(duì)應(yīng)的BufferedInputStream后,當(dāng)我們通過read()讀取輸入流的數(shù)據(jù)時(shí),BufferedInputStream會(huì)將該輸入流的數(shù)據(jù)分批的填入到緩沖區(qū)中。每當(dāng)緩沖區(qū)中的數(shù)據(jù)被讀完之后,輸入流會(huì)再次填充數(shù)據(jù)緩沖區(qū);如此反復(fù),直到我們讀完輸入流數(shù)據(jù)。(其實(shí)書寫的步驟是一樣的,內(nèi)在區(qū)別)
public static void readAndWrite(String[] args) { try {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f:/a.mp3"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:/b.mp3"));byte[] b=new byte[1024];int len=0;while(-1!= (len = bis.read(b, 0, b.length))) {bos.write(b, 0, len);}} catch (FileNotFoundException e) {System.out.println("文件找不到");e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{if (null! = bos){bos.close();}if (null! = bis){bis.close();}} }4.DataInputStream和DataOutputStream
DataInputStream 是數(shù)據(jù)輸入流,它繼承于FilterInputStream。
DataOutputStream 是數(shù)據(jù)輸出流,它繼承于FilterOutputStream。
二者配合使用,“允許應(yīng)用程序以與機(jī)器無關(guān)方式從底層輸入流中讀寫基本 Java 數(shù)據(jù)類型”。
二.字符流
二、字符流
1.InputStreamReader 和 OutputStreamWriter
InputStreamReader 和 OutputStreamWriter為各種輸入輸出字符流的基類,所有字符流都繼承這兩個(gè)基類。實(shí)際上,這兩個(gè)類內(nèi)部各自持有一個(gè)inputStream 和 outputStream對(duì)象,相當(dāng)于是對(duì)inputStream 和 outputStream進(jìn)行了包裝,將輸入字節(jié)流轉(zhuǎn)換成字符流,便于讀寫操作。
/*** 以字符為單位讀取文件,常用于讀文本,數(shù)字等類型的文件*///用reader的read()一次只讀一個(gè)字符public static void readFileByChars(String fileName) {File file = new File(fileName);Reader reader = null;try {System.out.println("以字符為單位讀取文件內(nèi)容,一次讀一個(gè)字節(jié):");//1. 一次讀一個(gè)字符 reader = new InputStreamReader(new FileInputStream(file));//可以是任意的InputStream類,不一定必須FileInputStreamint tempchar;while ((tempchar = reader.read()) != -1) {if (((char) tempchar) != '\r') {System.out.print((char) tempchar);}}reader.close();} catch (Exception e) {e.printStackTrace();}----------------------------------------------------------------------------------------//用用reader的read(b)一次讀數(shù)組大小個(gè)字符try {System.out.println("以字符為單位讀取文件內(nèi)容,一次讀多個(gè)字節(jié):");//2. 一次讀多個(gè)字符char[] tempchars = new char[30];int charread = 0;reader = new InputStreamReader(new FileInputStream(fileName));while ((charread = reader.read(tempchars)) != -1) {for (int i = 0; i < charread; i++) {if (tempchars[i] != '\r') {System.out.print(tempchars[i]);}}}} catch (Exception e1) {e1.printStackTrace();} finally {if (reader != null) {try {reader.close();} catch (IOException e1) {}}} }2.FileReader 和 FileWriter
FileReader 和 FileWriter分別繼承自 inputStreamReader 和 outputStreamWriter。它是對(duì)讀取文件操作系統(tǒng)的封裝,所有的讀寫都是直接操作文件系統(tǒng)。因此如果是頻繁讀寫操作,不建議使用FileReader 和 FileWriter,性能將會(huì)非常低,這時(shí)你需要使用BufferedReader。
(1)FileWriter類
構(gòu)造方法:
FileWriter fw = new FileWriter(String fileName); //創(chuàng)建字符輸出流類對(duì)象和已存在的文件相關(guān)聯(lián)。文件不存在的話,并創(chuàng)建。
FileWriter fw = new FileWriter(String fileName,boolean append); //創(chuàng)建字符輸出流類對(duì)象和已存在的文件相關(guān)聯(lián),并設(shè)置該該流對(duì)文件的操作是否為續(xù)寫。
主要方法:
write(char[] buffer, int offset, int count) //將字符數(shù)組寫入,offset為數(shù)組的起始地址,count為需要寫入的字符數(shù)
void write(String str) //寫入字符串。當(dāng)執(zhí)行完此方法后,字符數(shù)據(jù)還并沒有寫入到目的文件中去。此時(shí)字符數(shù)據(jù)會(huì)保存在緩沖區(qū)中。
viod flush() //刷新該流中的緩沖。將緩沖區(qū)中的字符數(shù)據(jù)保存到目的文件中去。
viod close() //關(guān)閉此流。在關(guān)閉前會(huì)先刷新此流的緩沖區(qū)。在關(guān)閉后,再寫入或者刷新的話,會(huì)拋IOException異常。
(2)FileReader類
構(gòu)造方法:
FileReader fr = new FileReader(String fileName); //使用帶有指定文件的String參數(shù)的構(gòu)造方法。創(chuàng)建該輸入流對(duì)象。并關(guān)聯(lián)源文件。
主要方法:
int read(); // 讀取單個(gè)字符。返回作為整數(shù)讀取的字符,如果已達(dá)到流末尾,則返回 -1。
int read(char []cbuf); //將字符讀入數(shù)組。返回讀取的字符數(shù)。如果已經(jīng)到達(dá)尾部,則返回-1。
void close(); //關(guān)閉此流對(duì)象。釋放與之關(guān)聯(lián)的所有資源。
4.BufferedReader 和 BufferedWriter
(1)BufferedReader和BufferedWriter類各擁有8192字符的緩沖區(qū)。當(dāng)BufferedReader在讀取文本文件時(shí),會(huì)先盡量從文件中讀入字符數(shù)據(jù)并置入緩沖區(qū),而之后若使用read()方法,會(huì)先從緩沖區(qū)中進(jìn)行讀取。如果緩沖區(qū)數(shù)據(jù)不足,才會(huì)再從文件中讀取,使用BufferedWriter時(shí),寫入的數(shù)據(jù)并不會(huì)先輸出到目的地,而是先存儲(chǔ)至緩沖區(qū)中。如果緩沖區(qū)中的數(shù)據(jù)滿了,才會(huì)一次對(duì)目的地進(jìn)行寫出。
(2)從標(biāo)準(zhǔn)輸入流System.in中直接讀取使用者輸入時(shí),使用者每輸入一個(gè)字符,System.in就讀取一個(gè)字符。為了能一次讀取一行使用者的輸入,使用了BufferedReader來對(duì)使用者輸入的字符進(jìn)行緩沖。readLine()方法會(huì)在讀取到使用者的換行字符時(shí),再一次將整行字符串傳入
System.in是一個(gè)位流,為了轉(zhuǎn)換為字符流,可使用InputStreamReader為其進(jìn)行字符轉(zhuǎn)換,然后再使用BufferedReader為其增加緩沖功能。例如:
public static void readAndWrite() { try { //緩沖System.in輸入流 //System.in是位流,可以通過InputStreamReader將其轉(zhuǎn)換為字符流 BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in)); //緩沖FileWriter BufferedWriter bufWriter = new BufferedWriter(new FileWriter("/sdcard/log/test.txt")); String input = null; //每讀一行進(jìn)行一次寫入動(dòng)作 while(!(input = bufReader.readLine())) { bufWriter.write(input); //newLine()方法寫入與操作系統(tǒng)相依的換行字符,依執(zhí)行環(huán)境當(dāng)時(shí)的OS來決定該輸出那種換行字符 bufWriter.newLine(); } bufReader.close(); bufWriter.close(); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("沒有指定文件"); } catch(IOException e) { e.printStackTrace(); }}三、 RandomAccessFile
RandomAccessFile不屬于InputStream和OutputStream類系的。實(shí)際上,它和這兩個(gè)類系毫不相干,甚至不使用InputStream和OutputStream類中已經(jīng)存在的任何功能;它是一個(gè)完全獨(dú)立的類。這可能是因?yàn)镽andomAccessFile能在文件里面前后移動(dòng),所以它的行為與其它的I/O類有些根本性的不同。
RandomAccessFile的基本功能有:定位用的getFilePointer( ),在文件里移動(dòng)用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節(jié)數(shù)。此外,它的構(gòu)造函數(shù)還要一個(gè)表示以只讀方式(“r”),還是以讀寫方式(“rw”)打開文件的參數(shù)。實(shí)際它和C的fopen()一模一樣,都是直接對(duì)文件句柄操作。
總結(jié)
以上是生活随笔為你收集整理的详细领略Java的输入流和输出流的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 表哥的Access入门++以Excel视
- 下一篇: Java EE eclipse 优化配置