字节码指令(中篇)

上一篇

字节码指令

四、类型检查指令

检查类实例或数组类型的指令:instanceof、checkcast.
·指令checkcast用于检查类型强制转换是否可以进行。如果可以进行,那么checkcast指令不会改变操作数栈,否则它会抛出ClassCastException异常。
·指令instanceof用来判断给定对象是否是某一个类的实例,它会将判断结果压入操作数栈。

在这里插入图片描述

方法调用指令

: invokevirtual、invokeinterface、invokespecial、invokestatic . invokedynamic
以下5条指令用于方法调用:
invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),支持多态。这也是Java语言中最常见的方法分派方式。
invokeinterface指令用于调用接口方法,它会在运行时搜索由特定对象所实现的这个接口方法,并找出适合的方法进行调用。
invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法(构造器)、私有方法和父类方法。这些方法都是静态类型绑定的,不会在调用时进行动态派发。
. invokestatic指令用于调用命名类中的类方法(static方法。这是静态绑定的。
invokedynamic:调用动态绑定的方法,这个是JDK 1.7后新加入的指令。用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法。前面4条调用指令的分派逻辑都固化在 java虚拟机内部,而
invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。
代码实例

  //方法调用指令:invokespecial:静态分派
    public void invoke1(){
        //情况1:类实例构造器方法:<init>()
        Date date = new Date();

        Thread t1 = new Thread();
        //情况2:父类的方法
        super.toString();
        //情况3:私有方法
        methodPrivate();
    }

    private void methodPrivate(){

    }
    //字节码    //    //    //    //    //    //    //
     0 new #2 <java/util/Date>
 3 dup
 4 invokespecial #3 <java/util/Date.<init>>
 7 astore_1
 8 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 <main/likou/MethodInvokeReturnTest.methodPrivate>
25 return
   //方法调用指令:invokestatic:静态分派
    public void invoke2(){
        methodStatic();
    }
    public static void methodStatic(){

    }
    //字节码//字节码//字节码//字节码//字节码//字节码//字节码
 0 invokestatic #8 <main/likou/MethodInvokeReturnTest.methodStatic>
3 return
//invokeinterface和invokestaticy优先考虑后者
  //方法调用指令: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 dup
 4 invokespecial #5 <java/lang/Thread.<init>>
 7 astore_1
 8 aload_1
 9 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

    //方法调用指令:invokeVirtual:动态分派
    public void invoke4(){
        System.out.println("hello");

        Thread t1 = null;
        t1.run();
    }
//字节码//字节码//字节码//字节码//字节码//字节码//字节码
0 getstatic #12 <java/lang/System.out>
 3 ldc #13 <hello>
 5 invokevirtual #14 <java/io/PrintStream.println>
 8 aconst_null
 9 astore_1
10 aload_1
11 invokevirtual #15 <java/lang/Thread.run>
14 return

方法返回指令

方法调用结束前,需要进行返回。方法返回指令是根据返回值的类型区分的。包括ireturn(当返回值是 boolean、byte、char、short和int类型时使用)、lreturn、freturn、dreturn和areturnI
在这里插入图片描述

·另外还有一条return指令供声明为 void的方法、实例初始化方法以及类和接口的类初始化方法使用。

举例:
通过ireturn指令,将当前函数操作数栈的顶层元素弹出,并将这个元素压入调用者函数的操作数栈中(因为调用者非常关心函数的返回值),所有在当前函数操作数栈中的其他元素都会被丢弃。
如果当前返回的是synchronized方法,那么还会执行一个隐含的monitorexit指令,退出临界区。
最后,会丢弃当前方法的整个帧,恢复调用者的帧,并将控制权转交给调用者。

public byte returnByte(){
        return 0;
    }

    public void methodReturn(){
        int i = returnByte();
    }
//字节码//字节码//字节码//字节码//字节码//字节码//字节码//字节码//字
0 iconst_0
1 ireturn


0 aload_0
1 invokevirtual #17 <main/likou/MethodInvokeReturnTest.returnByte>
4 istore_1
5 return

操作数栈管理指令

详细教程
如同操作一个普通数据结构中的堆栈那样,JVM提供的操作数栈管理指令,可以用于直接操作操作数栈的指令。

这类指令包括如下内容:
·将一个或两个元素从栈顶弹出,并且直接废弃:pop, pop2;
·复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶: dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2;
·将栈最顶端的两个Slot数值位置交换:swap。Java虚拟机没有提供交换两个64位数据类型(long、double)数值的指令。
·指令nop,是一个非常特殊的指令,它的字节码为0x00。和汇编语言中的nop一样,它表示什么都不做。这条指令一般可用于调试、占位等。
这些指令属于通用型,对栈的压入或者弹出无需指明数据类型。

说明;
·不带_x的指令是复制栈顶数据并压入栈顶。包括两个指令dup和dup2.dup的系数代表要复制的slot个数
.dup开头的指令用于复制1个Slot的数据。例如1个int或1个reference类型数据
.dup2开头的指令用于复制2个Slot的数据。例如1个long,或2个int,或1个int+1个float类型数据
·带_x的指令是复制栈顶数据并插入栈顶以下的某个位置。共有4个指令, dup_x1, dup2_x1,dup_x2,dup2_x2.对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置。因此
. dup_x1插入位置:1+1=2,即栈顶2个Slot下面.
dup_x2插入位置:1+2=3,即栈顶3个S1ot下面.
dup2_x1插入位置:2+1=3,即栈顶3个S1ot下面.
dup2_×2插入位置:2+2=4,即栈顶4个Slot下面
在这里插入图片描述
pop:将栈顶的1个slot数值出栈。例如1个short类型数值
pop2:将栈顶的2个Slot数值出栈。例如1个pouble类型数值,或者2个int类型数值

测试代码

public class StackOperateTest {

    public void print(){
        Object obj = new Object();
//        String info = obj.toString();
        obj.toString();
    }
    //类似的
    public void foo(){
        bar();
    }
    public long bar(){
        return 0;
    }

    public long nextIndex() {
        return index++;
    }

    private long index = 0;
}



/字节码
//1.print
 0 new #3 <java/lang/Object>
 3 dup
 4 invokespecial #1 <java/lang/Object.<init>>
 7 astore_1
 8 aload_1
 9 invokevirtual #4 <java/lang/Object.toString>
12 pop
13 return

//2.foo
0 aload_0
1 invokevirtual #5 <main/likou/StackOperateTest.bar>
4 pop2
5 return

//

在这里插入图片描述
下一篇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月屯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值