Java之IO(四)DataInputStream和DataOutputStream
转载请注明源出处:http://www.cnblogs.com/lighten/p/6986155.html
1.前言
DataInputStream和DataOutputStream分别继承了FilterInputStream和FilterOutputStream,这块内容在第二节介绍BufferedInputStream和BufferedOutputStream的时候介绍过了,这里不再介绍。Java中针对于IO有许多现有的封装类可以使用,但是每个封装类都有各自的特点,使用场景不一样。本章介绍的这对IO还实现了其它的接口,DataInput和DataOutput。之前所讲到的流都是字节流,但是实际上对我们而言,字节流是没有什么太大的意义,单个字节人是无法知道含义的,所以通常需要对字节进行解析,获取真正有价值的意义。而这对流就完成了一个对基本数据类型(boolean,byte,unsignedByte,short,unsignedShort,char,int,long,float,double)的写入和读取和一些常用的读一行,读取UTF格式等方法,不需要我们自己重复写。当然Java的IO体系针对字符流有专门的Reader和Writer进行操作字符流。这在之后的章节进行介绍。
2.DataInputStream
DataInputStream需要接受一个输入源流。
这些基础的InputStream的方法都是使用输入源的基本方法。
这些方法其实也十分的简单,都是针对每个类型的特点,将其读取出来,当然写入流的时候也要这样才行。
// 读取一个整形,根据是否为0返回boolean值 public final boolean readBoolean() throws IOException { int ch = in.read(); if (ch < 0) throw new EOFException(); return (ch != 0); } // 读取一个整形,转成byte类型(实际上存的就是byte类型) public final byte readByte() throws IOException { int ch = in.read(); if (ch < 0) throw new EOFException(); return (byte)(ch); } // 读取一个整形,直接返回就是无符号的byte类型值了 public final int readUnsignedByte() throws IOException { int ch = in.read(); if (ch < 0) throw new EOFException(); return ch; } // 读取两个字节,short是占两个字节,把第一读出来的左移8位到高8位。最后强转成short public final short readShort() throws IOException { int ch1 = in.read(); int ch2 = in.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (short)((ch1 << 8) + (ch2 << 0)); } // 和short的方式一样,不进行强转就是无符合short public final int readUnsignedShort() throws IOException { int ch1 = in.read(); int ch2 = in.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + (ch2 << 0); } // char也是两个字节,方法和short一样,最后强转成char public final char readChar() throws IOException { int ch1 = in.read(); int ch2 = in.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (char)((ch1 << 8) + (ch2 << 0)); } // int是4个字节,第一个左移24位到最高8位,之后的依次就是最后的结果 public final int readInt() throws IOException { int ch1 = in.read(); int ch2 = in.read(); int ch3 = in.read(); int ch4 = in.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); } // long是8个字节,读入一个数组,然后按照一定的转换方式进行转换 public final long readLong() throws IOException { readFully(readBuffer, 0, 8); return (((long)readBuffer[0] << 56) + ((long)(readBuffer[1] & 255) << 48) + ((long)(readBuffer[2] & 255) << 40) + ((long)(readBuffer[3] & 255) << 32) + ((long)(readBuffer[4] & 255) << 24) + ((readBuffer[5] & 255) << 16) + ((readBuffer[6] & 255) << 8) + ((readBuffer[7] & 255) << 0)); } // float是调用readInt再通过Float的方法来转成float public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } // double与float类似,不过是8位所以用readLong() public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); }
最后的readline()方法已经被废弃了。不过可以看看源代码,比较有意思:
public final String readLine() throws IOException { char buf[] = lineBuffer; if (buf == null) { buf = lineBuffer = new char[128]; } int room = buf.length; int offset = 0; int c; loop: while (true) { switch (c = in.read()) { case -1: case '\n': break loop; case '\r': int c2 = in.read(); if ((c2 != '\n') && (c2 != -1)) { if (!(in instanceof PushbackInputStream)) { this.in = new PushbackInputStream(in); } ((PushbackInputStream)in).unread(c2); } break loop; default: if (--room < 0) { buf = new char[offset + 128]; room = buf.length - offset - 1; System.arraycopy(lineBuffer, 0, buf, 0, offset); lineBuffer = buf; } buf[offset++] = (char) c; break; } } if ((c == -1) && (offset == 0)) { return null; } return String.copyValueOf(buf, 0, offset); }
用了一个无限循环,读取到换行符才进行break,因为换行符根据操作系统不同,有两种'\r\n'和'\n'。所以'\r'的时候还判断了一下后面的字符是不是\n。不是的话使用了一个PushbackInputStream,这个很有意思,一般流读取了是无法反悔的(不能再次读取),但是专门有了个反悔的输入流。这个其实也简单,和BufferedInputStream原理类似,就是里面有个数组,不同的是BufferedInputStream是为了存从流中取得的字节,PushbackInputStream是放入反悔的字节,pos计数还是从缓存大小倒数的,这也是个不同点。最后是将流读取成UTF格式的字符串。但是只限于DataOutputStream输出的,其解析有固定的格式,前一个Int是字符串长度。
3.DataOutputStream
DataOutputStream也需要一个输出源,里面有一个计数字段written,计算写入了多少字节的数据,是int类型。如果长度超多了int类型的最大值,就会使用Integer.MAX_VALUE来标记。
超过了就会变成负数,就是变成了Integer.MAX_VALUE。接下来就是与DataInputStream解析方法对应的写入方法了。
public final void writeBoolean(boolean v) throws IOException { out.write(v ? 1 : 0); incCount(1); } public final void writeByte(int v) throws IOException { out.write(v); incCount(1); } public final void writeShort(int v) throws IOException { out.write((v >>> 8) & 0xFF); out.write((v >>> 0) & 0xFF); incCount(2); } public final void writeChar(int v) throws IOException { out.write((v >>> 8) & 0xFF); out.write((v >>> 0) & 0xFF); incCount(2); } public final void writeInt(int v) throws IOException { out.write((v >>> 24) & 0xFF); out.write((v >>> 16) & 0xFF); out.write((v >>> 8) & 0xFF); out.write((v >>> 0) & 0xFF); incCount(4); } public final void writeLong(long v) throws IOException { writeBuffer[0] = (byte)(v >>> 56); writeBuffer[1] = (byte)(v >>> 48); writeBuffer[2] = (byte)(v >>> 40); writeBuffer[3] = (byte)(v >>> 32); writeBuffer[4] = (byte)(v >>> 24); writeBuffer[5] = (byte)(v >>> 16); writeBuffer[6] = (byte)(v >>> 8); writeBuffer[7] = (byte)(v >>> 0); out.write(writeBuffer, 0, 8); incCount(8); } public final void writeFloat(float v) throws IOException { writeInt(Float.floatToIntBits(v)); } public final void writeDouble(double v) throws IOException { writeLong(Double.doubleToLongBits(v)); }
还有一些方法比如writeBytes(String)和writeUTF方法就不介绍了。
4.例子与结语
@Test public void test() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); DataOutputStream dos = new DataOutputStream(baos); dos.writeByte(1); dos.writeInt(234); dos.writeShort(56); dos.writeLong(789L); dos.writeDouble(11.11); dos.writeFloat(22.22f); dos.writeChar('a'); dos.writeBoolean(false); dos.writeUTF("你好!"); dos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); DataInputStream dis = new DataInputStream(bais); System.out.println(dis.readByte()); System.out.println(dis.readInt()); System.out.println(dis.readShort()); System.out.println(dis.readLong()); System.out.println(dis.readDouble()); System.out.println(dis.readFloat()); System.out.println(dis.readChar()); System.out.println(dis.readBoolean()); System.out.println(dis.readUTF()); dis.close(); }
上述就是一个简单的例子了。这里还需要说明的是:这对流是线程不安全的,如果你要在多线程下使用,需要自己保证线程安全。