1 继承的概念
如果类 B 具有类 A 的全部属性和方法,而且又具有自己特有的某些属性和方法,则把类 A 称作一般类,把类 B 称作特殊类。
在面向对象程序设计中运用继承原则,就是在每个由一般类和特殊类形成的一般 - 特殊结构中,把一般类的对象实例和所有特殊类的对象实例都共同具有的属性和操作一次性地在一般类中进行显式的定义,在特殊类中不再重复地定义一般类中已经定义的东西,但是在语义上,特殊类却自动地、隐含地拥有它的一般类(以及所有更上层的一般类)中定义的属性和操作。
特殊类的对象拥有其一般类的全部或部分属性与方法,称作特殊类对一般类的继承。
继承所表达的就是一种对象之间的相交关系,它使得某类对象可以继承另外一类对象的数据成员和成员方法。
若类 B 继承类 A 时,则属于 B 的对象便具有类 A 的全部或部分性质(数据属性)和功能(操作)。
我们称被继承的类 A 为基类、父类或超类,而称继承类 B 为 A 的派生类或子类。
继承避免了对一般类和特殊类之间共同特征进行的重复描述。
2 继承的特征
继承关系是传递的。继承是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。
继承简化了人们对事物的认识和描述,能清晰体现相关类间的层次结构关系。
提供软件复用功能。
通过增强一致性来减少模块间的接口和界面,大大增加程序的易维护性。
提供多重继承机制。从理论上说,一个类可以是多个一般类的特殊类,它可以从多个一般类中继承属性和方法,这便是多重继承。而 Java 出于安全性和可靠性的考虑,仅支持单重继承,而通过使用接口机制来实现多重继承。
3 Java 用 extends 指明继承关系
在 Java 程序设计中,继承是通过 extends 关键字来实现的。
在定义类时使用 extends 关键字指明新定义类的父类,新定义的类称为指定父类的子类,这样就在两个类之间建立起了继承关系。
这个新定义的子类可以从父类那里继承所有非 private 的属性和方法作为自己的成员。
实际上,在定义一个类而不给出 extends 关键字及父类名时,默认这个类是系统类 Object 的子类。
3.1 数据成员的继承
子类可以继承父类的所有非私有的数据成员。
3.2 数据成员的隐藏
数据成员的隐藏是指在子类中重新定义一个与父类中已经定义的数据成员名完全相同的数据成员,即子类拥有了两个相同名字的数据成员,一个是继承父类的,另一个是自己定义的。
当子类引用这个同名的数据成员时,默认操作是它自己定义的数据成员,而把从父类那里继承来的数据成员“隐藏”起来。
当子类要引用继承自父类的同名数据成员时,可使用关键字 super 引导。
3.3 成员方法的继承
子类可以继承父类的非私有成员方法。
3.4 成员方法的覆盖(Overload)
子类可以重新定义与父类同名的成员方法,实现对父类方法的覆盖。
方法的覆盖与数据成员的隐藏的不同之处在于:
子类隐藏父类的数据成员只是使之不可见,父类同名的数据成员在子类对象中仍然占有自己独立的内存空间;
子类方法对父类同名方法的覆盖将清除父类方法占用的内存,从而使父类方法在子类对象中不复存在。
需要注意的是:子类在重新定义父类已有的方法时,应保持与父类完全相同的方法名、返回值类型和参数列表,否则就不是方法的覆盖,而是子类定义自己特有的方法,与父类的方法无关。
4 this 与 super
4.1 this 的使用场合
在方法内借助 this 来明确表示引用的是类的数据成员,而不是形参或局部变量,从而提高程序的可读性。
简单地说,this 代表了当前对象的一个引用,可将其理解为对象的另一个名字,通过这个名字可以顺利地访问对象、修改对象的数据成员、调用对象的方法。
this 的使用场合主要有以下三种:
用来访问当前对象的数据成员:
this.数据成员
用来访问当前对象的成员方法:
this.成员方法(参数)
当有重载的构造方法时,用来引用同类的其他构造方法:
this(参数)
4.2 super 的使用场合
super 表示的是当前对象的直接父类对象,是当前对象的直接父类对象的引用。
所谓直接父类是相对于当前对象的其他“祖先”类而言。
若子类的数据成员或成员方法名与父类的数据成员或成员方法名相同时,当要调用父类的同名方法或使用父类的同名数据成员,则可用关键字 super 来指明父类的数据成员和方法。
super 的使用场合有三种:
用来访问直接父类隐藏的数据成员:
super.数据成员
用来调用直接父类中被覆盖的成员方法:
super.成员方法(参数)
用来调用直接父类的构造方法:
super(参数)
5 构造方法的重载与继承
5.1 构造方法的重载
一个类的若干个构造方法之间可以相互调用。
当一个构造方法需要调用另一个构造方法时,可以使用关键字 this,同时这个调用语句应该是整个构造方法的第一个可执行语句。
使用关键字 this 来调用同类的其他构造函数时,优点同样是可以最大限度地提高对已有代码的利用程度,提高程序的抽象度和封装性,减少程序的维护工作量。
5.2 构造方法的继承
子类可以继承父类的构造方法,构造方法的继承遵循以下的原则:
子类无条件地继承父类的不含参数的构造方法。
如果子类自己没有构造方法,则它将继承父类的无参数构造方法作为自己的构造方法;如果子类自己定义了构造方法,则在创建新对象时,它将先执行继承自父类的无参数构造方法,然后再执行自己的构造方法。
对于父类含参数的构造方法,子类可以通过在自己的构造方法中使用 super 关键字来调用它,但这个调用语句必须是子类构造方法的第一个可执行语句。
6 向方法传递对象
传递给方法的参数可以是表达式(如常量、变量)、对象等。
传递给方法的参数若是变量,则只能由实参传递给形参,而不能由形参带回,它是一种单向值传递。
在方法的引用过程中,对于形参变量值的修改并不影响实参变量的值。
但是,传递给方法的参数若是对象,则方法可以对其做永久性修改。
7 类转换
类转换就是指父类对象与子类对象之间在一定条件下的相互转换。
父类对象与子类对象之间相互转换规则如下:
父类对象与子类对象之间可以隐式转换(也称默认转换),也可以显式转换(也称强制转换)。
处于相同类层次的类的对象不能进行转换。
子类对象可以转换成父类对象,但对数据成员的引用必须使用强制转换。
类转换格式如下:
(子类)父类
或
(父类)子类
8 继承与封装的关系
在面向对象系统中,封装性主要指的是对象的封装性,即将属于某一类的一个具体对象封装起来,使其数据和操作成为一个整体。
在引入了继承机制的面向对象系统中,对象依然是封装得很好的实体,其他对象与它进行通讯的途径仍然只有一条,那就是发送消息。
类机制是一种静态机制,不管是基类还是派生类,对于对象来说,它仍然是一个类的实例,既可能是基类的实例,也可能是派生类的实例。
因此,继承机制的引入丝毫没有影响对象的封装性。
继承和封装机制还具有一定的相似性,它们都是一种共享代码的手段。
继承是一种静态共享代码的手段,通过派生类对象的创建,可以接收某一消息,启动其基类所定义的代码段,从而使基类和派生类共享了这一段代码。
封装机制所提供的是一种动态共享代码的手段,通过封装,我们可将一段代码定义在一个类中,在另一个类所定义的操作中,我们可以通过创建该类的实例,并向它发送消息而启动这一段代码,同样也达到共享的目的。