如何通过字节码看java中this的隐式传参

2023-06-15,

这篇文章将为大家详细讲解有关如何通过字节码看javathis的隐式传参,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

前言

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

static与非static方法都是存储java的方法区。在static 方法中,没有this引用,因此无法使用当前类中所定义的变量,而非static方法则会默认传入this。

概述

  • this关键字,是一个隐式参数,另外一个隐式参数是super。

  • this用于方法里面,用于方法外面无意义。

  • this关键字一般用于set方法和构造方法中。

我们今天就从另一个角度来真实看一下这个答案吧!

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

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
  10  0 this Lcom/xx/api/Hello;
  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
  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
  26  0 this Lcom/xx/api/Hello;
  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
  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 中的变量了。

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

关于“如何通过字节码看java中this的隐式传参”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

《如何通过字节码看java中this的隐式传参.doc》

下载本文的Word格式文档,以方便收藏与打印。