美文网首页
(转)DataInputStream类readLong()引起的

(转)DataInputStream类readLong()引起的

作者: 王刚_768f | 来源:发表于2018-05-06 00:20 被阅读0次

    今天无意中看了下jdk中的DataInputStream类,然后看到readLong()方法,如下:

    private byte readBuffer[] = new byte[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));
        }
    

    顿时觉得很困惑,为什么数组里的第一个元素直接进行移位运算,而后面的都和255进行了与运算呢?
    当时觉得困惑的原因是因为byte类型转成int类型应该不用做任何处理的,后来查了下资料后获得了灵感,找到了原因。
    原因是这样的,在将输入流的内容读取到byte数组时,会进行截断。因为输入流读取时,虽然是按byte读取的,但是是以int类型返回,且数据范围是1~255,除非到了输入流结束时,返回才是-1。所以在将数据读取到byte数组,不可避免会进行截断,对于一般的数据可能没有问题,但是对于255这样高位以1开头的数据,会有问题。因为java都是有符号数,开头为1代表是负数。这样,在readLong()里,对数据元素进行移位时,会默认转换成int型,这样就导致byte型的255转成int型后,高位依旧为1(实际上代表的是-1了)。这样并不是我们想要的。实际上需要对这些元素进行无符号扩展,也就是高位补0。这就是为什么都要和255做与运算的原因。同样,可以考虑下为什么第一个元素没有进行与运算直接就移位了?其实答案很简单,就是因为在左移动56位后,高位的8位数字必然是数组里的第一个元素。
    通过这个,我们其实可以做一些无符号左移的操作。

    byte[] bytes = new byte[] { (byte) -42 };
            ByteArrayInputStream input = new ByteArrayInputStream(bytes);
            int i = input.read();       
            System.out.println("无符号数:" + i);        
            System.out.println("无符号二进制数:"  + Integer.toBinaryString(i));
    

    另外可以用更简单的方式:

    byte b = (byte) -42;
            int result = (b & 0xFF);
            System.out.println("无符号数:" + result);
            System.out.println("无符号二进制数:" + Integer.toBinaryString(result)); 
    

    这种方式就用到上面提到的与计算方式。

    备注:
    (long)readBuffer[0] << 56的运算顺序是先对readBuffer[0]向上转型为long,然后做移位运算。
    对于byte,short类型的变量,他们的值域是包括正负的,所以要得到无符号的int值就需要和0xff,0xffff做与运算,其目的是保留低位同时高位置零。
    对于char类型的变量,他的值域不包括负数,所以直接强转成int型就可以了。
    从DataInputStream的readShort()和readUnsignedShort()可以看出,在方法内部都用了in.read(),得到了无符号的int,然后两个int进行拼接。最后如果是要shot值就直接向下转型,如果是要得到无符号值,就返回int。

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

    相关文章

      网友评论

          本文标题:(转)DataInputStream类readLong()引起的

          本文链接:https://www.haomeiwen.com/subject/fvlyrftx.html