关键字 this
最大的作用就让类中的一个方法访问该类的另一个方法或属性。其实 this
关键字是很容易理解的,接下来作者举两个例子进行对比,相信大家看后对 this
的知识就完全掌握了。
第一段代码演示了没有使用 this
的情况,具体代码如下所示。
class A {
private int aa, bb; // 声明两个 int 类型变量
public int returnData(int x, int y) { // 一个返回整数的方法
aa = x;
bb = y;
return aa + bb;
}
}
在第二段代码中使用 this
,具体代码如下所示。
在下面的代码中需要重点注意在 MyDate newDay=new MyDate(this);
语句中 this
的作用。
class MyDate {
private int day;
private int month;
private int year; // 定义 3 个成员变量
public MyDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
} // 构造方法
public MyDate(MyDate date) {
this.day = date.day;
this.month = date.month;
this.year = date.year; // 将参数 Date 类中的成员变量赋给 MyDate 类
} // 构造方法
public int getDay() {
return day;
}// 方法
public void setDay(int day) {
this.day = day; // 参数 day 赋给此类中的 ddy
}
public MyDate addDays(int moreDay) {
MyDate newDay = new MyDate(this);
newDay.day = newDay.day + moreDay;
return newDay; // 返回整个类
}
public void print() {
System.out.println("My Date: " + year + "-" + month + "-" + day);
}
}
public class TestMyDate {
public static void main(String args[]) {
MyDate myBirth = new MyDate(19, 11, 1987); // 利用构造函数初始化
MyDate next = myBirth.addDays(7);
// addDays() 的返回值是类,将其返回值赋给变量 next
next.print();
}
}
事实上,前两个类从本质说是相同的,而为什么在第二个类中使用 this
关键字呢?注意,第二个类中的方法 returnData (int aa,int bb)
的形式参数分别为 aa
和 bb
,这刚好和 private int aa,bb;
里的变量名是一样的。现在问题来了:究竟如何在 returnData
的方法体中区别形式参数 aa
和全局变量 aa
呢?两个 bb
也是如此吗?这就是引入 this
关键字的用处所在了。this.aa
表示的是全局变量 aa
,而没有加 this
的 aa
表示形式参数 aa
,bb
也是如此。
在此建议,在编程中不能过多使用 this
关键字。这从上面的代码中也可以看出,当相同的变量名加上 this
关键字过多时,有时会让人分不清。这时可以按照第三段代码进行修改,避免使用 this
关键字。
class A {
private int aa, bb; // 声明两个 int 类型变量
public int returnData(int aa1, int bb1) {
aa = aa1; // 在 aa 后面加上数字 1 加以区分,其他以此类推
bb = bb1;
return aa + bb;
}
}
由此可以看出,尽管上面的第一段代码、第二段代码、第三段代码都是一样的,但是第三段代码既避免了使用 this
关键字,又避免了第一段代码中参数意思不明确的缺点,所以建议使用与第三段代码一样的方法。
推出抽象方法的原因
当编写一个类时,常常会为该类定义一些方法,这些方法用以描述该类的行为方式,这时这些方法都有具体的方法体。在某些情况下,某个父类只是知道其子类应该包含什么样的方法,但却无法准确知道这些子类如何实现这些方法,例如定义一个 Shape 类,这个类应该提供一个计算周长的方法 scalPerimeter()
,不同 Shape 子类对周长的计算方法是不一样的,也就是说 Shape 类无法准确知道其子类计算周长的方法。
很多人以为,既然 Shape 不知道如何实现 scalPerimeter()
方法,那么就干脆不要管它了。其实这是不正确的作法,假设有一个 Shape 引用变量,该变量实际上会引用到 Shape 子类的实例,那么这个 Shape 变量就无法调用 scalPerimeter()
方法,必须将其强制类型转换为其子类类型才可调用 scalPerimeter()
方法,这就降低了 Shape 的灵活性。 究竟如何既能在 Shape 类中包含 scalPerimeter()
方法,又无须提供其方法实现呢?Java 中的做法是使用抽象方法满足该要求。抽象方法是只有方法签名,并没有方法实现的方法。
static 修饰的作用
使用 static
修饰的方法属于这个类,或者说属于该类的所有实例所共有。使用 static
修饰的方法不但可以使用类作为调用者来调用,也可以使用对象作为调用者来调用。值得指出的是,因为使用 static
修饰的方法还是属于这个类的,所以使用该类的任何对象来调用这个方法都将会得到相同的执行结果,这与使用类作为调用者的执行结果完全相同。
不使用 static
修饰的方法则属于该类的对象,它不属于这个类。因此不使用 static
修饰的方法只能用对象作为调用者来调用,不能使用类作为调用者来调用。使用不同对象作为调用者来调用同一个普通方法,可能会得到不同的结果。
数组内是同一类型的数据
Java 是一门是面向对象的编程语言,能很好地支持类与类之间的继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉,但这个数组中的数组元素类型还是唯一的,只能是水果类型。
另外,由于数组是一种引用类型的变量,因此使用它定义一个变量时,仅表示定义了一个引用变量(也就是定义了一个指针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。由于定义数组仅是定义了一个引用变量,并未指向任何有效的内存空间,所以还没有内存空间来存储数组元素,这时这个数组也不能使用,只有数组初始化后才可以使用。
继承的定义
类的继承是指从已经定义的类中派生出一个新类,是指我们在定义一个新类时,可以基于另外一个已存在的类,从已存在的类中继承有用的功能(例如属性和方法)。这时已存在的类便被称为父类,而这个新类则称为子类。在继承关系中,父类一般具有所有子类的共性特征,而子类则会为自己增加一些更具个性的方法。类的继承具有传递性,即子类还可以继续派生子类,因此,位于上层的类在概念上就更抽象,而位于下层的类在概念上就更具体。
父类和子类
继承是面向对象的机制,利用继承可以创建一个公共类,这个类具有多个项目的共同属性。我们可再用一些具体的类来继承该类,同时加上自己特有的属性。在 Java 中实现继承的方法十分简单,具体格式如下所示。
<修饰符> class <子类名> extends <父类名> {
[<成员变量定义>]…
[<方法定义>]…
}
我们通常所说的子类一般指的是某父类的直接子类,而父类也可称为该子类的直接超类。如果存在多层继承关系,比如,类 A 继承的是类 B,则它们之间的关系就必须符合下面的要求。
- 若存在另外一个类 C,类 C 是类 B 的子类,类 A 是类 C 的子类,那么可以判断出类 A 是类 B 的子类。
- 在 Java 程序中,一个类只能有一个父类,也就是说在 extends 关键字前只能有一个类,它不支持多重继承。
调用父类的构造方法
构造方法是 Java 类中比较重要的方法,一个子类可以访问构造方法,这在前面已经使用过多次。Java 语言调用父类构造方法的具体格式如下所示。
super(参数);
访问父类的属性和方法
在 Java 程序中,一个类的子类可以访问父类的属性和方法,具体语法格式如下所示。
super.[方法和全局变量];