zoukankan      html  css  js  c++  java
  • Java中byte做&0xff运算的原因及解析

    Java中byte做&0xff运算的原因及解析

    一个行走的民 2018-03-18 17:23:05 6163 收藏 5
    分类专栏: Java 文章标签: java unsigned sigened 无符号数据类型 0xff
    版权

    Java
    专栏收录该内容
    23 篇文章0 订阅
    订阅专栏
    网上瞎写的一大堆,实在是无语。把自己理解的整理分享给大家。
    首先要知道原码、反码、补码是什么。可以参考:
    http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html

    第一种情况:signed/unsigned之间的转化
    java中,除了char是unsigned 两个字节,用来表达UTF-16,此外byte/short/int/long都是signed的。
    取值范围:
    boolean:一字节
    byte:(-128,127) 一字节
    char:(0,65535) 两字节
    short:(-32768,32767)两字节

    那么在Java与别的语言编写的程序通信时,就可能涉及到signed/unsigned之间的转化。比如C写的socket传输unsigned数据

    signed到unsigned的转化
    //JDK 1.8中Byte.toUnsignedInt(byte x)
    public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;//将高24位全部变成0,低8位保持不变
    }

    //JDK 1.8中Integer.toUnsignedLong((int x)
    public static long toUnsignedLong(int x) {
    return ((long) x) & 0xffffffffL;//将高32位全部变成0,低32位保持不变
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    比较典型,经典的运用则是下面的:

    //JDK1.8 ByteArrayInputStream read函数源码

    protected byte buf[];//用于缓存数据的数组

    //调用该函数会返回一个(0,255)的int类型数字,如果读到stream的end,则返回-1
    public synchronized int read() {
    return (pos < count) ? (buf[pos++] & 0xff) : -1;//这里跟调用toUnsignedInt(byte x)的效果是一样的
    }
    1
    2
    3
    4
    5
    6
    7
    8
    上面的源码可能有的人会犯迷糊
    0xff的二进制是1111 1111 (8bit),一个byte也是8bit,
    上面的操作buf[pos++] & 0xff是将一个byte类型的数字&0xff,那么得到的结果应该是还是这个byte类型数字本身呀?

    可能你忽略了一个问题了:Java二元运算符的类型自动提升
    也就是说buf[pos++] & 0xff这个运算中,buf[pos++]已经被自动提升为int了。

    byte b = -20;
    System.out.println(b & 0xff);// 236

    final byte b2 = -20;
    final byte mask = (byte) 0xff;
    System.out.println(b2 & mask); // -20
    1
    2
    3
    4
    5
    6
    第二种情况: byte转化为16进制String
    在如下一段代码中有int v = src[i] & 0xFF这样的代码。

    public static String bytesToHexString(byte[] src){
    StringBuilder stringBuilder = new StringBuilder("");
    if (src == null || src.length <= 0) {
    return null;
    }
    for (int i = 0; i < src.length; i++) {
    int v = src[i] & 0xFF;
    String hv = Integer.toHexString(v);
    if (hv.length() < 2) {
    stringBuilder.append(0);
    }
    stringBuilder.append(hv);
    }
    return stringBuilder.toString();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    这里做&0xff的原因是:
    1.byte是1byte(8位),int是4byte(32位)表示的。
    2.Java中是使用了补码的形式进行数据存储的。
    3.java中byte数据转化为int数据时会自动补位,如果最高位(符号位)是0,则高24位全部补0,若是1,则高24位全部补1。
    4.知道了上面的三点,就有:
    byte -1的二进制为(补码):1111 1111 -->对应的16进制为0xFF
    int -1的二进制为(补码):1111 1111 1111 1111 1111 1111 1111 1111 -->对应的16进制为0xFFFFFFFF
    5.Integer.toHexString(int i)函数内部是通过传进来数字的二进制(补码形式)来进行转换的,因此如果不进行int v = src[i] & 0xFF;
    则得到的结果就是0xFFFFFFFF。而&0xff只后,传入的参数的二进制就变为0000 0000 0000 0000 0000 0000 1111 1111(虽然这个数的值以不再是-1,但是将他进行转换得到的0xff才是我们需要的)
    源码如下:

    //JDK1.8
    static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
    int charPos = len;
    int radix = 1 << shift;
    int mask = radix - 1;
    do {
    buf[offset + --charPos] = Integer.digits[val & mask];//每次do-while循环都会取4位(从高位到低位),Integer.digits[]数组是十六进制的字符集
    val >>>= shift;
    } while (val != 0 && charPos > 0);

    return charPos;
    }

    final static char[] digits = {
    '0' , '1' , '2' , '3' , '4' , '5' ,
    '6' , '7' , '8' , '9' , 'a' , 'b' ,
    'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
    'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
    'o' , 'p' , 'q' , 'r' , 's' , 't' ,
    'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    6.以上五点是全部原因,end
    ————————————————
    版权声明:本文为CSDN博主「一个行走的民」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhaominpro/article/details/79602381


    今天看到一个问题是关于0xff的,深入看进base64的源码,加上看了一些资料,明白的它的用意。
    /**
    * Encodes all remaining bytes from the specified byte buffer into
    * a newly-allocated ByteBuffer using the {@link Base64} encoding
    * scheme.
    *
    * Upon return, the source buffer's position will be updated to
    * its limit; its limit will not have been changed. The returned
    * output buffer's position will be zero and its limit will be the
    * number of resulting encoded bytes.
    *
    * @param buffer
    * the source ByteBuffer to encode
    * @return A newly-allocated byte buffer containing the encoded bytes.
    */
    public ByteBuffer encode(ByteBuffer buffer) {
    int len = outLength(buffer.remaining());
    byte[] dst = new byte[len];
    int ret = 0;
    if (buffer.hasArray()) {
    ret = encode0(buffer.array(),
    buffer.arrayOffset() + buffer.position(),
    buffer.arrayOffset() + buffer.limit(),
    dst);
    buffer.position(buffer.limit());
    } else {
    byte[] src = new byte[buffer.remaining()];
    buffer.get(src);
    ret = encode0(src, 0, src.length, dst);
    }
    if (ret != dst.length)
    dst = Arrays.copyOf(dst, ret);
    return ByteBuffer.wrap(dst);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    进入encode0方法中可以看到下面的代码块

    private int encode0(byte[] src, int off, int end, byte[] dst) {
    char[] base64 = isURL ? toBase64URL : toBase64;
    int sp = off;
    int slen = (end - off) / 3 * 3;
    int sl = off + slen;
    if (linemax > 0 && slen > linemax / 4 * 3)
    slen = linemax / 4 * 3;
    int dp = 0;
    while (sp < sl) {
    int sl0 = Math.min(sp + slen, sl);
    for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
    int bits = (src[sp0++] & 0xff) << 16 |
    (src[sp0++] & 0xff) << 8 |
    (src[sp0++] & 0xff);
    dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
    dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
    dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f];
    dst[dp0++] = (byte)base64[bits & 0x3f];
    }
    int dlen = (sl0 - sp) / 3 * 4;
    dp += dlen;
    sp = sl0;
    if (dlen == linemax && sp < end) {
    for (byte b : newline){
    dst[dp++] = b;
    }
    }
    }
    if (sp < end) { // 1 or 2 leftover bytes
    int b0 = src[sp++] & 0xff;
    dst[dp++] = (byte)base64[b0 >> 2];
    if (sp == end) {
    dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
    if (doPadding) {
    dst[dp++] = '=';
    dst[dp++] = '=';
    }
    } else {
    int b1 = src[sp++] & 0xff;
    dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
    dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
    if (doPadding) {
    dst[dp++] = '=';
    }
    }
    }
    return dp;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    src[sp0++] & 0xff src 是一个 byte数组 哎
    网上有很多说这个用法的含义的,我就不强行解释了
    总结为一句话:byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性。当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。
    感谢学习大佬的文章[跳转地址](https://blog.csdn.net/zhaowei5210/article/details/70920711)
    ————————————————
    版权声明:本文为CSDN博主「秋天的铁工匠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_37970049/article/details/103762790


    关于原码反码和补码以及byte&0xff

     

    1. 问题由来

    本笔记是由byte&0xff引申出来的。在看某一段代码的时候发现有这么一个逻辑:

     

    该方法的功能是把四个元素的byte数组转换成ip4地址,从debug的中间过程可以看出来src的第二个元素为负数-100,但它确实是表示ip地址的第二个字节,且src[1] & 0xff之后又变为了正数156,这其中的现象如何解释?要想知道这里面的原因,首先需要知道原码、反码和补码的概念。

    2. 原码、反码和补码

    数字是可以二进制来表示的,比如8的二进制表示为00001000,-8的二进制为10001000,其中最高位为符号位。对于正数来说,其二进制原码,反码,补码均相同。而对于负数来说,反码等于符号位位不变,其余各位取反;补码等于其反码加1。

    比如问题中的156,其二进制表示为10011100,其反码和补码也是10011100。而-100的二进制表示为11100100,其反码为10011011,补码为1011100。这时候会发现156的原码和-100的补码是一致的。

    3. byte & 0xff的细节

    我们知道byte是java的一种基本类型,其大小为一个字节,表示的整数范围是-128~127。而当其最高为不解释为符号位时,其最大可以表示的数为255。因此例子中字节数组的第二个元素debug时被解释为-100的原因是,156的二进制表示正好可以解释为带符号byte的-100,且我们可以发现byte中存储的内容其实是补码。

    ok,第一个问题清楚了,-100的补码与无符号的156的二进制表示一致,且java中的byte存储的是补码。那么,为什么src[1] & 0xff之后就变回了正数呢?讲道理,0xff可以表示为11111111,我们知道和1与运算之后还是其本身,乍一看,这个运算是没有意义的。

    0xff究竟是怎么一回事呢。可以考虑一下下面这段代码中c的值会是什么:

    int a = 255;
    int b = 0xff;
    boolean c = a == b;

    c的值为true。其实0xff与255没有本质的区别,一个是十进制一个是十六进制,都是用来表示一个int型正数。回道上面的问题,src[1] & 0xff也就是src[1]与一个整数相与,那么src[1]首先就要先转换成一个四字节的整数:

    11111111 11111111 11111111 10011100,而0xff为一个整数,其四字节表示为00000000 00000000 00000000 1111111,两者相与的结果为00000000 00000000 00000000 10011100,也即是解释为整数156,这样也就达到了想要获取byte中实际装入的无符号156的目的。

    南来地,北往的,上班的,下岗的,走过路过不要错过!

    ======================个性签名=====================

    之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?

    下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!

    如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.

    我的开发工具

    对于博客园里的网友,不敢称为叫"程序员"的人,你们攻击性太强,看来你们是不会想到我的用意的.园子里有不少人都非常喜欢Jeffrey,是因为它的第一版 框架设计 CLR via C#.
    可是从第一版到现在的第三版,没有看到真正底层的东西,内容仅仅是比MSDN文档更丰富一些,可能是我的要求太高了吧.
    也就是因为它很多时候会接触到微软开发人员,会经常聊聊某些问题而已,而它又将这些问题反应到书中.也许它就像一个小记者.
    它的年龄大我们不多,我的孩子与它小儿子一般大,如果我能向它那样出入微软与它们开发人员长时间交流,不仅仅会牛成它这样.....
    可是微软的开发人员不会扔太多时间在它这儿的.所以它会整天追着这个,赶它那个..屁颠个不停吧...
    而它的另一版被称为好书的 Windows核心编程,更是没有什么深度可言,仅仅是将windows提供的api,以及内核功能再重申了一遍.
    这些书对晋及编程知识是有些贡献的,再说一遍我不是在匾低谁,说说想法而已.

  • 相关阅读:
    合理处理沉没成本
    推荐一个基于Ajax的查询API网站
    为blog添加天气预报功能
    我仅仅一个熟练的coder
    管理和IT的对话
    10个你未必知道的CSS技巧
    如何使用ajax开发web应用程序(二)
    5月20日,系分考试后感。
    说说大型高并发高负载网站的系统架构
    盗用sina的爱问投诉代码实现网页对话框。
  • 原文地址:https://www.cnblogs.com/ioriwellings/p/15131131.html
Copyright © 2011-2022 走看看