从字节码看java中 this 的隐式传参

时间:2022-02-12 17:07:11

  从字节码看java中 this 隐式传参具体体现(和python中的self如出一辙,但是比python中藏得更深),也发现了 static 与 非 static 方法的区别所在!

  static与非static方法都是存储java的方法区。在static 方法中,没有this引用,因此无法使用当前类中所定义的变量,而非static方法则会默认传入this。我们今天就从另一个角度来真实看一下这个答案吧!

来个例子,并将其反编译为可视代码:

public class Hello {

    private final int ii;

    public Hello(int a) {
        ii = a;
    }

    public static void main(String[] args) throws Exception {
        sayHelloStatic("ok");
    }

    public void sayHello(String word) {
        System.out.println("hello, " + word);
    }
    public static void sayHelloStatic(String word) {
        System.out.println("static hello, " + word);
    }
}

反汇编命令:

javap -verbose Hello.class

 反汇编结果:

Classfile /D:/xx/target/classes/com/xx/api/Hello.class
  Last modified 2018-11-8; size 1069 bytes
  MD5 checksum 9d39cd9d4e95588a73c059a4e69f01e8
  Compiled from "Hello.java"
public class com.xx.api.Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #14.#38        // java/lang/Object."<init>":()V
   #2 = Fieldref           #13.#39        // com/xx/api/Hello.ii:I
   #3 = String             #40            // ok
   #4 = Methodref          #13.#41        // com/xx/api/Hello.sayHelloStatic:(Ljava/lang/String;)V
   #5 = Fieldref           #42.#43        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Class              #44            // java/lang/StringBuilder
   #7 = Methodref          #6.#38         // java/lang/StringBuilder."<init>":()V
   #8 = String             #45            // hello,
   #9 = Methodref          #6.#46         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #10 = Methodref          #6.#47         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #11 = Methodref          #48.#49        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #12 = String             #50            // static hello,
  #13 = Class              #51            // com/xx/api/Hello
  #14 = Class              #52            // java/lang/Object
  #15 = Utf8               ii
  #16 = Utf8               I
  #17 = Utf8               <init>
  #18 = Utf8               (I)V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               Lcom/xx/api/Hello;
  #24 = Utf8               a
  #25 = Utf8               main
  #26 = Utf8               ([Ljava/lang/String;)V
  #27 = Utf8               args
  #28 = Utf8               [Ljava/lang/String;
  #29 = Utf8               Exceptions
  #30 = Class              #53            // java/lang/Exception
  #31 = Utf8               sayHello
  #32 = Utf8               (Ljava/lang/String;)V
  #33 = Utf8               word
  #34 = Utf8               Ljava/lang/String;
  #35 = Utf8               sayHelloStatic
  #36 = Utf8               SourceFile
  #37 = Utf8               Hello.java
  #38 = NameAndType        #17:#54        // "<init>":()V
  #39 = NameAndType        #15:#16        // ii:I
  #40 = Utf8               ok
  #41 = NameAndType        #35:#32        // sayHelloStatic:(Ljava/lang/String;)V
  #42 = Class              #55            // java/lang/System
  #43 = NameAndType        #56:#57        // out:Ljava/io/PrintStream;
  #44 = Utf8               java/lang/StringBuilder
  #45 = Utf8               hello,
  #46 = NameAndType        #58:#59        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #47 = NameAndType        #60:#61        // toString:()Ljava/lang/String;
  #48 = Class              #62            // java/io/PrintStream
  #49 = NameAndType        #63:#32        // println:(Ljava/lang/String;)V
  #50 = Utf8               static hello,
  #51 = Utf8               com/xx/api/Hello
  #52 = Utf8               java/lang/Object
  #53 = Utf8               java/lang/Exception
  #54 = Utf8               ()V
  #55 = Utf8               java/lang/System
  #56 = Utf8               out
  #57 = Utf8               Ljava/io/PrintStream;
  #58 = Utf8               append
  #59 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #60 = Utf8               toString
  #61 = Utf8               ()Ljava/lang/String;
  #62 = Utf8               java/io/PrintStream
  #63 = Utf8               println
{
  public com.xx.api.Hello(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iload_1
         6: putfield      #2                  // Field ii:I
         9: return
      LineNumberTable:
        line 14: 0
        line 15: 4
        line 16: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/xx/api/Hello;
            0      10     1     a   I

  public static void main(java.lang.String[]) throws java.lang.Exception;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: ldc           #3                  // String ok
         2: invokestatic  #4                  // Method sayHelloStatic:(Ljava/lang/String;)V
         5: return
      LineNumberTable:
        line 42: 0
        line 45: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  args   [Ljava/lang/String;
    Exceptions:
      throws java.lang.Exception

  public void sayHello(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #6                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #8                  // String hello,
        12: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_1
        16: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 48: 0
        line 49: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  this   Lcom/xx/api/Hello;
            0      26     1  word   Ljava/lang/String;

  public static void sayHelloStatic(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #6                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #12                 // String static hello,
        12: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 51: 0
        line 52: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  word   Ljava/lang/String;
}
SourceFile: "Hello.java"

 


我们从字节码文件中可以看出来:

  sayHello(String word) 和 sayHelloStatic(String word) 都只有一个参数,但是在字节码中:

    sayHello(String word) 中引用 word 时使用了 15: aload_1, 可以看出其加载的变量是在 slot1中,而 slot0中即保存了 this 。

    sayHelloStatic(String word) 中引用 word 时使用了 15: aload_0, 可以看出静态方法中,直接将变量存在了 slot0中,因此无法使用 this 中的变量了。

            在LocalVariableTable 本地变量表中,可以清楚地看到,哪个slot存储了什么变量。

   当要操作当前类的变量或方法时,需要先 aload_0, 然后再做相关操作!