我要悄悄学习 Java 字节码指令,在成为技术大佬的路上一去不复返(3)

简介: 我要悄悄学习 Java 字节码指令,在成为技术大佬的路上一去不复返

04、对象的创建和访问指令


Java 是一门面向对象的编程语言,那么 Java 虚拟机是如何从字节码层面进行支持的呢?


1)创建指令


数组也是一种对象,但它创建的字节码指令和普通的对象不同。创建数组的指令有三种:


newarray:创建基本数据类型的数组

anewarray:创建引用类型的数组

multianewarray:创建多维数组

普通对象的创建指令只有一个,就是 new,它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型。


举例来说。


public void newObject() {
    String name = new String("沉默王二");
    File file = new File("无愁河的浪荡汉子.book");
    int [] ages = {};
}


通过 jclasslib 看一下 newObject() 方法的字节码指令。


image.png


new #13 <java/lang/String>,创建一个 String 对象。

new #15 <java/io/File>,创建一个 File 对象。

newarray 10 (int),创建一个 int 类型的数组。

2)字段访问指令


字段可以分为两类,一类是成员变量,一类是静态变量(static 关键字修饰的),所以字段访问指令可以分为两类:


访问静态变量:getstatic、putstatic。

访问成员变量:getfield、putfield,需要创建对象后才能访问。

举例来说。


public class Writer {
    private String name;
    static String mark = "作者";
    public static void main(String[] args) {
        print(mark);
        Writer w = new Writer();
        print(w.name);
    }
    public static void print(String arg) {
        System.out.println(arg);
    }
}



通过 jclasslib 看一下 main() 方法的字节码指令。


image.png


getstatic #2 <com/itwanger/jvm/Writer.mark>,访问静态变量 mark

getfield #6 <com/itwanger/jvm/Writer.name>,访问成员变量 name


05、方法调用和返回指令


方法调用指令有 5 个,分别用于不同的场景:


invokevirtual:用于调用对象的成员方法,根据对象的实际类型进行分派,支持多态。

invokeinterface:用于调用接口方法,会在运行时搜索由特定对象实现的接口方法进行调用。

invokespecial:用于调用一些需要特殊处理的方法,包括构造方法、私有方法和父类方法。

invokestatic:用于调用静态方法。

invokedynamic:用于在运行时动态解析出调用点限定符所引用的方法,并执行。

举例来说。


public class InvokeExamples {
    private void run() {
        List ls = new ArrayList();
        ls.add("难顶");
        ArrayList als = new ArrayList();
        als.add("学不动了");
    }
    public static void print() {
        System.out.println("invokestatic");
    }
    public static void main(String[] args) {
        print();
        InvokeExamples invoke = new InvokeExamples();
        invoke.run();
    }
}



我们用 javap -c InvokeExamples.class 来反编译一下。


Compiled from "InvokeExamples.java"
public class com.itwanger.jvm.InvokeExamples {
  public com.itwanger.jvm.InvokeExamples();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  private void run();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String 难顶
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: new           #2                  // class java/util/ArrayList
      20: dup
      21: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
      24: astore_2
      25: aload_2
      26: ldc           #6                  // String 学不动了
      28: invokevirtual #7                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
      31: pop
      32: return
  public static void print();
    Code:
       0: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #9                  // String invokestatic
       5: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #11                 // Method print:()V
       3: new           #12                 // class com/itwanger/jvm/InvokeExamples
       6: dup
       7: invokespecial #13                 // Method "<init>":()V
      10: astore_1
      11: aload_1
      12: invokevirtual #14                 // Method run:()V
      15: return
}



InvokeExamples 类有 4 个方法,包括缺省的构造方法在内。


1)InvokeExamples() 构造方法中


缺省的构造方法内部会调用超类 Object 的初始化构造方法:


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


2)成员方法 run() 中


invokeinterface #5,  2  // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z


由于 ls 变量的引用类型为接口 List,所以 ls.add() 调用的是 invokeinterface 指令,等运行时再确定是不是接口 List 的实现对象 ArrayList 的 add() 方法。


invokevirtual #7 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z


由于 als 变量的引用类型已经确定为 ArrayList,所以 als.add() 方法调用的是 invokevirtual 指令。


3)main() 方法中


invokestatic  #11 // Method print:()V


print() 方法是静态的,所以调用的是 invokestatic 指令。


方法返回指令根据方法的返回值类型进行区分,常见的返回指令见下图。


image.png



相关文章
|
2月前
|
安全 Java API
JDK 11中的动态类文件常量:探索Java字节码的灵活性与动态性
在JDK 11中,Java语言引入了一个新的特性,允许在运行时动态地修改类文件常量。这一特性为Java开发者提供了更大的灵活性,使他们能够根据需要在运行时更改类文件中的常量值。本文将深入探讨动态类文件常量的工作原理、优点、限制以及在实际项目中的应用。
48 11
|
7月前
|
Oracle Java 编译器
Java 中如何生成字节码?
Java 中如何生成字节码?
|
7月前
|
前端开发 Java 编译器
Java 字节码
Java 字节码
28 1
|
9天前
|
存储 缓存 安全
Java并发基础之互斥同步、非阻塞同步、指令重排与volatile
在Java中,多线程编程常常涉及到共享数据的访问,这时候就需要考虑线程安全问题。Java提供了多种机制来实现线程安全,其中包括互斥同步(Mutex Synchronization)、非阻塞同步(Non-blocking Synchronization)、以及volatile关键字等。 互斥同步(Mutex Synchronization) 互斥同步是一种基本的同步手段,它要求在任何时刻,只有一个线程可以执行某个方法或某个代码块,其他线程必须等待。Java中的synchronized关键字就是实现互斥同步的常用手段。当一个线程进入一个synchronized方法或代码块时,它需要先获得锁,如果
24 0
|
5月前
|
安全 算法 Java
从零开发基于ASM字节码的Java代码混淆插件XHood
因在公司负责基础框架的开发设计,所以针对框架源代码的保护工作比较重视,之前也加入了一系列保护措施,例如自定义classloader加密保护,授权license保护等,但都是防君子不防小人,安全等级还比较低,经过调研各类加密混淆措施后,决定自研混淆插件,自主可控,能够贴合实际情况进行定制化,达到框架升级后使用零感知,零影响
70 1
从零开发基于ASM字节码的Java代码混淆插件XHood
|
2月前
|
缓存 监控 Java
Javaassist:编写字节码,改变Java的命运
Javaassist:编写字节码,改变Java的命运
28 0
|
3月前
|
Java
Idea如何方便的查看Java字节码文件😁
Idea如何方便的查看Java字节码文件😁
56 0
|
4月前
|
Java Windows
查看java文件汇编代码与字节码
查看java文件汇编代码与字节码
46 0
|
4月前
|
存储 算法 Java
Java字节码编程之非常好用的javassist
Java字节码编程之非常好用的javassist
78 0
|
5月前
|
存储 Java 关系型数据库
如何用JAVA调用服务器系统指令
如何用JAVA调用服务器系统指令
40 1