Skip to main content

Java深耕之this关键字

· 11 min read
CheverJohn

本文灵感来源于知乎文章(https://zhuanlan.zhihu.com/p/62779357) 以及《Java编程思想》P85页

使用this关键字之前

Java提供了一个叫做this关键字,this关键字总是指向调用该方法的对象。根据this出现的位置不同,this作为对象的默认引用有两种情况。

  1. 构造器中引用该构造器正在初始化的对象
  2. 在方法中引用调用该方法的对象

在方法中引用调用该方法的对象

代码示例

那我们直接上了概念,肯定是不能够被大家理解的哈,我们转念换个角度来想一想,我们如果没有this关键字,会面临一个什么样子的情况呢?

public class Person {
//定义一个move()方法
public void move(){
System.out.println("正在执行move()方法");
}
//定义一个eat()方法,eat()方法需要借助move()方法
public void eat(){
Person p = new Person();
p.move();
System.out.println("正在执行eat()方法");
}
public static void main(String[] args) {
//创建Person对象
Person p = new Person();
//调用Person的eat()方法
p.eat();
}
}
// 代码来源于:https://zhuanlan.zhihu.com/p/62779357

运行结果为:

正在执行move()方法
正在执行eat()方法

代码讲解

上述的方式确实能够做到eat()方法里调用move()方法,但是我们在main()方法里可以看到我们总共创建了两个对象:main()方法里创建了一个对象;eat()方法里创建了一个对象。但是实际上我们是不需要两个对象的,因为在程序调用第一个eat()方法时一定会提供一个Person对象,而不需要重新创建一个Person了。

因此我们可以通过this关键字在eat()方法中获得调用该方法的对象。this关键字只能在方法内部使用,表示对”调用方法的那个对象“的引用。

于是上面的代码Person类中的eat()方法改为下面这种方式较为合适:

//定义一个eat()方法,eat()方法需要借助move()方法
public void eat(){
//使用this引用调用eat()方法的对象
this.move();
System.out.println("正在执行eat()方法");
}

不过呢,虽然接下来要说的,可能会让读者同学感觉我是在耍你哈。但是可不是哦,上面这么多我只是做个引子而已,用来引导大家的。我先说我要说的知识点吧

this关键字的用法和其他对象引用并无不同。但是如果要在方法内部调用同一个类的另一个方法,就不必使用this,直接调用即可。当前方法的this引用会自动应用用途同一类中的其他方法。所以上述代码也可以这样写:

//定义一个eat()方法,eat()方法需要借助move()方法
public void eat(){
move();
System.out.println("正在执行eat()方法");
}

整体代码可以如下

package cn.mr8god.example;

/**
* @author Mr8god
* @date 2020/4/14
* @time 16:09
*/

public class Person {
public void move(){
System.out.println("正在执行move()方法");
}

public void eat(){
move();
System.out.println("正在执行eat()方法");
}

public static void main(String[] args) {
Person p = new Person();
p.eat();
}
}

暂时的小总结

​ 在eat()方法内部,你可以写this.move(),但无此必要。编译器能够帮你自动添加。只有当明确指出对当前对象的引用时,才需要使用this关键字。例如,当需要返回对当前对象的引用的时候,就常常在return语句里这样写:

package cn.mr8god.chapterfive;

/**
* @author Mr8god
* @date 2020/4/14
* @time 11:18
*/
public class Leaf {
int i = 0;
Leaf increment(){
i++;
return this;
}
void print(){
System.out.println("i = " + i);
}

public static void main(String[] args) {
Leaf x = new Leaf();
x.increment().increment().increment().print();
}
}

代码中,由于increment()通过this关键字返回了对当前对象的引用,所以很容易就可以在一条语句里对同一个对象执行多次操作。

this关键字对于将当前对象传递给其他方法也很有用

package cn.mr8god.chapterfive;

/**
* @author Mr8god
* @date 2020/4/14
* @time 11:21
*/

class Person{
public void eat(Apple apple){
Apple peeled = apple.getPeeled();
System.out.println("Yummy");
}
}

class Peeler{
static Apple peel(Apple apple){
return apple;
}
}
class Apple{
Apple getPeeled(){ return Peeler.peel(this); }
}
public class PassingThis {
public static void main(String[] args) {
new Person().eat(new Apple());
}

}

Apple需要调用Peeler.peel()方法,这个方法是一个外部的工具方法,将执行由于某种原因而必须放在Apple外部的操作。为了将其自身传递给外部方法,Apple必须使用this关键字。

在构造器中调用构造器

另一种情形是:this关键字可以用于构造器中作为默认引用,由于构造器是直接使用new关键字来调用的,而不是使用对象来调用的,所以this在构造器中代表该构造器正在初始化的对象。

例一

package cn.mr8god.example;

/**
* @author Mr8god
* @date 2020/4/14
* @time 17:05
*/
public class Person {
public int age;
public Person(){
int age = 0;
this.age = 3;
}

public static void main(String[] args) {
System.out.println(new Person().age);
}
}

与普通方法类似,大部分时候,我们在构造器中访问其他成员变量和方法时都可以省略this前缀,但是如果构造器中有一个与成员变量同名的局部变量,又必须在构造器中访问这个被覆盖的成员变量,则必须使用this前缀。正如上面的程序所示。

this作为对象的默认引用使用时,程序可以像访问普通引用变量一样来访问这个this引用,甚至可以把this当成普通方法的返回值。如下面的程序

public class Person {
public int age;
public Person grow() {
age ++;
return this;
}
public static void main(String[] args) {
Person p = new Person();
//可以连续调用同一个方法
p.grow().grow().grow();
System.out.println("p对象的age的值是:"+p.age);
}
}

运行结果为:

p对象的age的值是:3

上面的代码中可以看到,如果在某个方法中把this作为返回值,则可以多次连续调用同一个方法,从而使得代码变得更加的简洁。

例二

有时候为一个类写了多个构造器,我们可能想在一个构造器中调用另一个构造器,以避免重复代码。可以使用this关键字做到这一点。

package cn.mr8god.chapterfive;

import static net.mindview.util.Print.print;

/**
* @author Mr8god
* @date 2020/4/14
* @time 11:31
*/
public class Flower {
int petalCount = 0;
String s = "initial value";
Flower(int petals){
petalCount = petals;
print("Constructor w/ int arg only, petalCount= "
+ petalCount);
}
Flower(String ss){
print("Constructor w/ String arg only, s = " + ss);
s = ss;
}
Flower(String s, int petals){
this(petals);
this.s = s;
print("String & int args");
}
Flower(){
this("hi", 47);
print("default constructor (no args)");
}
void printPetalCount(){
print("petalCount = " + petalCount + " s = " + s);
}

public static void main(String[] args) {
Flower x = new Flower("江晨玥",222);
// Flower x = new Flower();
x.printPetalCount();
}
}

这串代码如果给初学者看的话,会有一点不明确的地方,毕竟代码太长了嘛,这边给个建议,就只看我们的this指针部分哦!

然后代码中,我选择初始化了一个Flower("江晨玥", 222)的方法,首先这行代码会被用到上面的Flower(String s, int petals)里边,很好的实现了方法的重载嘛。然后我们就可以直观地看到这边,我讲解几个可能会有疑问的地方哈

可能会有疑问一

这边

Flower(String s, int petals){
this(petals);
this.s = s;
print("String & int args");
}

代码中的this(petals)到底是怎么一回事,其实有这个疑问还是要算你的this关键字没有理解到家,this其实在这里指的是Flower,相当于我在这个Flower(String s, int petals)里引用了Flower(int petals)方法。

可能会有疑问二

这边的this.s = s,可能也会有疑问,其实这个也展示了this的另外一种用法。由于参数s的名称和数据成员s的名字相同,所以会很容易产生歧义。使用this.s就可以来代表数据成员解决这个问题。Java日常编程经常会这样的哦

讲解一

printPetalCount()

方法表明,除构造器之外,编译器禁止在其他任何方法中调用构造器,不信?你可以试试!

Over!