偶然看到了一道有关 java 基本数据类型中变量相加和常量相加的面试题,觉得很有意思,先总结如下
题目
1 | byte b1=1, b2=2, b3, b4; |
题目咋一看,似乎很简单。两个数相加,不就是考虑数据类型是否一致,以及运算结果是否会溢出的问题吗?
照着这一思路,我首先判断第 2 行的代码应该是没有问题的,理由如下:(1)变量 b1 和 b2 都是 byte 类型变量;(2)b1+b2 结果为 3,没有超出 byte 的存储空间 - 128~127。
至于第 3 行代码,是两个常量相加,再将结果赋值给变量 b4,应该也不存在编译问题。
编译
编译输出结果如下:
1 | Error(3):java: incompatible types: possible lossy conversion from int to byte |
第 3 行编译报错,第 4 行通过编译
分析
初步断定是数据类型转换失败,因为 int
向 byte
转换时,会存在精度丢失的问题。
那么,这里的 int
数据又是哪里来的呢?难道 byte
类型的 b1 和 b2 相加,结果被自动转成了 int?
带着这些疑问,我查阅了相关资料,发现原来在 java 中基本数据类型在进行运算时,普遍存在着所谓自动数据类型转化问题。之间的转化图可以总结如下:
其中红线表示可以在不丢失精度的情况下完成转化,而蓝线则意味着转化后可能会损失部分精度;不管怎样,图中所有转化,都是 java 编译器所允许的自动类型转换。
相关规则是:
当 byte、short、int、long、char 等数据类型在一起运算,即只有整型数据参与运算时:无 long 型,所有非 int 类型转成 int 类型;有 long 类型,都转成 long 类型。
当 float、double 等数据类型一起运算,即只有浮点型数据参与运算时:一律转为 double 类型进行运算。
当所有数据类型混在一起运算,即整数和浮点数据类型同时存在时:一律转为 double 类型进行运算。
另外还需要注意的是:整型数据的默认类型为 int,浮点型则为 double。一般数据运算都先转为默认数据类型,然后才开始运算;除非有更高级别的数据类型存在,比如规则 1 中存在 long 型,则都转为 long(虽然默认数据类型是 int)
再回到那道面试题,两个 byte
类型的变量进行加法运算,编译器在编译这行代码时会自动将其类型转换为整型默认数据类型 int
,这种现象也叫做 java 的自动类型提升。
从反编译看数据类型转换
另外,借助反编译工具 procyon.jar
,我们可以从代码执行的底层进一步窥探到 java 数据类型转换的过程。由于 b3 = b1 + b2;
编译不通过,现将原始代码修改如下:
1 | byte b1=1, b2=2, b3, b4; |
反编译如下:
1 | b1 : byte |
从第 8 行,我们可以知道 b1+b2,结果是以 int 类型保存的,故源码中我们企图将其运算结果赋值给 b3 会报数据类型不匹配的错误。
而 1+2 这个常量运算式,编译器是先将其结果计算出后,再根据其要被赋值的变量类型(这里被赋值的变量类型是 byte),动态分配存储空间的。
总结
变量相加:一般是先开辟内存空间(根据参与运算的变量类型决定,规则见上面的分析),然后进行相关操作。
常量相加:一般是编译器先帮我们算出结果,然后根据所要赋值的变量类型开辟相应内存空间。
v1.5.2