您好,登錄后才能下訂單哦!
這篇文章主要介紹了java中IO流有哪些,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
以FileInputStream為例子。FileInputStream的硬盤讀取方式分為兩種,一次讀取一個字節(jié)和一次讀取一個字節(jié)數(shù)組。字節(jié)數(shù)組的大小不同,實際IO耗時也不同,1為示例代碼,3展示了1代碼中讀寫耗時隨字節(jié)數(shù)組大小的變化趨勢。隨著字節(jié)數(shù)組的增大,讀寫耗時減小,主要是硬盤尋道時間(seek time)和旋轉(zhuǎn)時間(rotational latency)的減少。在硬盤讀寫耗時很長時,內(nèi)存讀寫的耗時相比硬盤讀寫可以忽略,硬盤讀寫的耗時分為尋道時間(seek time)、旋轉(zhuǎn)時間(rotational latency)和傳輸時間(transfer time),傳輸時間相對于尋道時間和旋轉(zhuǎn)時間(尋道時間和旋轉(zhuǎn)時間后合并稱為尋址時間)可以忽略【1】。硬盤的尋址時間在一個塊中的第一個字節(jié)耗時長,一個塊中的其余字節(jié)可以忽略。當字節(jié)數(shù)組增大時(從32增加到1024*16 byte),尋址一個塊中的第一個字節(jié)的場景線性減少,尋址時間也線性減少,因此IO耗時呈線性減少趨勢。當字節(jié)數(shù)組大小繼續(xù)增大(從1024 * 8增加到1024 * 1024 * 16),此時尋址時間已降到很低,相比傳輸時間可以忽略時,IO耗時的變化趨于平穩(wěn)。當字節(jié)數(shù)組大小繼續(xù)增大時,讀寫耗時又出現(xiàn)增大的趨勢,這個我還沒找到原因。當在數(shù)組較大(大于1024 *1024 *4)時,read(byte[])方法中除去讀寫之外也會有其它耗時,測試代碼如2,測試數(shù)據(jù)如圖3附表,這個機制我還不清楚(可能需要深入了解jvm的底層實現(xiàn)了),3中在計算讀寫耗時時應減去這部分時間。
示例代碼
public class Demo01_Copy { public static void main(String[] args) throws IOException { File src = new File ("e:\\foxit_Offline_FoxitInst.exe"); File dest = new File("e:\\ithema\\foxit_Offline_FoxitInst.exe"); byte[] bytes = new byte[1024*128];//調(diào)整字節(jié)數(shù)組的大小,看IO耗時的變化 long time1 = System.currentTimeMillis(); copyFile2(src,dest,bytes); long time2 = System.currentTimeMillis(); System.out.println(time2 -time1); } public static void copyFile2(File src,File dest,byte[] bytes) throws IOException{ InputStream in = new FileInputStream(src); OutputStream os = new FileOutputStream(dest); int len = 0; while((len = in.read(bytes))!=-1){ os.write(bytes,0,len); } in.close(); os.close(); } }
1.通過FileInputStream一次讀取一個字節(jié)數(shù)組
public class Demo02_Copy { public static void main(String[] args) throws IOException { File src = new File ("e:\\1.txt"); File dest = new File("e:\\ithema\\1.txt"); byte[] bytes = new byte[1024*128];//調(diào)整字節(jié)數(shù)組的大小,看IO耗時的變化 long time1 = System.currentTimeMillis(); copyFile2(src,dest,bytes); long time2 = System.currentTimeMillis(); System.out.println(time2 -time1); } public static void copyFile2(File src,File dest,byte[] bytes) throws IOException{ InputStream in = new FileInputStream(src); OutputStream os = new FileOutputStream(dest); int len = 0; while((len = in.read(bytes))!=-1){ os.write(bytes,0,len); } in.close(); os.close(); } }
2.測試除硬盤內(nèi)存讀寫外的其它耗時(1.txt文件為空)
3.當字節(jié)數(shù)組大小變化,讀寫總耗時的變化趨勢(折線圖數(shù)據(jù)來源于表格中藍色背景填充的數(shù)據(jù))
當數(shù)組大小從32逐漸增大到1024*16byte時,IO耗時呈線性減少,這基于FileInputStream的read(byte[])實現(xiàn)。read(byte[])的源碼如4所示,read(byte b[])是一個本地方法,它保證了硬盤的尋址時間在讀取一個數(shù)組大小的字節(jié)塊的第一個字節(jié)耗時較長,字節(jié)塊的其余字節(jié)可以忽略。而相對于read()方法,一個字節(jié)一個字節(jié)讀取,每讀取一個字節(jié)都要重新進行硬盤尋址。
public class FileInputStream extends InputStream { public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /** * Reads a subarray as a sequence of bytes. * @param b the data to be written * @param off the start offset in the data * @param len the number of bytes that are written * @exception IOException If an I/O error has occurred. */ private native int readBytes(byte b[], int off, int len) throws IOException; }
4.FileInputStream 的 read(byte[]) 方法源碼
假設現(xiàn)在你要寫一個程序以計算一個text文件的行數(shù)。一種方法是使用read()方法從硬盤中一次讀取1個字節(jié)到內(nèi)存中,并檢查該字節(jié)是不是換行符“\n”【2】。這種方法已被證明是低效的。
更好的方法是使用字節(jié)緩沖流,先將字節(jié)從硬盤一次讀取一個緩沖區(qū)大小的字節(jié)到內(nèi)存中的讀緩沖區(qū),然后在從讀緩沖區(qū)中一次讀取一個字節(jié)。在逐字節(jié)讀取讀取緩沖區(qū)時,檢查字節(jié)是不是換行符'\n'。字節(jié)緩沖流BufferedInputStream的源碼如5所示,先從硬盤讀取一個緩沖大小的字節(jié)塊到緩沖區(qū),然后逐個讀取緩沖區(qū)的字節(jié);當緩沖區(qū)的字節(jié)讀取完畢后,在調(diào)用fill()方法填充緩沖區(qū)。字節(jié)緩沖流BufferedInputStream的緩沖區(qū)大小為8192。6中對比了字節(jié)緩沖流和普通字節(jié)流的讀寫效率;字節(jié)緩沖流的讀耗時僅為8ms,而沒有緩沖區(qū)的普通字節(jié)流的耗時為567ms。圖7中展示了6中字節(jié)緩沖流讀寫文件的示意圖。
public class BufferedInputStream extends FilterInputStream { private static int DEFAULT_BUFFER_SIZE = 8192; public synchronized int read() throws IOException { //當緩沖區(qū)的字節(jié)已被讀取完畢后,調(diào)用fill()方法從硬盤讀取字節(jié)塊填充緩沖區(qū); if (pos >= count) { fill(); if (pos >= count) return -1; } //返回緩沖區(qū)的一個字節(jié) return getBufIfOpen()[pos++] & 0xff; } private void fill() throws IOException { byte[] buffer = getBufIfOpen(); //初始定義int markpos = -1; if (markpos < 0) pos = 0; /* no mark: throw away the buffer */ else if (pos >= buffer.length) /* no room left in buffer */ if (markpos > 0) { /* can throw away early part of the buffer */ int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; /* buffer got too big, invalidate mark */ pos = 0; /* drop buffer contents */ } else if (buffer.length >= MAX_BUFFER_SIZE) { throw new OutOfMemoryError("Required array size too large"); } else { /* grow buffer */ int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { // Can't replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf == null; throw new IOException("Stream closed"); } buffer = nbuf; } count = pos; //從硬盤讀取一個緩沖區(qū)大小的塊到緩沖區(qū) int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; } }
5.BufferedInputStream的read()方法源碼
public class Demo03_Copy { public static void main(String[] args) throws IOException { File src = new File ("e:\\settings.xml"); File dest = new File("e:\\ithema\\settings.xml"); byte[] bytes = new byte[1024*128]; long time1 = System.currentTimeMillis(); //耗時:567 ms //copyFile1(src,dest); //耗時:8 ms copyFile3(src,dest); long time2 = System.currentTimeMillis(); System.out.println(time2 -time1); } //使用普通字節(jié)流 public static void copyFile1(File src,File dest) throws IOException{ InputStream in = new FileInputStream(src); OutputStream os = new FileOutputStream(dest); int len = 0; int lineSum = 1; while((len = in.read())!= -1){ if(len == '\n'){ lineSum++; } os.write(len); } System.out.println("lineSum:"+lineSum); in.close(); os.close(); } //使用字節(jié)緩沖流 public static void copyFile3(File src,File dest) throws IOException{ InputStream in = new BufferedInputStream(new FileInputStream(src)); OutputStream os = new BufferedOutputStream(new FileOutputStream(dest)); int len = 0; int lineSum = 1; while((len = in.read())!=-1){ if(len == '\n'){ lineSum ++; } os.write(len); } System.out.println("lineSum:"+lineSum); in.close(); os.close(); } }
6.字節(jié)緩沖流和普通字節(jié)流的讀寫效率對比
7.使用字節(jié)緩沖流在圖6中讀寫文件的示意圖
轉(zhuǎn)換流實現(xiàn)了在指定的編碼方式下進行字節(jié)編碼和字符編碼的轉(zhuǎn)換。轉(zhuǎn)換流如果直接從硬盤一次一個字節(jié)讀取的轉(zhuǎn)換流效率也很低,所以轉(zhuǎn)換流一般都是基于字節(jié)緩沖流的。轉(zhuǎn)換流InputStreamReader的使用如8所示,圖中代碼底層的執(zhí)行流程圖如9所示。InputStreamReader 的源碼解析圖如10所示,轉(zhuǎn)碼的關鍵代碼如11所示。如11,一個字符的字符編碼所占字節(jié)個數(shù)固定為2個字節(jié),但一個字符的字符編碼經(jīng)過轉(zhuǎn)換流按UTF-8格式轉(zhuǎn)換為字節(jié)編碼后,字節(jié)編碼所占字節(jié)個數(shù)為1~4個。
public class Demo01_InputStreamReader { public static void main(String[] args) throws IOException { readUTF(); } //一次讀取一個字符 public static void readUTF() throws IOException{ InputStreamReader isr = new InputStreamReader(new FileInputStream("e:\\2.txt"),"UTF-8"); int ch = 0; while((ch = isr.read())!=-1){ System.out.println((char)ch); } isr.close(); } }
8. 使用轉(zhuǎn)換流InputStreamReader一次讀取一個字符
9 .InputStreamReader在read()時的底層流程圖(文件中的字節(jié)編碼可通過FileInputStream讀取查看)
10 .InputStreamReader的read()源碼解析圖
class UTF_8 extends Unicode{ private CoderResult decodeArrayLoop(ByteBuffer paramByteBuffer, CharBuffer paramCharBuffer) { byte[] arrayOfByte = paramByteBuffer.array(); int i = paramByteBuffer.arrayOffset() + paramByteBuffer.position(); int j = paramByteBuffer.arrayOffset() + paramByteBuffer.limit(); char[] arrayOfChar = paramCharBuffer.array(); int k = paramCharBuffer.arrayOffset() + paramCharBuffer.position(); int m = paramCharBuffer.arrayOffset() + paramCharBuffer.limit(); int n = k + Math.min(j - i, m - k); while ((k < n) && (arrayOfByte[i] >= 0)) arrayOfChar[(k++)] = (char)arrayOfByte[(i++)]; while (i < j) { int i1 = arrayOfByte[i]; if (i1 >= 0) { if (k >= m) return xflow(paramByteBuffer, i, j, paramCharBuffer, k, 1); arrayOfChar[(k++)] = (char)i1; i++; } else { int i2; if ((i1 >> 5 == -2) && ((i1 & 0x1E) != 0)) { if ((j - i < 2) || (k >= m)) return xflow(paramByteBuffer, i, j, paramCharBuffer, k, 2); i2 = arrayOfByte[(i + 1)]; if (isNotContinuation(i2)) return malformedForLength(paramByteBuffer, i, paramCharBuffer, k, 1); arrayOfChar[(k++)] = (char)(i1 << 6 ^ i2 ^ 0xF80); i += 2; } else { int i3; int i4; if (i1 >> 4 == -2) { i2 = j - i; if ((i2 < 3) || (k >= m)) { if ((i2 > 1) && (isMalformed3_2(i1, arrayOfByte[(i + 1)]))) return malformedForLength(paramByteBuffer, i, paramCharBuffer, k, 1); return xflow(paramByteBuffer, i, j, paramCharBuffer, k, 3); } i3 = arrayOfByte[(i + 1)]; i4 = arrayOfByte[(i + 2)]; if (isMalformed3(i1, i3, i4)) return malformed(paramByteBuffer, i, paramCharBuffer, k, 3); char c = (char)(i1 << 12 ^ i3 << 6 ^ (i4 ^ 0xFFFE1F80)); if (Character.isSurrogate(c)) return malformedForLength(paramByteBuffer, i, paramCharBuffer, k, 3); arrayOfChar[(k++)] = c; i += 3; } else if (i1 >> 3 == -2) { i2 = j - i; if ((i2 < 4) || (m - k < 2)) { i1 &= 255; if ((i1 > 244) || ((i2 > 1) && (isMalformed4_2(i1, arrayOfByte[(i + 1)] & 0xFF)))) return malformedForLength(paramByteBuffer, i, paramCharBuffer, k, 1); if ((i2 > 2) && (isMalformed4_3(arrayOfByte[(i + 2)]))) return malformedForLength(paramByteBuffer, i, paramCharBuffer, k, 2); return xflow(paramByteBuffer, i, j, paramCharBuffer, k, 4); } i3 = arrayOfByte[(i + 1)]; i4 = arrayOfByte[(i + 2)]; int i5 = arrayOfByte[(i + 3)]; int i6 = i1 << 18 ^ i3 << 12 ^ i4 << 6 ^ (i5 ^ 0x381F80); if ((isMalformed4(i3, i4, i5)) || (!Character.isSupplementaryCodePoint(i6))) return malformed(paramByteBuffer, i, paramCharBuffer, k, 4); arrayOfChar[(k++)] = Character.highSurrogate(i6); arrayOfChar[(k++)] = Character.lowSurrogate(i6); i += 4; } else { return malformed(paramByteBuffer, i, paramCharBuffer, k, 1); } } } } return xflow(paramByteBuffer, i, j, paramCharBuffer, k, 0); } }
11 .UTF_8中將字節(jié)編碼解碼為字符編碼的方法decodeArrayLoop()
FileReader(String fileName)和InputStreamReader(new FileInputStream(String fileName))是等價的,如12所示,具體實現(xiàn)參見第3節(jié)。BufferedReader的實現(xiàn)與FileReader不同,它們的性能對比如13所示。14展示了BufferedReader的使用,這為了和7中InputStreamReader(new FileInputStream(String fileName))的使用做對比。14中代碼底層的執(zhí)行流程圖如15所示。BufferedReader的方法read()的源碼解析如16所示。BufferedReader和FileReader在字符編碼和字節(jié)編碼的轉(zhuǎn)換時都調(diào)用了CharsetDecoder.decode()方法;不同的是BufferedReader一次轉(zhuǎn)換了8192個字符(15),而FileReader一次只轉(zhuǎn)換了2個字符(9)。但由于BufferedReader和FileReader的字節(jié)緩沖區(qū)大小于均為8192個字節(jié),因此BufferedReader與FileReader效率相差不大。
public class FileReader extends InputStreamReader { public FileReader(String fileName) throws FileNotFoundException { super(new FileInputStream(fileName)); } }
12.FileReader(String filePath)的構(gòu)造方法
public class Demo01_Copy { public static void main(String[] args) throws IOException { File src = new File ("e:\\foxit_Offline_FoxitInst.exe"); File dest = new File("e:\\ithema\\foxit_Offline_FoxitInst.exe"); long time1 = System.currentTimeMillis(); //耗時:3801 ms //copyFile5(src,dest,bytes); //耗時:2938 ms copyFile6(src,dest); long time2 = System.currentTimeMillis(); System.out.println(time2 -time1); } public static void copyFile5(File src ,File dest) throws IOException { FileReader fr = new FileReader(src); FileWriter fw = new FileWriter(dest); int len = 0; while((len=fr.read())!=-1){ fw.write(len); } fr.close(); fw.close(); } public static void copyFile6(File src,File dest) throws IOException{ BufferedReader br = new BufferedReader(new FileReader(src)); BufferedWriter bw = new BufferedWriter(new FileWriter(dest)); int len = 0; while((len=br.read())!=-1){ bw.write(len); } br.close(); bw.close(); } }
13.FileReader和BufferedReader的性能對比
public class Demo01_BufferedReader { public static void main(String[] args) throws IOException { readUTF(); } //一次讀取一個字符 public static void readUTF() throws IOException{ BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream("e:\\2.txt"),"UTF-8")); int ch = 0; while((ch = br.read())!=-1){ System.out.println((char)ch); } br.close(); } }
14.使用BufferedReader一次讀取一個字符(與圖7做對比)
15.BufferedReader在read()時的底層流程圖(與圖8做對比)
16.BufferedReader的read()源碼解析圖(與圖9做對比)
感謝你能夠認真閱讀完這篇文章,希望小編分享的“java中IO流有哪些”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業(yè)資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。