3.8 特殊变量this

img

扫码看视频

我们继续分析代码3.8,读者可以思考一下该程序的输出结果,然后编译运行,看看实际结果是否符合预期。在main方法中,在构造Point对象时,调用new Point(5, 5),有参的构造方法Point中的形参x和y都被赋值为5,我们的设想是将形参x的值赋值给Point的成员变量x,形参y的值赋值给Point的成员变量y,这样在main方法中调用pt.show(),最终输出结果预期为5和5,然而实际输出结果却是0和0。这是为什么呢?

在2.3.1节,我们介绍过变量是有作用域范围的,在带参数的Point构造方法中,形参x和y的作用域范围是整个方法体(由一对花括号所包裹),在该方法体中,Point的成员变量x和y因为和形参x和y同名,因此是不可见的。在带参数的Point构造方法中,我们相当于做了无用功,将形参x的值又赋给形参x,将形参y的值又赋给了形参y,而Point的成员变量x和y根本没有被赋值,因此当调用pt.show()时,输出的pt对象的数据成员x和y的值是0和0。

要如何解决这个问题呢?最简单的办法自然是将方法的形参改名,比如改成a和b,如同Point类的init方法。但如果我们不想改名呢?这个时候就轮到一个特殊的变量this登场了。

Java中的this变量代表对象自身,若类中有两个同名变量,一个属于类(类的成员变量),而另一个属于某个特定的方法(方法中的局部变量),则使用this区分成员变量和局部变量。

修改代码3.8,如代码3.9所示。

img
img

注意粗体显示部分的代码,this.x指代的就是Point类的成员变量x,this.y指代的就是Point类的成员变量y。

程序的输出结果是:

img

很多读者无法理解this指代对象本身是什么意思。我们修改一下代码3.9,在main方法中再创建一个Point类的对象,如代码3.10所示。

img

程序的输出结果是:

img

当构造pt对象时,代码中的this指代的是pt对象,当构造pt2对象时,代码中的this指代的是pt2对象。你可以理解为:this变量在对象创建之前是没有值的,只有当对象创建之后才有引用当前对象的值。

从输出结果上来看,pt和pt2对象分别有自己的数据成员,换句话说,类中的数据成员每个对象都有自己的一份独立拷贝,占据独立的内存空间。与数据成员不同的是,类中的实例方法并不是每个对象都有一份,而是一个类只有一份方法的代码,存放在内存的方法区,所有对象调用的是同一份方法代码。那么问题来了,如果在方法中访问了类中的数据成员,当调用方法时,既然代码都是一样的,那么如何能够准确地定位到不同对象各自的数据成员呢?答案就是this变量了,每当调用一个实例方法时,this变量将被设置成引用该实例方法的特定的类对象,方法的代码会与this所代表的对象的特定数据建立关联。这就是this变量背后隐藏的秘密。

注意:this变量只能用在类的方法代码当中。

this变量除了可以调用数据成员外,还可以调用方法。在一般情况下(除非变量名存在冲突),为了明确访问类中的数据成员,可以显式地加上this,在其他情况下,是否通过this来访问数据成员,看个人的喜好。对于方法而言,加不加this都是一样的,因为方法代码只有一份。当使用IDE(集成开发环境)的时候,由于IDE有代码自动提示功能,所以有时候为了编码方便,我们通过输入this调出代码自动提示窗口,以便查找和选择要调用的方法。

this变量还有一些特殊的用法,我们看代码3.11。

img
img

我们先看粗体显示的代码。原本两个构造方法中的代码是有冗余的,为此,我们可以使用this来指代构造方法的调用,在无参构造方法中通过this(2, 2)来调用第二个构造方法,当用户使用无参构造方法来创建Point对象时,点的两个坐标将被初始化为2和2。这就是this的另一种方法,代替构造方法的调用,要注意,不能直接通过构造方法名字调用它,只能通过this来调用。

在有参数的构造方法中,我们通过this来调用init方法,从而完成坐标x和y的初始化,这里只是演示this的用法,并无实际意义。

在main方法中,pt对象通过有参构造方法来创建,pt2对象通过无参构造方法来创建,程序最终输出结果是:

img

在使用this调用其他构造方法时,一定要注意:(1)this必须是该构造方法的第一行代码;(2)不要造成递归调用。我们看代码3.12。

img

代码3.12在构造方法中就出现了递归调用。当构造方法出现递归调用时,Java编译器会给出错误信息。一般来说,我们使用this来调用重载构造方法都基于一个目的:使用一个通用的构造方法来初始化对象,使用重载的构造方法来设置一些默认值,如代码3.13所示。

img

在这个例子中,People(String name, int age)方法就是最基本的构造方法,它负责具体的初始化操作。其余的重载构造方法利用this调用,在缺少参数的情况下设置对象数据成员的默认值。