欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

通过字节码看java中this的隐式传参详解

程序员文章站 2024-02-29 13:09:58
前言 从字节码看java中 this 隐式传参具体体现(和python中的self如出一辙,但是比python中藏得更深),也发现了 static 与 非 static...

前言

从字节码看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, 然后再做相关操作!

总结:

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。