向最接近数舍入:在进行浮点数运算时,所有的结果都必须舍入到一个适当的精度,不是特别精确的结果必须舍入为可被表示的最接近的精确值,如果有两种可表示的形式与该值接近,将优先选择最低有效位为零的(类似四舍五入)。
向零舍入:将浮点数转换为整数时,采用该模式,该模式将在目标数值类型中选择一个最接近但是不大于原值的数字作为最精确的舍入结果(类似取整)。
我把所有的算术指令列一下:
加法指令:iadd、ladd、fadd、dadd
减法指令:isub、lsub、fsub、dsub
乘法指令:imul、lmul、fmul、dmul
除法指令:idiv、ldiv、fdiv、ddiv
求余指令:irem、lrem、frem、drem
自增指令:iinc
举例来说。
public void calculate(int age) {int add = age + 1;
int sub = age - 1;
int mul = age * 2;
int div = age / 3;
int rem = age % 4;
age++;
age--;
}
通过 jclasslib 看一下 calculate() 方法的字节码指令。
iadd,加法
isub,减法
imul,乘法
idiv,除法
irem,取余
iinc,自增的时候 +1,自减的时候 -1
03、类型转换指令可以分为两种:
1)宽化,小类型向大类型转换,比如 int–>long–>float–>double,对应的指令有:i2l、i2f、i2d、l2f、l2d、f2d。
从 int 到 long,或者从 int 到 double,是不会有精度丢失的;
从 int、long 到 float,或者 long 到 double 时,可能会发生精度丢失;
从 byte、char 和 short 到 int 的宽化类型转换实际上是隐式发生的,这样可以减少字节码指令,毕竟字节码指令只有 256 个,占一个字节。
2)窄化,大类型向小类型转换,比如从 int 类型到 byte、short 或者 char,对应的指令有:i2b、i2s、i2c;从 long 到 int,对应的指令有:l2i;从 float 到 int 或者 long,对应的指令有:f2i、f2l;从 double 到 int、long 或者 float,对应的指令有:d2i、d2l、d2f。
窄化很可能会发生精度丢失,毕竟是不同的数量级;
但 Java 虚拟机并不会因此抛出运行时异常。
举例来说。
public void updown() {int i = 10;
double d = i;
float f = 10f;
long ong = (long)f;
}
通过 jclasslib 看一下 updown() 方法的字节码指令。
i2d,int 宽化为 double
f2l, float 窄化为 long
04、对象的创建和访问指令Java 是一门面向对象的编程语言,那么 Java 虚拟机是如何从字节码层面进行支持的呢?
1)创建指令
数组也是一种对象,但它创建的字节码指令和普通的对象不同。创建数组的指令有三种:
newarray:创建基本数据类型的数组
anewarray:创建引用类型的数组
multianewarray:创建多维数组
普通对象的创建指令只有一个,就是 new,它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型。
举例来说。
public void newObject() {String name = new String("沉默王二");
File file = new File("无愁河的浪荡汉子.book");
int [] ages = {};
}
通过 jclasslib 看一下 newObject() 方法的字节码指令。
new #13 ,创建一个 String 对象。
new #15 ,创建一个 File 对象。
newarray 10 (int),创建一个 int 类型的数组。
2)字段访问指令
字段可以分为两类,一类是成员变量,一类是静态变量(static 关键字修饰的),所以字段访问指令可以分为两类:
访问静态变量:getstatic、putstatic。
访问成员变量:getfield、putfield,需要创建对象后才能访问。