Java this关键字底层逻辑和三大关键用法

一、底层核心结论

  1. this 本质就是:对象在堆内存中的内存地址引用
  2. 每个 Java 对象,天生自带一个隐藏的 this 引用,指向自己堆内存地址
  3. 调用成员方法时,JVM 默认偷偷把当前对象地址传给方法,方法里的 this 就是这个地址。
  4. this.xxx 本质:通过对象内存地址,找到堆里的成员变量/方法

二、内存底层视角:对象是怎么存的

1. 对象内存分布

创建对象 Student s = new Student();

  • 栈内存:存引用变量 s,保存堆内存地址
  • 堆内存:存真实对象
    • 成员变量(属性)
    • 隐藏的 this 指针(天生自带,看不见但真实存在)

通俗理解
每个对象出生时,自带一张自己的身份证地址,这张身份证就是 this

2. 方法调用底层原理(关键)

当执行:

s.setName("张三");

JVM 底层做了3件事:

  1. 拿到栈中 s 保存的堆对象地址
  2. 隐式把这个地址传给 setName 方法,作为隐藏第一个参数
  3. 方法内部的 this,就接收了这个对象地址

相当于底层真实执行是这样(假设的代码):

// JVM 偷偷帮你加的隐藏参数
public void setName(Student this, String name){
    //this隐藏在了参数列表中的第一个位置!!!
    this.name = name;
}

this在更加底层的代码中会显示在参数列表中的第一个位置,所以我们每次参数列表中输入了n个参数,但实际上底层代码中有n+1个参数,就是有this存在。
这也就是为什么方法里能直接用 this:JVM 自动传了对象地址进来。


Java this关键字三大用法

一、this.属性名 用法

大部分时候,普通方法访问类的成员变量时,无须使用 this 前缀。
如果方法局部变量、方法参数 和 类的成员变量同名,发生了变量名覆盖,程序又想访问类本身的成员变量,就必须加上 this 前缀区分。

示例:学生类 Student 定义

public class Student {
    // 成员变量:姓名、年龄、分数
    private String name;
    private int age;
    private double score;
}

上面 name、age、scoreprivate 修饰,类外部不能直接赋值访问。
我们可以给 Student 类添加有参构造方法,通过传参给成员变量赋值。

// 构造方法:给学生三个属性赋初始值
public Student(String name, int age, double score) {
    this.name = name;    // 给当前对象的姓名赋值
    this.age = age;      // 给当前对象的年龄赋值
    this.score = score;  // 给当前对象的分数赋值
}

构造方法里的 this 代表当前正在创建的学生对象

  • 等号左边 this.name当前学生对象身上的成员变量 name
  • 等号右边 name构造方法括号里传进来的参数

测试 main 方法

public static void main(String[] args) {
    Student stu = new Student("李四", 20, 95.5);
    System.out.println("学生信息如下:");
    System.out.println("学生姓名:" + stu.name +"\n学生年龄:" + stu.age +"\n考试分数:" + stu.score);

运行结果

学生信息如下:
学生姓名:李四
学生年龄:20
考试分数:95.5

提示:当类的成员变量名方法/构造的参数名相同时,必须用 this.属性名 指明访问的是类的成员变量,避免被局部变量覆盖。


二、this.方法名 用法

this 最大作用之一:类中的一个普通方法,调用本类里另一个普通方法

场景举例

定义学生类 Student,学生有 study() 学习方法read() 读书方法
要求:学习的时候,先要执行读书方法。

第一种写法(不推荐,重复创建对象)

public class Student {
    // 读书方法
    public void read() {
        System.out.println("正在认真读书");
    }

    // 学习方法,需要依赖读书方法
    public void study() {
        // 额外新建一个学生对象来调用read()
        Student s = new Student();
        s.read();
        System.out.println("正在专心学习");
    }
}

测试代码:

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.study();
    }
}

这种写法会产生两个 Student 对象

  1. main 方法里创建的 stu 对象
  2. study 方法内部又 new 了一个新 Student 对象

首先我们得知道,没有 static 修饰的普通方法,必须由对象调用,即非静态方法调用,必须通过对象调用。
那么问题来了,有没有必要重新 new 一个新学生对象?
完全没必要啊
调用 study() 方法时,已经存在一个学生对象了,直接用当前这个对象就行,不用额外新建。

this 就可以代表当前调用方法的对象

第二种写法(推荐:用 this.方法名)

public class Student {
    public void read() {
        System.out.println("正在认真读书");
    }

    public void study() {
        // this 代表当前学生对象,调用自己的read方法
        this.read();
        System.out.println("正在专心学习");
    }
}

谁调用 study(),this 就代表谁:

  • stu.study() 调用时,this 就指向 stu 这个学生对象。

简写形式(开发常用)

同一个类中,普通方法互相调用 可以省略 this,底层依然隐含 this:

public void study() {
    read(); // 省略this,等价于 this.read();
    System.out.println("正在专心学习");
}

注意
static 静态方法不能使用 this
因为静态方法属于类,不属于某个具体对象,而 this 必须指向某一个实例对象,所以静态方法里禁止用 this,也不能直接访问非静态方法。

总之,方法里访问本类其他普通方法、成员变量,加不加 this 效果一样;
只是省略 this 是语法简写,底层依然隐含 this 引用。


三、this() 访问本类构造方法

this() 专门用来调用本类中其他构造方法

  • 空括号 this():调用无参构造
  • 带参数 this(参数):调用有参构造

语法硬性规则:

  1. 一个构造方法里,只能写 1 次 this(...)
  2. this() 只能写在构造方法第一行
  3. 只能在构造方法中使用
  4. 不能循环互相调用

示例代码

public class Student {
    String name;

    // 无参构造
    public Student() {
        // 调用本类带一个字符串参数的有参构造
        this("张三");
    }

    // 有参构造
    public Student(String name) {
        this.name = name;
    }

    // 打印学生姓名
    public void printInfo() {
        System.out.println("姓名:" + name);
    }

    public static void main(String[] args) {
        // 调用无参构造
        Student stu = new Student();
        stu.printInfo();
    }
}

运行结果

姓名:张三

逻辑解释

  1. 执行 new Student(),先走无参构造方法
  2. 无参构造第一行 this("张三"),跳转调用有参构造
  3. 有参构造中 this.name = name,给当前学生对象的 name 赋值为 “张三”;
  4. 有参构造执行完毕,回到无参构造,构造流程结束;
  5. 调用 printInfo() 打印姓名。

全程只创建一个学生对象this() 只是在同一个对象初始化过程中,复用另一个构造方法的代码,不会重复 new 对象。

更多推荐