本文灵感来源于知乎文章(https://zhuanlan.zhihu.com/p/62779357) 以及《Java编程思想》P85页
使用this关键字之前
Java提供了一个叫做this关键字,this关键字总是指向调用该方法的对象。根据this出现的位置不同,this作为对象的默认引用有两种情况。
- 构造器中引用该构造器正在初始化的对象
- 在方法中引用调用该方法的对象
在方法中引用调用该方法的对象
代码示例
那我们直接上了概念,肯定是不能够被大家理解的哈,我们转念换个角度来想一想,我们如果没有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()
方法表明,除构造器之外,编译器禁止在其他任何方法中调用构造器,不信?你可以试试!