Java字节码练习

问题描述:

编写一个简单的Java程序,里面需要涉及基本类型四则运行if 和for,然后分析对应的字节码。

编写自己的代码:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int a,b;
        String operation;
        String result="";
        System.out.println("请输入两个整数和一个操作符");
        a=scanner.nextInt();
        b=scanner.nextInt();
        operation=scanner.next();
        if(operation.equals("+"))result=OpAdd(a,b);
        if(operation.equals("-"))result=OpMinus(a,b);
        if(operation.equals("*"))result=OpMultiply(a,b);
        if(operation.equals("/"))result=OpDivide(a,b);
        System.out.println("运算结果为:"+result);

        for (int i = 1; i <= 5; i++) {
            System.out.println("i = " + i);
        }
    }
    public static String OpAdd(int p1, int p2){
        return String.valueOf(p1+p2);
    }
    public static String OpMinus (int p1,int p2){
        return String.valueOf(p1-p2);
    }
    public static String OpMultiply (int p1,int p2){
        return String.valueOf(p1*p2);
    }
    public static String OpDivide(int p1,int p2){
        return String.valueOf(p1/p2);
    }
}

使用Javap -c 指令后输出:
 

Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #7                  // class java/util/Scanner
       3: dup
       4: getstatic     #9                  // Field java/lang/System.in:Ljava/io/InputStream;
       7: invokespecial #15                 // Method java/util/Scanner."<init>":(Ljava/io/InputStream;)V
      10: astore_1
      11: ldc           #18                 // String
      13: astore        5
      15: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
      18: ldc           #24                 // String 请输入两个整数和一个操作符
      20: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      23: aload_1
      24: invokevirtual #32                 // Method java/util/Scanner.nextInt:()I
      27: istore_2
      28: aload_1
      29: invokevirtual #32                 // Method java/util/Scanner.nextInt:()I
      32: istore_3
      33: aload_1
      34: invokevirtual #36                 // Method java/util/Scanner.next:()Ljava/lang/String;
      37: astore        4
      39: aload         4
      41: ldc           #40                 // String +
      43: invokevirtual #42                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      46: ifeq          56
      49: iload_2
      50: iload_3
      51: invokestatic  #48                 // Method OpAdd:(II)Ljava/lang/String;
      54: astore        5
      56: aload         4
      58: ldc           #54                 // String -
      60: invokevirtual #42                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      63: ifeq          73
      66: iload_2
      67: iload_3
      68: invokestatic  #56                 // Method OpMinus:(II)Ljava/lang/String;
      71: astore        5
      73: aload         4
      75: ldc           #59                 // String *
      77: invokevirtual #42                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      80: ifeq          90
      83: iload_2
      84: iload_3
      85: invokestatic  #61                 // Method OpMultiply:(II)Ljava/lang/String;
      88: astore        5
      90: aload         4
      92: ldc           #64                 // String /
      94: invokevirtual #42                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      97: ifeq          107
     100: iload_2
     101: iload_3
     102: invokestatic  #66                 // Method OpDivide:(II)Ljava/lang/String;
     105: astore        5
     107: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
     110: aload         5
     112: invokedynamic #69,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
     117: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     120: iconst_1
     121: istore        6
     123: iload         6
     125: iconst_5
     126: if_icmpgt     148
     129: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
     132: iload         6
     134: invokedynamic #73,  0             // InvokeDynamic #1:makeConcatWithConstants:(I)Ljava/lang/String;
     139: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  public static java.lang.String OpMultiply(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: imul
       3: invokestatic  #76                 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
       6: areturn

  public static java.lang.String OpDivide(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: idiv
       3: invokestatic  #76                 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
       6: areturn
}

分析Java字节码: 

对于  1: invokespecial    #1             // Method java/lang/Object."<init>":()V

在字节码指令 1: invokespecial #1 中,我们可以解读如下:

  • 1 是指令的顺序标记,表示它是第1条指令。
  • invokespecial 是操作码,表示要调用一个特殊的方法。
  • #1 是常量池索引,它指向常量池中的一个方法符号引用,用于确定要调用的方法。
  • Method java/lang/Object."<init>":()V 是对被调用方法的描述,其中 java/lang/Object 是所属的类名,"<init>" 是构造函数的名称,() 表示该方法没有参数,V 表示该方法没有返回值。

对于 0: new    #7       // class java/util/Scanner

这条指令表示在堆上创建一个java.util.Scanner类的新实例。数字7是一个常量池索引,用于引用java.util.Scanner类。

对于   3: dup

该指令将栈顶的值复制一份并将其副本推入栈顶。这是为了在调用invokespecial指令之前,将栈上的引用留在栈上以供后续使用。

对于  15: getstatic   #20   // Field java/lang/System.out:Ljava/io/PrintStream; 

在字节码指令 `15: getstatic #20` 中,我们可以解读如下:

        - `15` 是指令的顺序标记,表示它是第15条指令。
        - `getstatic` 是操作码,表示获取静态字段的值。
        - `#20` 是常量池索引,它指向常量池中的一个字段符号引用,用于确定要获取值的静态字段。
        - `Field java/lang/System.out:Ljava/io/PrintStream;` 是对被获取的静态字段的描述,其中 `java/lang/System.out` 是字段所属的类和字段名,`Ljava/io/PrintStream;` 表示该字段的类型为 `java.io.PrintStream`。

因此,指令 `15: getstatic #20` 表示获取 `java/lang/System` 类中的静态字段 `out` 的值。在 Java 中,`System.out` 是一个 `PrintStream` 对象,可以用于输出文本到控制台。该指令将 `System.out` 的值推入栈顶,以供后续指令使用,比如在本例中用于调用 `println` 方法打印输出。

 简单来说:获取索引为#20的静态字段的值

对于   11: ldc           #18                 // String
 

在字节码指令 `11: ldc #18` 中,我们可以解读如下:

        - `11` 是指令的顺序标记,表示它是第11条指令。
        - `ldc` 是操作码,表示将常量推送至栈顶
        - `#18` 是常量池索引,它指向常量池中的一个字符串常量。

因此,指令 `11: ldc #18` 表示将常量池中索引为 #18 的字符串常量推送至栈顶。该指令在这个上下文中用于加载字符串常量。

对于 28: aload_1
        29: invokevirtual #32                 // Method java/util/Scanner.nextInt:()I
        32: istore_3

1. `28: aload_1` 表示局部变量表中索引为 1 的值加载到操作数栈顶
           - `aload_1` 是操作码,表示加载引用类型的局部变量到操作数栈顶。
           - 在这里,索引 1 对应的是局部变量表中的第 2 个位置,即 `main` 方法的参数 `String[]` 类型的局部变量。
           - 通过 `aload_1` 指令,将 `String[]` 类型的局部变量加载到操作数栈顶。

2. `29: invokevirtual #32` 表示调用对象的实例方法
           - `invokevirtual` 是操作码,表示调用虚方法。
           - `#32` 是常量池索引,它指向常量池中的一个方法符号引用。
           - 该指令调用位于操作数栈顶的对象上的方法,方法的符号引用由常量池索引指定。
           - 在这里,`#32` 指向常量池中的 `java/util/Scanner.nextInt` 方法。

3. `32: istore_3` 表示操作数栈顶的值存储到局部变量表的索引 3 处
           - `istore_3` 是操作码,表示将整型值存储到局部变量表的索引 3 处。
           - 在这里,将操作数栈顶的整型值存储到局部变量表中的索引 3 处。

综上所述,指令序列 `28: aload_1`、`29: invokevirtual #32` 和 `32: istore_3` 的作用是将 `main` 方法的参数 `String[]` 类型的局部变量加载到操作数栈顶,然后调用 `java.util.Scanner.nextInt()` 方法获取一个整数值,并将该整数值存储到局部变量表的索引 3 处。

对于   88: astore 5

在字节码指令 `88: astore 5` 中,我们可以解读如下:

        - `88` 是指令的顺序标记,表示它是第88条指令。
        - `astore` 是操作码,表示将操作数栈顶的引用类型值存储到局部变量表中。
        - `5` 是局部变量表索引,指示要存储值的位置。

指令 `astore 5` 的作用是将操作数栈顶的引用类型值存储到局部变量表的索引为5的位置。在这个上下文中,索引5对应的是一个字符串变量。

根据给出的字节码片段,`88: astore 5` 的目的是将操作数栈顶的引用类型值(在这里是存储操作符的字符串变量)存储到局部变量表中的索引5处。这样可以在后续的代码中使用该存储的值进行进一步的操作或判断。

总结 :

通过实验我学会了怎么使用javap -c 查看Java字节码,并且对字节码中的部分代码进行了分析。这使我对JVM中的操作和查看代码运行的规则有了更深入的了解,使我得到了较大的提升。在后续中我也将多次使用这样的方法来查看程序的可行性,使用这样的方法来提高对代码的效率和优化代码。

一名来自北京印刷学院计科的学生

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值