(转)DataInputStream类readLong()引起的思考

今天无意中看了下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);
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 8086汇编 本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他一样...
    Gibbs基阅读 37,679评论 8 114
  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 9,812评论 1 44
  • 1 关键字 1.1 关键字的概述 Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或...
    哈哈哎呦喂阅读 3,921评论 0 0
  • 或许我没有资格说 但是我觉得,自己真的是笨的可以 或许这样的人就是这样 亦或许,只是因为对象是我 呵呵~ 以后还是...
    Look杨雯子阅读 2,498评论 0 0
  • 成长的路,每一步都算数 空闲之余,今天翻看了李笑来写在知笔墨上的新生-七年就是一辈子。虽然只看了几篇,但是依然不妨...
    岸远_水声微阅读 1,306评论 0 0