举例来说。
public void pushConstLdc() {// 范围 [-1,5]
int iconst = -1;
// 范围 [-128,127]
int bipush = 127;
// 范围 [-32768,32767]
int sipush= 32767;
// 其他 int
int ldc = 32768;
String aconst = null;
String IdcString = "沉默王二";
}
通过 jclasslib 看一下 pushConstLdc() 方法的字节码指令。
iconst_m1:将 -1 入栈。范围 [-1,5]。
bipush 127:将 127 入栈。范围 [-128,127]。
sipush 32767:将 32767 入栈。范围 [-32768,32767]。
ldc #6 <32768>:将常量池中下标为 6 的常量 32768 入栈。
aconst_null:将 null 入栈。
ldc #7 <沉默王二>:将常量池中下标为 7 的常量“沉默王二”入栈。
3)将栈顶的数据出栈并装入局部变量表中
主要是用来给局部变量赋值,这类指令主要以 store 的形式存在。
xstore_(x 为 i、l、f、d、a,n 默认为 0 到 3)
xstore(x 为 i、l、f、d、a)
明白了 xload_ 和 xload,再看 xstore_ 和 xstore 就会轻松得多,作用反了一下而已。
大家来想一个问题,为什么要有 xstore_ 和 xload_ 呢?它们的作用和 xstore n、xload n 不是一样的吗?
xstore_ 和 xstore n 的区别在于,前者相当于只有操作码,占用 1 个字节;后者相当于由操作码和操作数组成,操作码占 1 个字节,操作数占 2 个字节,一共占 3 个字节。
由于局部变量表中前几个位置总是非常常用,虽然 xstore_<n> 和 xload_<n> 增加了指令数量,但字节码的体积变小了!
举例来说。
public void store(int age, String name) {int temp = age + 2;
String str = name;
}
通过 jclasslib 看一下 store() 方法的字节码指令。
istore_3:从操作数中弹出一个整数,并把它赋值给局部变量表中索引为 3 的变量。
astore 4:从操作数中弹出一个引用数据类型,并把它赋值给局部变量表中索引为 4 的变量。
通过查看局部变量表就能关联上了。
02、算术指令算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新压入操作数栈。可以分为两类:整型数据的运算指令和浮点数据的运算指令。
需要注意的是,数据运算可能会导致溢出,比如两个很大的正整数相加,很可能会得到一个负数。但 Java 虚拟机规范中并没有对这种情况给出具体结果,因此程序是不会显式报错的。所以,大家在开发过程中,如果涉及到较大的数据进行加法、乘法运算的时候,一定要注意!
当发生溢出时,将会使用有符号的无穷大 Infinity 来表示;如果某个操作结果没有明确的数学定义的话,将会使用 NaN 值来表示。而且所有使用 NaN 作为操作数的算术操作,结果都会返回 NaN。
举例来说。
public void infinityNaN() {int i = 10;
double j = i / 0.0;
System.out.println(j); // Infinity
double d1 = 0.0;
double d2 = d1 / 0.0;
System.out.println(d2); // NaN
}
任何一个非零的数除以浮点数 0(注意不是 int 类型),可以想象结果是无穷大 Infinity 的。
把这个非零的数换成 0 的时候,结果又不太好定义,就用 NaN 值来表示。
Java 虚拟机提供了两种运算模式: