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

前言

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

总结:

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

(0)

相关推荐

  • 详解java中this.getClass()和super.getClass()的实例

    详解java中this.getClass()和super.getClass()的实例 前言: 遇到this.getClass()和super.getClass()的返回值感到疑惑,经过探索豁然开朗. getClass()是java中Object类的一个方法,其原型为: public final Class<?> getClass() 返回值为 当前运行时类的Class对象. 所以写了一段代码来说明: getClass()不受this和super影响,而是有当前的运行类决定的. 代码如下: 父类

  • java this的应用方法解析

    这篇文章主要介绍了java this的应用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 package java04; /* * 当方法的局部变量和类的成员变量重名时,会根据"就近原则",优先使用局部变量. * 如果需要访问奔雷中的成员变量,需要使用格式: this.成员变量名 * * "通过谁调用的方法,谁就是this" * * */ public class Person2 { String name

  • 浅谈Java中的this作为返回值时返回的是什么

    有时会遇到this作为返回值的情况,那么此时返回的到底是什么呢? 返回的是调用this所处方法的那个对象的引用,读起来有点绕口哈,有没有想起小学语文分析句子成份的试题,哈哈. 一点点分析的话,主干是"返回的是引用": 什么引用呢?"那个对象的引用": 哪个对象呢?"调用方法的那个对象": 调用的哪个方法呢?"调用的是this所位于的方法":这样就清楚了. 再总结一下就是,this作为返回值时,返回的是调用某方法的对象的引用,这

  • java中this与super关键字的使用方法

    java中this与super关键字的使用方法 这几天看到类在继承时会用到this和super,这里就做了一点总结,与各位共同交流,有错误请各位指正~ this this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针. this的用法在java中大体可以分为3种: 1.普通的直接引用 这种就不用讲了,this相当于是指向当前对象本身. 2.形参与成员名字重名,用this来区分: class Person { private int age = 10; public Perso

  • Java this 关键字的使用方法详解

    Java this 关键字的使用方法详解 构造方法中的this关键字 构造方法是一个类的对象在通过new关键字创建时自动调用的,在程序中不能向调用其他方法一样通过方法名(也就是类名)来调用.但如果一个类有多个构造方法,可以在一个构造方法中通过this(paras-)来调用其他的构造方法. 使用this来调用其他构造方法有如下几个约束. 1) 只能在构造方法中通过this来调用其他构造方法,普通方法中不能使用. 2) 不能通过this递归调用构造方法,即不能在一个构造方法中通过this直接或间接调

  • java中this的n种使用方法

    this可能是几乎所有有一点面向对象思想的语言都会引用到的变量,java自然不例外.只是,this有多少种用法,我也不知道了,让我们来see see. 由简入奢! 易. 来个例子说明下: public class DebugerTest { public static void main(String[] args) { UserExample samp1 = new UserExample("amy"); System.out.println("who are u? &qu

  • 深入理解java中this关键字的使用

    一,表示类中属性 1,没有使用this的情况 class Person{ // 定义Person类 private String name ; // 姓名 private int age ; // 年龄 public Person(String name,int age){ // 通过构造方法赋值 name = name ; age = age ; } public String getInfo(){ // 取得信息的方法 return "姓名:" + name + ",年龄

  • Java中this,static,final,const用法详解

    一.this 用类名定义一个变量的时候,定义的应该只是一个引用,外面可以通过这个引用来访问这个类里面的属性和方法,那们类里面是够也应该有一个引用来访问自己的属性和方法纳?JAVA提供了一个很好的东西,就是 this 对象,它可以在类里面来引用这个类的属性和方法. Java关键字this只能用于方法方法体内.当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this.因此,this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现t

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

    前言 从字节码看java中 this 隐式传参具体体现(和python中的self如出一辙,但是比python中藏得更深),也发现了 static 与 非 static 方法的区别所在! static与非static方法都是存储java的方法区.在static 方法中,没有this引用,因此无法使用当前类中所定义的变量,而非static方法则会默认传入this. 概述 this关键字,是一个隐式参数,另外一个隐式参数是super. this用于方法里面,用于方法外面无意义. this关键字一般用

  • Python中字符串的修改及传参详解

    发现问题 最近在面试的时候遇到一个题目,选择用JavaScript或者Python实现字符串反转,我选择了Python,然后写出了代码(错误的): #!/usr/bin/env python #-*-coding:utf-8-*- __author__ = 'ZhangHe' def reverse(s): l = 0 r = len(s) - 1 while l < r: s[l],s[r] = s[r],s[l] l += 1 r -= 1 return s 然后面试官问了两个问题: (1)

  • Java中的重要核心知识点之继承详解

    目录 一.继承 1.概念 2.语法 3.父类成员的访问 (1)子类中访问父类成员变量 (2)子类中访问父类成员方法 4.super关键字 5.子类构造方法 6.super和this 7.代码块执行顺序 8.父类成员在子类中的可见性 9.继承方式 10.final关键字 11.组合 一.继承 1.概念 继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类.继承呈现了面向对象程序设计的

  • Java 中 Class Path 和 Package的使用详解

    目录 一. 类路径 (class path) 二. 包 (package) 三. jar 文件 一. 类路径 (class path) 当你满怀着希望安装好了 java, 然后兴冲冲地写了个 hello world,然后编译,运行, 就等着那两个美好的单词出现在眼前, 可是不幸的是, 只看到了 Can't find class HelloWorld 或者 Exception in thread "main" java.lang.NoSuchMethodError : maain.为什么

  • Java中的Collections类的使用示例详解

    Collections的常用方法及其简单使用 代码如下: package Collections; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Stack; public class collections { public static void main(String[]args){ int array[]={125,75,56,7}; Li

  • Java中volatile关键字的作用与用法详解

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile 关键字作用是,使系统中所有线程对该关键字修饰的变量共享可见,可以禁止线程的工作内存对volatile修饰的变量进行缓存. volatile 2个使用场景: 1.可见性:Java提供了volatile关键字来保证可见性. 当一个共享变量被volatile修饰时,它会保证修

  • java 中 String format 和Math类实例详解

    java 中 String format 和Math类实例详解 java字符串格式化输出 @Test public void test() { // TODO Auto-generated method stub //可用printf(); System.out.println(String.format("I am %s", "jj")); //%s字符串 System.out.println(String.format("首字母是 %c",

  • java中functional interface的分类和使用详解

    java 8引入了lambda表达式,lambda表达式实际上表示的就是一个匿名的function. 在java 8之前,如果需要使用到匿名function需要new一个类的实现,但是有了lambda表达式之后,一切都变的非常简介. 我们看一个之前讲线程池的时候的一个例子: //ExecutorService using class ExecutorService executorService = Executors.newSingleThreadExecutor(); executorSer

  • java中的i++和++i的区别详解

    java中的前加加++和后加加++,有很多人搞的很晕,不太明白!今天我举几个例子说明下前++和后++的区别! 其实大家只要记住一句话就可以了,前++是先自加再使用而后++是先使用再自加! 前++和后++总结:其实大家只要记住一句话就可以了,前++是先自加再使用而后++是先使用再自加! 请大家看下面的例子就明白了! public class Test { public static void main(String[] args) { //测试,前加加和后加加 //前++和后++总结:其实大家只要

  • java中压缩文件并下载的实例详解

    当我们对一些需要用到的资料进行整理时,会发现文件的内存占用很大,不过是下载或者存储,都不是很方便,这时候我们会想到把文件变成zip格式,即进行压缩.在正式开始压缩和下载文件之前,我们可以先对zip的格式进行一个了解,然后再就具体的方法给大家带来分享. 1.ZIP文件格式 [local file header + file data + data descriptor]{1,n} + central directory + end of central directory record 即 [文件

随机推荐