一 4种方法调用的区别
java提供了两类基本的方法:
实例方法和类(静态)方法,区别:
(1)实例方法在被调用之前,需要一个实例,而类方法不用;
(2)实例方法使用动态绑定,而类方法使用静态绑定。
<一>从这几个方面考虑:常量池池入口类型、调用的方法的类型、操作数栈中的内容、从常量池获取的东西
1 invokespecial 常量池入口类型CONST_Methodref_info; 运行时按照对象的类来调用实例方法;超类方法(使用super关键字调用的方法)、私有方法、<init>方法,但不可以是<clinit>;栈中n-1个方法参数和objectref引用
2 invokeinterface 常量池入口类型CONST_InterfaceMethodref_info;静态方法-不可以是<init>或<clinit>;栈中n-1个方法参数和objectref引用
3 invokevirtual 常量池入口类型CONST_Methodref_info;不可以是<init>或<clinit>;栈中n-1个方法参数和objectref引用
4 invokestatic 常量池入口类型CONST_Methodref_info;调用静态方法;不可以是<init>或<clinit>;栈中n个方法参数
<二>invokevirtual和invokeinterface的区别:都需要获取此对象的实例方法的引用,然后在方法表中调用这个方法。
invokevirtual 为objectref所指的对象的类定位方法表,(从常量池入口获取的方法的表索引index和参数个数)访问index位置的直接引用。
invokeinterface
(1)定位方法表(2)在其中搜索是否有与所解析的方法的名称和描述符匹配的方法 (3)从接口调用一个方法,必须搜索对象的类的方法表来找到一个合适的方法。
invokeinterface比invokevirtual慢的原因
(1)invokevirtual 在常量池入口解析得到在方法表中的偏移量,根据偏移量定位方法。
invokeinterface 在常量池入口解析得到方法的名字和描述符,根据名字和描述符搜索整个方法表
(2)_quick技术,使用quik技术把invokevirtual操作码替换成invokevirtual_quik指令,虚拟机还把方法表偏移量和参数的个数放入每个invokevirtual指令后面的两个操作字节中;在操作码后面的字节流中直接使用方法表偏移量,节约了在解析过常量池入口中查找偏移量的时间。
<三>invokevirtual和invokespecial的区别:
invokespecial在编译时选择要调用的方法,它的选择是基于引用类型的(静态绑定) ACC_SUPER例外:在子类调用超类方法的时候发生。
invokevirtual在运行期间选择要调用的方法,它的选择是基于对象类型的(动态绑定)
public class Null { public static void greet(){ System.out.println("Hello World!"); } public static void main(String[] args) { Null x = null; x.greet(); ((Null)x).greet(); ((Null)null).greet(); } }
三条greet方法调用均会打印 “Hello World”,分析原因,使用javap -verbose 命令显示编译后的指令码,使用三条invokestatic指令完成操作。
public static void main(java.lang.String[]); Code: Stack=1, Locals=2, Args_size=1 0: aconst_null 1: astore_1 2: invokestatic #31; //Method greet:()V 5: invokestatic #31; //Method greet:()V 8: invokestatic #31; //Method greet:()V 11: return LineNumberTable: line 28: 0 line 29: 2 line 30: 5 line 31: 8 line 32: 11 LocalVariableTable: Start Length Slot Name Signature 0 12 0 args [Ljava/lang/String; 2 10 1 x Lnihao/dlut/Null;
二 栈和局部变量操作
1 把常量压入操作数栈、执行通用栈操作、在局部变量和操作数栈之间传递值。
2 常量入栈操作数来源:隐含在操作码内部、在字节流中如同操作数紧随操作码、常量池取;
3 wide指令
三 浮点运算
一个浮点数有四部分组成:符号 位数 指数 基数
规范化浮点数:针对尾数来说,它的小数点左边的数一定是0,紧接小数点右边的的数字一定不是0;
这一部分没有看懂:最大、最小、NaN、+0,-0、正负无穷大的二进制表示??
四 对象和数组
new : 新建一个实例,用默认值初始化对象实例变量、把新对象的引用压入栈。
五 控制流
lookupswitch和tableswitch的区别?
lookupswitch 需要逐个比较case中的值,才能确认;搜索完毕没有找到匹配项,执行默认程序分支偏移量;
tableswitch 先比较高值偏移量和低值偏移量,键值在他们之间的时候,用键值减去低值,得到分支偏移量列表中的偏移量。
六 异常
继承自Error和RuntimeException的异常都是unchecked,其他都是checked,需要方法处理(catch或者throws)
异常表中的每个入口都包括4部分信息:
起点
终点
将要跳转到的字节码序列中的pc指针偏移量
被捕获的异常类的常量池索引。
七 finally
------------------------------------------------------------------
参考《inside JVM 2e 》