对象的创建与访问指令
创建指令
虽然类实例和数组都是对象,但Java虚拟机对类实例和数组的创建和操作使用了不同的字节码指令 创建类实例指令:new
它接收一个操作数,指向常量池的索引,表示要创建的类型,执行完成后,将对象的引用压入操作数栈
创建数组的指令:newarray,arewarray,multianewarray
newarray:创建基本类型数组 anewarray:创建引用类型数组 multianewarray:创建多维数组
字段访问指令
对象创建后,可能通过对象访问指令获得对象实例或数组实例中的字段或数组元素
访问字段(static字段,或称为类变量)的指令:getstatic,putstatic 访问类实例字段(非static字段,可实例变量):getfield,putfield 如:以getstatic指令为例,它含有一个操作数,为指向常量池的Fieldref索引,它的作用是获取Fieldref指定的对象或者值,并将其压入操作数栈
public void sayHello ( ) { System . out. println ( "hello" ) ; }
0 getstatic #8 < java/ lang/ System . out>
3 ldc #9 < hello>
5 invokevirtual #10 < java/ io/ PrintStream . println>
8 return
数组操作指令
数组操作指令主要有:xastore和xaload指令
把一个数组元素加载到操作数栈的命令:baload,caload,saload,iaload,laload,faload,daload,aaload 将一个操作数栈的值存储到数组元素中的指令:bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore 取数组长度的指令:arraylength
该指令弹出栈顶的数组元素,获取数组的长度,将长度压入栈
public void arrLength ( ) { double [ ] arr = new double [ 10 ] ; System . out. println ( arr. length) ; } 0 bipush 10 2 newarray 7 ( double ) 4 astore_15 getstatic #8 < java/ lang/ System . out> 8 aload_19 arraylength
10 invokevirtual #14 < java/ io/ PrintStream . println>
13 return
指令xload表示将数组的元素压栈,如saload,caload表示压入short数组和char数组,指令xaload在执行时,要求操作数中栈顶元素为数组索引i,栈顶顺位第二个元素为数组引用a,该指令会弹出栈顶这两个元素 ,并将a[i]重新压入堆栈 xastore则专门针对数组操作,以iastore为例,它用于给一个int数组的给定索引赋值,在iastore执行前,操作数栈顶需要以此准备3个元素:值、索引、数组引用,istore会弹出这三个值 ,并将值败给数组中指定索引的位置
public void setArray ( ) { int [ ] intArray = new int [ 10 ] ; intArray[ 3 ] = 20 ; System . out. println ( intArray[ 1 ] ) ; }
0 bipush 10 2 newarray 10 ( int ) 4 astore_15 aload_1 6 iconst_3 7 bipush 20 9 iastore
10 getstatic #8 < java/ lang/ System . out>
13 aload_1
14 iconst_1
15 iaload
16 invokevirtual #14 < java/ io/ PrintStream . println>
19 return
类型检查指令
检查类实例或数组类型的指令:instanceof,checkcast
指令checkcast用于检查类型强制转换是否可以进行,如果可以进行,那么checkcast指令不会改变操作数栈,否则它会抛出CassCastException异常 指令instanceof用来判断是否是某一个类的实例,它会将判断结果压入操作数栈
public String checkCast ( Object obj) { if ( obj instanceof String ) { return ( String ) obj; } else { return null ; } }
0 aload_11 instanceof #17 < java/ lang/ String > 4 ifeq 12 ( + 8 ) 7 aload_18 checkcast #17 < java/ lang/ String >
11 areturn
12 aconst_null
13 areturn
方法调用与返回指令
方法调用指令
方法调用指令:invokevirtual,invokeinterface,invokespecial,invokestatic,invokedynamic
invokevirtual:用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),支持多态,是最常见的方法分派方式 invokeinterface:用于调用接口方法 ,它会在运行时搜索由特定对象所实现的这个接口方法,并找出适合的方法进行调用
public void invoke3 ( ) { Thread t1 = new Thread ( ) ; ( ( Runnable ) t1) . run ( ) ; Comparable < Integer > com = null ; com. compareTo ( 123 ) ; } 0 new #4 < java/ lang/ Thread > 3 dup4 invokespecial #5 < java/ lang/ Thread . < init> > 7 astore_18 aload_19 invokeinterface #9 < java/ lang/ Runnable . run> count 1
14 aconst_null
15 astore_2
16 aload_2
17 bipush 123
19 invokestatic #10 < java/ lang/ Integer . valueOf>
22 invokeinterface #11 < java/ lang/ Comparable . compareTo> count 2
27 pop
28 return
invokespecial:调用一些特殊处理的实例方法,包含实例初始化方法(构造器),私有方法和父类方法,这些方法是静态类型绑定的,不会在调用时动态派发
public void invoke1 ( ) { Date date = new Date ( ) ; Thread t1 = new Thread ( ) ; super . toString ( ) ; methodPrivate ( ) ; } private void methodPrivate ( ) { }
0 new #2 < java/ util/ Date > 3 dup4 invokespecial #3 < java/ util/ Date . < init> > 7 astore_18 new #4 < java/ lang/ Thread >
11 dup
12 invokespecial #5 < java/ lang/ Thread . < init> >
15 astore_2
16 aload_0
17 invokespecial #6 < java/ lang/ Object . toString>
20 pop
21 aload_0
22 invokespecial #7 < com/ chapter10/ MethodInvokeReturnTest . methodPrivate>
25 return
invokestatic:调用命名类中的类方法(static方法) ,这是静态绑定的
public void invoke2 ( ) { methodStatic ( ) ; } public static void methodStatic ( ) { }
0 invokestatic #8 < com/ chapter10/ MethodInvokeReturnTest . methodStatic>
3 return
invokedynamic:调用动态绑定的方法,JDK1.7新加入的指令,用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法,前4条调用指令的分派逻辑都固化在java虚拟机内部,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的
方法返回指令
方法调用结束前,需要进行返回,方法返回指令是根据返回值的类型区分的
包括ireturn(boolan,byte,char,short和int类型使用),lreturn,freturn,dreturn和areturn 还有一条return指令供声明为void的方法,实例初始化方法以及类和接口的类初始化方法使用
通过ireturn指令,将当前函数操作数栈的顶层元素弹出,并将这个元素压入调用者函数的操作数栈中,所有在当前函数操作数栈中的其他元素都会被丢弃 如果当返回值是synchronized方法,那么还会执行一个隐含的monitorexit指令,退出临界区 最后,会丢弃当前方法的整个帧,恢复调用者的帧,并将控制权转交给调用者
public int returnInt ( ) { int i = 200 ; return i; }
0 sipush 200
3 istore_1
4 iload_1
5 ireturnpublic double returnDouble ( ) { return 0.0 ; } 0 dconst_0
1 dreturnpublic String returnString ( ) { return "hello,world" ; }
0 ldc #16 < hello, world>
2 areturnpublic int [ ] returnArr ( ) { return null ; } public float returnFloat ( ) { int i = 10 ; return i; }
0 bipush 10
2 istore_1
3 iload_1
4 i2f
5 freturnpublic byte returnByte ( ) { return 0 ; }
0 iconst_0
1 ireturn