通過字節(jié)碼看java中this的隱式傳參詳解
前言
從字節(jié)碼看java中 this 隱式傳參具體體現(xiàn)(和python中的self如出一轍,但是比python中藏得更深),也發(fā)現(xiàn)了 static 與 非 static 方法的區(qū)別所在!
static與非static方法都是存儲java的方法區(qū)。在static 方法中,沒有this引用,因此無法使用當(dāng)前類中所定義的變量,而非static方法則會默認(rèn)傳入this。
概述
- this關(guān)鍵字,是一個隱式參數(shù),另外一個隱式參數(shù)是super。
- this用于方法里面,用于方法外面無意義。
- this關(guān)鍵字一般用于set方法和構(gòu)造方法中。
我們今天就從另一個角度來真實看一下這個答案吧!
來個例子,并將其反編譯為可視代碼:
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
反匯編結(jié)果:
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"
我們從字節(jié)碼文件中可以看出來:
sayHello(String word) 和 sayHelloStatic(String word) 都只有一個參數(shù),但是在字節(jié)碼中:
sayHello(String word) 中引用 word 時使用了 15: aload_1, 可以看出其加載的變量是在 slot1中,而 slot0中即保存了 this 。
sayHelloStatic(String word) 中引用 word 時使用了 15: aload_0, 可以看出靜態(tài)方法中,直接將變量存在了 slot0中,因此無法使用 this 中的變量了。
當(dāng)要操作當(dāng)前類的變量或方法時,需要先 aload_0, 然后再做相關(guān)操作!
總結(jié):
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
SpringBoot如何手寫一個starter并使用這個starter詳解
starter是SpringBoot中的一個新發(fā)明,它有效的降低了項目開發(fā)過程的復(fù)雜程度,對于簡化開發(fā)操作有著非常好的效果,下面這篇文章主要給大家介紹了關(guān)于SpringBoot如何手寫一個starter并使用這個starter的相關(guān)資料,需要的朋友可以參考下2022-12-12
如何利用Spring?MVC實現(xiàn)RESTful風(fēng)格
這篇文章主要介紹了如何利用Spring?MVC實現(xiàn)RESTful風(fēng)格,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java實現(xiàn)excel大數(shù)據(jù)量導(dǎo)入
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)excel大數(shù)據(jù)量導(dǎo)入,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-08-08
springboot項目打包發(fā)布部署的過程及jar和war的區(qū)別
Spring Boot使用了內(nèi)嵌容器,因此它的部署方式也變得非常簡單靈活,可以將Spring Boot項目打包成JAR包來獨(dú)立運(yùn)行,Spring Boot項目既可以生成WAR包發(fā)布,也可以生成JAR包發(fā)布,那么它們有什么區(qū)別呢2022-11-11
MyBatis3源碼解析之如何獲取數(shù)據(jù)源詳解
用myBatis3與spring整合的時候,我們可以通過多種方式獲取數(shù)據(jù)源,下面這篇文章主要給大家介紹了關(guān)于MyBatis3源碼解析之如何獲取數(shù)據(jù)源的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06

