关于 Java 的 byte 型和 int 型类型之间比较的问题

2018-08-20 11:29:59 +08:00
 shayuvpn0001

最近在看 Java,关于类型比较,有一个问题一直想弄清楚。先直接上代码:

public class Main {
    public static void main(String[] args) {
	// write your code here
        int p = (int)(25);
        byte q = (byte)(25);
        int result = 0;
        if (p == q) {
            result = 1;
        }else{
            result = 2;
        }
        System.out.println("result is "+result);
        return;
    }
}

当一个 byte 类型变量与 int 类型变量进行比较时,head first java 上面的解释是:
The == operator can be used to compare two variables of any kind, and it simply compares the bits.

if (a == b) {...} looks at the bits in a and b and returns true if the bit pattern is the same (although it doesn ’ t care about the size of the variable, so all the extra zeroes on the left end don ’ t matter).

如果按照这个解释,那么==操作符执行的动作应该是将 byte 的 8bit 与 int 的最低位 8bit 逐位比较,如果两者一模一样,则返回 true。

然而我记得以前看过其他语言,还有两种可能的操作: 第一种是 cast,将占用空间小的变量转换为空间大的变量,比如将本例中 byte 类型的 p 直接 cast 为 int 类型的变量,这中间隐藏了一次强制类型转换的操作。 第二种是 scale up and padding,先将 byte 的 1 字节转换为 int 的 4 字节,然后将 byte 转换后的 int 类型高 2 字节填充为低 2 字节的符号位。 我尝试过将上面代码替换为

        int p = (int)(-127);
        byte q = (byte)(-127);

打印结果显示 p 和 q 仍然相等,这样的话,第二种应该是不成立的。

又用 c,C#试验,结果类似,唯一不同的是 C#将 byte 类型处理为 unsigned 类型,不允许赋一个负值。

综上,对于此种比较,一共有三种解释:head first 解释,cast 解释,scale up and padding 解释.

如果按照 head first 的解释,那么 byte 型值 6 应该与整形值 53254 相等,因为前者的二进制是 0000 0110,而后者的二进制值是 1101 0000 0000 0110,因为按照该解释是逐位比较,高位不比。用代码进行试验:

        int p = (int)(53254);
        byte q = (byte)(6);

输出结果是:2,也就是两者并不相等。

想请各位帮我看看,这个是怎么回事?

7280 次点击
所在节点    程序员
5 条回复
honeycomb
2018-08-20 11:46:57 +08:00
在 Java 里 53254 的 int 和 6 的 byte 作==的比较时,会自动类型转换为 53254 的 int 和 6 的 int,所以两者不相等。
honeycomb
2018-08-20 11:50:36 +08:00
-127 的 byte 强制转换为 int 后则是-127 的 int,但 int 的 6 并不是 int 的 53254。

如果你用按位与的方式把-127 的 byte 转换成 int,则输出的符号位依然是 0
szq8014
2018-08-20 12:46:53 +08:00
Integer.toBinaryString 打印两者试试,看看是不是和你的期望一样。

jvm 里面的操作数栈是 32 位的,所以 byte, short, int 三兄弟进去大家都一样按 32 位处理,顶多 byte, short 有一个 range check。。

[oracle 对 JVM 操作数栈定义]( https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.6.2)

[SO 提问,为啥 byte, short 操作的时候要通过 bipush, sipush 来 expand]( https://stackoverflow.com/questions/18469326/why-jvm-expands-byte-short-to-int-before-pushing-on-stack
Lonely
2018-08-20 13:05:48 +08:00
这种问题,可以去看 JavaSE 的 Specification 啊,不要自己在那瞎猜。。。
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).
(出自 JavaSE8 Specification 15.21.1 )
shayuvpn0001
2018-08-20 13:59:58 +08:00
@honeycomb 谢谢,那就是默认有一次操作将 byte 类型 cast 为 int 类型,然后比较了,那这么看来 head first 里面说的不准确。

@szq8014
@Lonely
恩,刚看到。里面说的 signed extension 和 zero extension 应该就是高位按符号位补齐还是按 0 补齐了。JVM 和 Java Language Spec.我确实是想看,我以前是写 C#的,知道有这些东西,但细节方面还是不了解。现在是急着面试,也没办法全看,只能需要的时候看一点是一点了。为了追求速度,也只能有问题就直接上来问,厚着脸皮当伸手党了。

按照 @szq8014 给的 JVM Spec.中 Table 2.3 Actual and Computational types in the JVM 的描述,我理解为 JVM 在做计算的时候是将 boolean, byte, char, short 都用 int 来处理的。如果按照 @Lonely 指出的有 binary numeric promotion,那么我理解比较一个 long 和 byte 比较也会发生 binary numeric promotion。

@szq8014 提到了使用 Integer.toBinaryString 打印观察变量的实际情况,我在想如果直接观察编译后生成的字节码应该也能看出端倪吧(虽然我现在还没仔细了解字节码)

还有一点就是我想了解一下这个 binary promotion 或者说 signed/zero extension 是在编译器编译成字节码时就完成了还是在 JVM 运行的时候才完成的。

sorry,唠唠叨叨说了一大堆。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/481366

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX