数据流DataInput(Output)Stream 和 字节数组流 ByteArrayInput(Output) Stream
2014-07-15 00:00
281 查看
一, 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个构造方法
new ByteArrayOutputStream(int bufferSize)
参数只能用于指定缓冲区大小.
具体看看本节的例子.
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 size ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); DataOutputStream dos = new DataOutputStream(bos); dos.writeDouble(f);//this method will create a new byte[] object bArr = bos.toByteArray(); dos.close(); // bos will be cloased cascade return 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 size ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); DataOutputStream dos = new DataOutputStream(bos); dos.writeDouble(f);//this method will create a new byte[] object bArr = bos.toByteArray(); dos.close(); // bos will be cloased cascade return 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.xml main: [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.678000 BUILD SUCCESSFUL Total time: 1 second
相关文章推荐
- Java之数据流DataInput(Output)Stream 和 字节数组流 ByteArrayInput(Output) Stream的嵌套
- JAVA基础学习(二十二)--IO流四-对象序列化、管道流、RandomAccessFile、DataStream、ByteArrayStream、转换流的字符编码
- IO流 操作字节数组ByteArrayStream
- Expected one of #, input, filter, output at line 2, column 1 (byte 2): Logstash
- logstash启动报配置文件错误Expected one of #, input, filter, output at line 1, column 1 (byte 1) after
- 黑马程序员_IO流四(对象的序列化,管道流,RandomAccessFile,DataStream,ByteArrayStream)
- 黑马程序员_IO流——ByteArrayStream
- Lesson_for_java_day18--java中的IO流(序列化、ByteArrayStream、DataStream、RandowAccessFile)
- IO流5(IO包中的其他类,ObjectStream,管道流,RandomAccessFile,DataStream,ByteArrayStream)
- ByteArrayStream
- reason=>"Expected one of #, input, filter, output at line 1, column 1 (byte 1) after "}
- 内存为源的目的和操作ByteArrayStream
- IO流——操作字节数组ByteArrayStream
- IO流__【对象的序列化】【管道流】【RandomAccessFile】【DataStream】【ByteArrayStream等】
- void LUT(InputArray src, InputArray lut, OutputArray dst)
- 2006-05-16 (3)ByteArray*Stream
- IO流(ByteArrayStream)
- 用于操作字节数组的流对象 ByteArrayStreamDemo
- Stream::Read 方法 (array<Byte>^, Int32, Int32)
- Stream::Read 方法 (array<Byte>^, Int32, Int32)