数据流DataInput(Output)Stream 和 字节数组流 ByteArrayInput(Output) Stream
一, 1個網絡傳輸模型
在一個網絡傳輸模型中.
假如1個電腦A想把1個double類型的值12345.678 傳給另1個網絡另一端的電腦B.?
要如何實現呢?
大概分成幾個步驟.
1. 轉為字節數組(電腦A).
無論網絡上兩個終端要傳輸什么類型的數據, 實際在網線上傳輸的都是2進制數據(bit).
所以電腦A是不能直接把1個double類型傳送出去的.?? 必須將double類型的值12345.678的值轉換為字節類型(16個bit).
將1個double類型轉換為字節類型并不簡單, 這設計編碼的問題, 但是java能把這些都封裝好了.
而1個double類型的數據并不是1個字節就能存放的, double類型長度是64位啊, 所以就需要1個64位長度的字節數組才能完整存放1個float類型的數據.
有人問,既然網絡上傳送的是位(bit), 為何不直接把變量轉化為bit類型.
答案很簡單, 絕大部分計算機的內存的最小單位(1個地址)是8 bit,?? 所以大部分編程語言, 例如c/c++, java等所有變量最小長度就是8bit(1個字節).
所以無論傳輸什么類型的變量, 都需要把變量的數據轉為字節數據, 然后存放在1個字節數組中.
2. 將字節數組打包(電腦A).
為了盡量減少網絡上傳輸的數據大小,? 我們通常要把要傳輸的字節數組打成1個壓縮數據包. 本文不會詳細討論這點.
3. 傳輸數據包(網絡).
這樣, 在網絡上傳輸的實際就是數據包了.?? 本文不會詳細討論這點.
4. 解壓數據包得到字節數組(電腦B)
電腦B 接受到數據包后, 就需要解壓數據包得到1里面封裝的字節數組. 本文不會詳細討論這點.
5. 將字節數組里的數據轉換為原始類型(double)(電腦B)
電腦B 最后還要把該字節數組的數據轉換為原來的類型double, 才真正得到由電腦A傳過來的float類型的值.
如下圖
二, 將1個double變量轉化為1個字節數組
上一節提過了, 本文的重點在于基本變量和字節數組的轉換.
有人覺得數據類型的轉化很基礎, 但是實際上java并沒有提供1個封裝好的方法, 例如 xxx.toByteArray(Double d) 就返回1個字節數組.. 除非你自己寫.
致使是double基本類型的封裝類Double, 也沒有提供轉換為ByteArray的方法.
Double類里有1個類似的byteValue(), 但是返回的只是1個Byte, 實際上只取double變量的低8位. 高位的字節數據都被截掉了..
自己寫1個int類型轉換為1個ByteArray是可行的, 因為我們知道Java中int類型的編碼是補碼, 但是要用到位運算.
例如:
public static byte[] intToBytes2(int n){ byte[] b = new byte[4]; for(int i = 0;i < 4;i++){ b[i] = (byte)(n >> (24 - i * 8)); } return b; }
但是double的編碼遠比int復雜, 必須要用另外的方法來編寫.
而且這樣的話違背了Java的最大優勢和本質:
Java讓程序猿focus on business, 并不需要關心數據在內存里是怎樣存儲的.
有沒有一種方法, 可以接受各種基本類型參數, 轉換為字節數組? 上面說過了, Java沒有提供.
但是Java提供兩個流, 它們嵌套使用的話, 就相當于實現了上面所說的功能, 將各種基本類型轉換為字節數組.
它們就是DataOutputStream 和 ByteArrayOutputStream
2.1 ByteArrayOutputStream
我們知道FlieOutputStream是1個指向文件的的字節流,? 同樣地, ByteArrayOutputStream 也可以理解為1個指向1個ByteArray的字節流.
我們可以同個這個流添加字節數據到1個字節數組.
2.1.1 構造方法
有必要提提這個ByteArrayOutputStream的構造方法.
既然是指向ByteArray的流, 必然要有1個core 字節數組(目標數組).
例如FileOutputStream
有1個構造方法是
new FileOutputStream(File f)作為參數,我們必須制定文件輸出字節流的目標文件
而對于ByteArrayOutputStream,
則沒有
new ByteArrayOutputStream(byte[] b)也就是講我們不能指定數組ouput字節流的目標字節數組.
實際上, 對于ByteArrayOutputStream來講,目標字節數組是builded in的, 也就是它對于程序猿是隱藏的.
但是它提供了另1個方法
toByteArray() , 這個方法new了1個新的字節數組對象, 然后把隱藏的目標字節數組的數據復制到的這個新的字節數組內.
也就是說返回1個隱藏字節數組的副本.
而這個流提供了另1個構造方法
參數只能用于指定緩沖區大小.
具體看看本節的例子.
2.1.2 write方法
ByteArrayOutputStream 的成員方法 和 FileOutputStream 很類似.
都具有
write(int)? 寫1個字節
write(byte[]) 寫1個字節數組
函數
但根據本文, 很明顯我們需要的是 write(double)這個方法, 但是 ByteArrayOutputStream只是1個原始的字節流, 并沒有提供.
所以我們需要1個額外的包裹流.
2.2 DataOutputStream
跟上面那個流不同,? DataOutputStream不能理解為指向"Data"的字節流.
實際上DataOutputStream是處理流(包裹流)的一種, 必須基于1個原始流之上.
DataOutputStream 提供的方法就強大得多了.
例如:
writeFloat(float f)?? 寫入1個float到流中.
writeLong(long l)?? 寫入1個long類型到流中
writeDouble(double d)? 寫入1個double類型到流中
....
可見, DataOutputStream實際提供了各種將基本類型寫入到字節流的1種渠道. 包裹ByteArrayOutputStream的話就能轉換為字節數組.
而writeDouble(double d)方法正是我們需要的.
2.3 1個把double對象轉換為字節數組的例子
private static byte[] Send(double f) throws IOException{byte[] bArr = new byte[64];//the core byteArray is built in. and the 1024 is buffer sizeByteArrayOutputStream bos = new ByteArrayOutputStream(1024);DataOutputStream dos = new DataOutputStream(bos);dos.writeDouble(f);//this method will create a new byte[] objectbArr = bos.toByteArray();dos.close(); // bos will be cloased cascadereturn bArr; }上面的send方法就是接受1個double f,并返回對應的字節數組.
這個就是上面模型電腦A在第一步完成的事情
三, 從字節數組內提取1個double類型
這個步驟是上面模型的電腦B的最后一步, 實際上就是上面第二節的逆行為.
那么到底如何從網絡上接收到的字節數組提取1個double類型?
同樣地, java也沒有提供現成的方法.
道理是樣的,
我們一樣可以利用DataInputStream 和 ByteArrayInputStream 來見建立一條從字節數組到程序的輸入字節流.
如果弄懂了上面第二節(Output), 那么這個Input的原理也不難理解. 把write替換成read就ok了.
下面例子就是把 1個字節數組轉為double類型的方法:
private static double Get(byte[] bArr) throws IOException{double f;ByteArrayInputStream bis = new ByteArrayInputStream(bArr);DataInputStream dis = new DataInputStream(bis);f = dis.readDouble();dis.close();return f;}四, 結合上面兩個方法的簡單程序
下面程序是這樣的.
就是模擬本文一開始的模型,
一段發送1個double, 利用Send方法轉換為 字節數組, 然后利用Get方法還原為double類型.
最后輸出還原后的double類型..
import java.io.*;public class DataStream1{public static void f(){double fi = 12345.678;double fo = 0;byte[] bSend;byte[] bGet;try{bSend = Send(fi);//network.........bGet = bSend;fo = Get(bGet);}catch(IOException e){e.printStackTrace();}System.out.printf("fo is %f\n",fo); }private static byte[] Send(double f) throws IOException{byte[] bArr = new byte[64];//the core byteArray is built in. and the 1024 is buffer sizeByteArrayOutputStream bos = new ByteArrayOutputStream(1024);DataOutputStream dos = new DataOutputStream(bos);dos.writeDouble(f);//this method will create a new byte[] objectbArr = bos.toByteArray();dos.close(); // bos will be cloased cascadereturn bArr; }private static double Get(byte[] bArr) throws IOException{double f;ByteArrayInputStream bis = new ByteArrayInputStream(bArr);DataInputStream dis = new DataInputStream(bis);f = dis.readDouble();dis.close();return f;} }輸出:
gateman@TPEOS Java_1 $ ant Buildfile: /media/store1/Studies/Java/java_start/Java_1/build.xmlmain:[javac] /media/store1/Studies/Java/java_start/Java_1/build.xml:10: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds[javac] Compiling 1 source file to /media/store1/Studies/Java/java_start/Java_1/build/classes[java] fo is 12345.678000BUILD SUCCESSFUL Total time: 1 second
總結
以上是生活随笔為你收集整理的数据流DataInput(Output)Stream 和 字节数组流 ByteArrayInput(Output) Stream的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 缓冲流简介及简单用法
- 下一篇: Java 转换流 简介