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();
	}

  上述就是一个简单的例子了。这里还需要说明的是:这对流是线程不安全的,如果你要在多线程下使用,需要自己保证线程安全。

 

  

 

posted @ 2017-06-11 20:55  dark_saber  阅读(1372)  评论(0编辑  收藏  举报