7.6 this关键字的原理
最后更新于:2022-04-01 14:11:21
这一节我们来讲一个关键字,就是this关键字。
我们还是通过例子来看吧:
~~~
class Person
{
private String name;
private int age;
Person(String n,int a)
{
name = n;
age = a;
}
public void speak()
{
System.out.println(name+":"+age);
}
}
~~~
~~~
class ThisTest
{
public static void main(String[] args)
{
Person kobe = new Person("KOBE",37);
kobe.speak();
}
}
~~~
这个例子我们应该很熟悉了,前面几节都在用这个例子,我们再来看一看结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-18_573c41b0680db.jpg)
很显然,构造函数对对象kobe进行了初始化。
但是我们发现,虽然结果是我们想要的,但是,我们单独看这个构造函数,从可读性的角度我们分析,我们根本就不知道函数传过来的是什么内容,我们可以说一无所知,可阅读性太差了,那么我们再对这个构造函数进行改造:
~~~
class Person
{
private String name;
private int age;
Person(String name,int age)
{
name = name;
age = age;
}
public void speak()
{
System.out.println(name+":"+age);
}
}
~~~
唉,这样不是很清晰了吗,我们一眼就看出这个构造函数要告诉我们初始化对象的姓名和年龄了,我们看结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-18_573c41ec76dd2.jpg)
嗯???这是什嘛情况,KOBE人呢?
我们7.4节谈过了构造函数的内存加载过程,但是我们没有提到这里遇到的这种情况,这种情况我们可以说是:成员变量和局部变量重名的问题,此时对于栈内存和堆内存中都会变量name和age,而调用的构造函数会自动到栈内存中寻找这两个变量,而此时,恰好都有,所以系统就会做一个非常有趣的事情,就是把栈内存中的name原赋给本身,而对象的name和age的值其实在堆内存中,所以结果就是我们刚才看到的结果喽。
那么对于这个问题,我们该怎么解决呢?
java给我们解决方案,那就是用一个关键字this来区分成员变量和局部变量。我们再来改造:
~~~
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public void speak()
{
System.out.println(name+":"+age);
}
}
~~~
结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-18_573c41ec8a72c.jpg)
很好,KOBE同志又回来了。
所以我们可以说,当成员变量与局部变量重名时,我们可以用this来区分。
那么我们就想明白,this到底代表什么呢?java语言说,this代表的是对象。我们还想明白,this代表的是哪个对象?java语言又说,代表的就是当前对象。
专业点的术语是这样定义this的:this就是所在函数所在对象的引用。说简单点就是:this代表本类对象的引用。
我们自己用通俗点的语言来定义:就是哪个对象调用了this所在的哪个函数,this就代表哪个对象,也就是说this就是这个对象的引用。
比如上面的例子中的kobe调用了构造函数Person(String name,int age),那么我们可说this就可以代表kobe这个对象。
那么我们再来对this在内存中的体现过程分析一下,我们继续7.4的过程,只有小的变动。
1.main方法进栈内存,main方法中有一个Person类类型变量kobe;
2.new创建Person对象,在堆内存中创建空间(假如地址为0x0045),该空间中有两个成员变量name和age;
3.对对象的两个成员变量进行初始化,此时会自动选择调用构造函数Person(String n,int a);
4.构造函数Person(String name,int age)进栈内存,参数name="KOBE",age=0也加载入栈。而此时系统会自动为该栈内存中加载一个对象的引用,也就是this,并且把kobe的堆内存地址赋给this;
5.然后在把this.name和this.age的初始化为栈内存中name和age,这样就很清晰了,this.name和this.age我们可以理解为就this所指堆内存中对象的成员变量,此时对象的初始化完成;
6.把地址0x0045赋给main方法中的实例变量kobe;
7.构造函数Person(String name,int age)出栈,释放参数name和age和this引用;
8.执行kobe.speak()语句,调用Person类中的speak()方法,则speak方法进栈,此时系统也会为speak方法加载一个this引用,指向堆内存中的对象地址(0x0045);
9.执行打印语句,跳出speak方法,speak方法出栈,释放this引用;
10.跳出main方法,main方法出栈,程序运行结束。
通过上面的过程分析,我们可以简单的总结出这样一个结论:当在函数中需要用到调用函数的对象时,就用this关键字。
为了更好的理解这个结论,我们把上面的例子可以标准的写成下面这样:
~~~
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public void speak()
{
System.out.println(this.name+":"+this.age);
}
}
~~~
我们对这个类方法中的所有成员变量都标准的写成了this.成员变量的格式。
那么我们刚开始看的第一个例子为什么没有this,而结果是也是正确的呢?
很显然,当然对于成员变量与局部变量不重名时,this是可以省略的,但请注意,不是没有,而是省略。