------- android培训、java培训、期待与您交流! ----------注意:下面的所有内容在java和C#中均适用。二者唯一的不同就是java字节码与.NET IL指令的形式。
首先看看看一下代码,猜测运行结果是什么,结局肯定出乎你的意料
package MyPackage;
public class TypeCast {
public static void main(String[] args) {
byte a=12;
byte b=13;
byte c=a+b;
}
}
这段代码无论在java中,还是C#中,都会给一个类型转换的编译错误:“无法将int类型隐式转换为byte类型”。从这个错误提示中我们可以得出如下结论:a+b结果变成了一个int类型。由于不能将这个int类型隐式转换为byte类型,所以c=a+b;才会出现无法将int类型转换为byte类型的错误。
可问题是, 两个byte类型相加,结果应该还是byte,怎么会成为int呢?这个问题在我最初学习C#的时候就已经遇到了。要想深入理解这个问题,还是需要从java的虚拟机或者.net的CLR说起。由于这是一篇关于java的日志,所以下面以java虚拟机为例:
在32位java虚拟机中,最基本的数据单元是word,简称字。一个字大小被称作字长,32位java虚拟机的字长是4字节。也就是说,在java虚拟机中,每四个字节为一个单位,如果你存储一个byte类型,虽然byte大小事1字节,但他也要占用4字节,byte类似在被存储到虚拟机的那一刻会被转换为int。同样,如果存储一个short类型,他也会占用4字节,并且隐式转换为int类型。
为了更形象的说明这个问题,可以将java虚拟机中的内存想象成一个个窗格,每个窗格大小是4个字节。如果你存储一个小于4字节的类型,也要占用4字节。如果你存储一个大于4字节的类型,那么就需要占用多个窗格。这个窗格就是java虚拟机中的字,窗格的大小就是字长。如下图
现在我们加上一个显示转换,如下:
package MyPackage;
public class TypeCast {
public static void main(String[] args) {
byte a=12;
byte b=13;
byte c=(byte)(a+b);
}
}
编译运行,结果为25;
为了更深刻的理解虚拟机如何处理类型转换,我们将反汇编一下,反汇编后的代码如下:
第1行:bipush 12 将byte类型的12转为int类型后,压入操作数栈。bipush中的b为byte的首字母,i为int的首字母,意味将byte隐式转换为int。注意,这时候,12就已经成为int类型了,占4字节大小。
第2行:istore_1 将操作数栈顶元素12弹出,存储到局部变量a中。
这两行字节码对应于源代码中的byte a=12;完成赋值操作。
第3行:bipush 13;
第4行:istore_2
这两行代码对应于源代码中的byte b=13;
第5行:iload_1;将a压入操作数栈。
第6行:iload_2; 将b压入操作数栈;。
第7行:iadd;弹出操作数栈顶的两个元素,执行int类型的加法操作,将结果再压入栈中。注意,这时候结果是int类型值。
第8行:i2b;将栈顶元素弹出,转换为byte类型;然后再压入操作数栈中。
第9行:istore_3弹出栈顶元素,赋值给c;
第10行:return 返回。
这里最重要的地方有如下几处:
第一行bipush 12指令,这个指令将值12作为int类型压入栈。从这一刻开始,就已经不存在byte类型了。接下来第2行指令istore_2将这个栈顶元素赋值给变量a。请注意,变量a占用4个字节,他被隐式转换为int类型存放在栈的局部变量区了。
第7行iadd指令,该指令执行加法操作 ,结果是一个int类型。
第8行i2b指令,这个指令执行转换操作,将int类型转为byte类型,然后再将转换后的值压入栈。第9行,赋值给变量c;
接下来介绍一些有趣的事情,将一个long类型转换为short类型,java虚拟机是如何工作的。看如下代码:
package MyPackage;
public class TypeCast {
public static void main(String[] args) {
long a=12;
long b=13;
byte c=(byte)(a+b);
}
}
用javap反编译之后代码如下:
![](https://img-my.csdn.net/uploads/201301/08/1357614421_5708.jpg)
0:ldc2_w #16 这条指令比较复杂,简而言之,就是将值12压入操作数栈。
3: lstore_1 弹出操作数栈顶元素值12,赋值给变量a。
4: ldc2_w #18 将值13压入操作数栈。
7: lstore_3 弹出操作数栈顶元素值13,赋值给变量b。
8: lload_1 将变量a压入操作数栈。
9: lload_3 将变量a压入操作数栈。
10: ladd 弹出栈顶的两个元素,执行long类型的加法运算,将结果再压入栈。
11: l2i 弹出栈顶元素,转换为int类型,将转换后的值再次压入栈。
12: i2b 弹出栈顶元素,转换为byte类型,将转换后的值再次压入栈。
13: istore 5 弹出操作数栈顶元素值25,赋值给变量c。
15: return
这里的关键是l2i和i2b指令,这两个指令完成了从long类型到byte类型的转换。需要注意的是,在java虚拟机中以及.netCLR中,没有一个直接的指令用于将long类型转换为byte类型,只能先将long类型转换为int类型,然后在将int类型转换为byte类型。可能会造成产生一定的性能损失。