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

从字节码看java中 this 的隐式传参的更多相关文章

  1. 从字节码看java类型转换【 深入理解 (T[]) new Object[size] 】

    我们都知道,java中对类型的检查是很严格的,所以我们平操作时,也往往很小心. 如题: (T[]) new Object[size],这种写法是一般我们是不会干的!但是有点经验的同学,还是会遇到这样写 ...

  2. 通过字节码分析java中的switch语句

    在一次做题中遇到了switch的问题,由于对switch执行顺序的不了解,在这里简单的通过字节码的方式理解一下switch执行顺序(题目如下): public class Ag{ static pub ...

  3. 从源码看java中Integer的缓存问题

    在开始详细的说明问题之前,我们先看一段代码 public static void compare1(){ Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128; ...

  4. java中调用三方接口post传参时map和jsonobject的区别转换

    post方法名及参数为:(具体方法可参考https://www.cnblogs.com/mufengforward/p/10510337.html) public static String doPo ...

  5. java中类型的隐式转换

    byte+byte=int,低级向高级是隐式类型转换,高级向低级必须强制类型转换,byte<char<short<int<long<float<double

  6. 从源码浅析Java中的Lock和AbstractQueuedSynchronizer

    在之前的文章中我也曾经介绍过Lock,像ReentrantLock(可重入锁)和ReentrantReadWriteLock(可重入读写锁),这些所我们在说的时候并没有详细的说明它们的原理,仅仅说明了 ...

  7. F#中的自定义隐式转换

    我们知道隐式变换在可控情况下会使代码变得简洁.熟悉C#的都知道C#中可以自定义隐式变换,例如 public class A { private int data; public static impl ...

  8. SQL Server中提前找到隐式转换提升性能的办法

        http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前 ...

  9. vue-cli项目中使用全局过滤器及传参(日期格式化)

    // 过滤日期格式,传入时间戳,根据参数返回不同格式 const formatTimer = function(val, hours) { if (val) { ); var y = dateTime ...

随机推荐

  1. centos 防火墙端口开放

    开放端口 永久的开放需要的端口 sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent sudo firewall-cmd -- ...

  2. oracle redo日志文件损坏恢复

    参考:How to Recover from Loss Of Online Redo Log And ORA-312 And ORA-313 (Doc ID 117481.1) 在线重做日志文件丢失后 ...

  3. js中的回调函数 和promise解决异步操作中的回调地狱问题。

    回调函数 : 函数作为参数传递到另外一个函数中.简单数据类型和引入数据类型中的数组和对象作为参数传递大家肯定都不陌生,其实引用数据类型中的函数也是可以的. 事实上大家见到的很多,用到的也很多,比如jQ ...

  4. [树上倍增+二分答案][NOIP2012]运输计划

    题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 公元 2044 年,人类进入了宇宙纪元 L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条航道建立在两个星球之间,这 n-1n− ...

  5. bittorrent 学习(二) LOG日志和peer管理连接

    代码中的log.h log.c比较简单 void logcmd() 记录命令  int logfile();运行日志的记录 int init_logfile() 开启log文件 源码比较清晰也很简单. ...

  6. HTTP之请求消息Request

    客户端发送一个HTTP请求到服务器的请求消息包括以下格式: 请求行(request line).请求头部(header).空行和请求数据四个部分组成. 请求行以一个方法符号开头,以空格分开,后面跟着请 ...

  7. 【MySQL】初识数据库及简单操作

    一.数据库概述 1.1 什么是数据(Data) 描述事物的符号记录称为数据,描述事物的符号既可以是数字,也可以是文字.图片,图像.声音.语言等,数据由多种表现形式,它们都可以经过数字化后存入计算机. ...

  8. C#的app.Config文件中设置,可以选择执行环境(左配置,有程序),app.Config中的appSettings首字母必须小写,符合源码要求

  9. 用python turtle画玫瑰

    1.turtle 库 2.画玫瑰的代码: import turtle turtle.penup() turtle.left() turtle.fd() turtle.pendown() turtle. ...

  10. IOS 设置视图半透明子控件不透明

    代码处理: UIColor *color = [[UIColor blackColor] colorWithAlphaComponent:0.6]; self.view.backgroundColor ...