通过字节码看java中this的隐式传参详解
前言
从字节码看java中this隐式传参具体体现(和python中的self如出一辙,但是比python中藏得更深),也发现了static与非static方法的区别所在!
static与非static方法都是存储java的方法区。在static方法中,没有this引用,因此无法使用当前类中所定义的变量,而非static方法则会默认传入this。
概述
- this关键字,是一个隐式参数,另外一个隐式参数是super。
- this用于方法里面,用于方法外面无意义。
- this关键字一般用于set方法和构造方法中。
我们今天就从另一个角度来真实看一下这个答案吧!
来个例子,并将其反编译为可视代码:
publicclassHello{ privatefinalintii; publicHello(inta){ ii=a; } publicstaticvoidmain(String[]args)throwsException{ sayHelloStatic("ok"); } publicvoidsayHello(Stringword){ System.out.println("hello,"+word); } publicstaticvoidsayHelloStatic(Stringword){ System.out.println("statichello,"+word); } }
反汇编命令:
javap-verboseHello.class
反汇编结果:
Classfile/D:/xx/target/classes/com/xx/api/Hello.class Lastmodified2018-11-8;size1069bytes MD5checksum9d39cd9d4e95588a73c059a4e69f01e8 Compiledfrom"Hello.java" publicclasscom.xx.api.Hello minorversion:0 majorversion:52 flags:ACC_PUBLIC,ACC_SUPER Constantpool: #1=Methodref#14.#38//java/lang/Object."":()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." ":()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//statichello, #13=Class#51//com/xx/api/Hello #14=Class#52//java/lang/Object #15=Utf8ii #16=Utf8I #17=Utf8 #18=Utf8(I)V #19=Utf8Code #20=Utf8LineNumberTable #21=Utf8LocalVariableTable #22=Utf8this #23=Utf8Lcom/xx/api/Hello; #24=Utf8a #25=Utf8main #26=Utf8([Ljava/lang/String;)V #27=Utf8args #28=Utf8[Ljava/lang/String; #29=Utf8Exceptions #30=Class#53//java/lang/Exception #31=Utf8sayHello #32=Utf8(Ljava/lang/String;)V #33=Utf8word #34=Utf8Ljava/lang/String; #35=Utf8sayHelloStatic #36=Utf8SourceFile #37=Utf8Hello.java #38=NameAndType#17:#54//" ":()V #39=NameAndType#15:#16//ii:I #40=Utf8ok #41=NameAndType#35:#32//sayHelloStatic:(Ljava/lang/String;)V #42=Class#55//java/lang/System #43=NameAndType#56:#57//out:Ljava/io/PrintStream; #44=Utf8java/lang/StringBuilder #45=Utf8hello, #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=Utf8statichello, #51=Utf8com/xx/api/Hello #52=Utf8java/lang/Object #53=Utf8java/lang/Exception #54=Utf8()V #55=Utf8java/lang/System #56=Utf8out #57=Utf8Ljava/io/PrintStream; #58=Utf8append #59=Utf8(Ljava/lang/String;)Ljava/lang/StringBuilder; #60=Utf8toString #61=Utf8()Ljava/lang/String; #62=Utf8java/io/PrintStream #63=Utf8println { publiccom.xx.api.Hello(int); descriptor:(I)V flags:ACC_PUBLIC Code: stack=2,locals=2,args_size=2 0:aload_0 1:invokespecial#1//Methodjava/lang/Object." ":()V 4:aload_0 5:iload_1 6:putfield#2//Fieldii:I 9:return LineNumberTable: line14:0 line15:4 line16:9 LocalVariableTable: StartLengthSlotNameSignature 100thisLcom/xx/api/Hello; 101aI publicstaticvoidmain(java.lang.String[])throwsjava.lang.Exception; descriptor:([Ljava/lang/String;)V flags:ACC_PUBLIC,ACC_STATIC Code: stack=1,locals=1,args_size=1 0:ldc#3//Stringok 2:invokestatic#4//MethodsayHelloStatic:(Ljava/lang/String;)V 5:return LineNumberTable: line42:0 line45:5 LocalVariableTable: StartLengthSlotNameSignature 60args[Ljava/lang/String; Exceptions: throwsjava.lang.Exception publicvoidsayHello(java.lang.String); descriptor:(Ljava/lang/String;)V flags:ACC_PUBLIC Code: stack=3,locals=2,args_size=2 0:getstatic#5//Fieldjava/lang/System.out:Ljava/io/PrintStream; 3:new#6//classjava/lang/StringBuilder 6:dup 7:invokespecial#7//Methodjava/lang/StringBuilder." ":()V 10:ldc#8//Stringhello, 12:invokevirtual#9//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15:aload_1 16:invokevirtual#9//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19:invokevirtual#10//Methodjava/lang/StringBuilder.toString:()Ljava/lang/String; 22:invokevirtual#11//Methodjava/io/PrintStream.println:(Ljava/lang/String;)V 25:return LineNumberTable: line48:0 line49:25 LocalVariableTable: StartLengthSlotNameSignature 260thisLcom/xx/api/Hello; 261wordLjava/lang/String; publicstaticvoidsayHelloStatic(java.lang.String); descriptor:(Ljava/lang/String;)V flags:ACC_PUBLIC,ACC_STATIC Code: stack=3,locals=1,args_size=1 0:getstatic#5//Fieldjava/lang/System.out:Ljava/io/PrintStream; 3:new#6//classjava/lang/StringBuilder 6:dup 7:invokespecial#7//Methodjava/lang/StringBuilder." ":()V 10:ldc#12//Stringstatichello, 12:invokevirtual#9//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15:aload_0 16:invokevirtual#9//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19:invokevirtual#10//Methodjava/lang/StringBuilder.toString:()Ljava/lang/String; 22:invokevirtual#11//Methodjava/io/PrintStream.println:(Ljava/lang/String;)V 25:return LineNumberTable: line51:0 line52:25 LocalVariableTable: StartLengthSlotNameSignature 260wordLjava/lang/String; } SourceFile:"Hello.java"
我们从字节码文件中可以看出来:
sayHello(Stringword)和sayHelloStatic(Stringword)都只有一个参数,但是在字节码中:
sayHello(Stringword)中引用word时使用了15:aload_1,可以看出其加载的变量是在slot1中,而slot0中即保存了this。
sayHelloStatic(Stringword)中引用word时使用了15:aload_0,可以看出静态方法中,直接将变量存在了slot0中,因此无法使用this中的变量了。
当要操作当前类的变量或方法时,需要先aload_0,然后再做相关操作!
总结:
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。