面向对象之继承
2016-03-11 本文已影响37人
jackLee
什么是继承
- 对象这种概念,本身就是十分方便的工具,我们可以通过概念将数据和功能封装到一个类中。因此可以对问题空间的观念给出恰当的表示,而不用受制于必须使用底层机器语言。
- 那么,同时,通过面向对象的抽象,相应问题来了:在创建一个类后,即使另外一个新类具有与其相似的功能,你还得重新创建一个新类。如果我们能以现有的类为基础,复制它,然后通过添加和修改这个副本来创建新的类那就好了。这里我们引出面向对象的一个概念--继承。
- 比如,我们创造了自行车这个类,同时还有轿车,电动车,卡车等,他们有着相似的功能,但却又有着各种不同的表现形式。那么我们可以抽象出一个AbstractCar的基类,定义如下接口:
<code>
AbstractCar.java
public interface AbstractCar{
public void on();
public void sart();
public void go();
public void stop();
public void speedUp();
public void speedDown();
public void off();
}
//注意:此处我为了方便定义的是接口,而不是抽象类。
</code>
此时的AbstractCar就是一个基类,也称作超类或父类,自行车,电动车,轿车等可以继承它,然后实现各种的业务逻辑并可以在基类的基础上添加各自的特性。这就是继承的通俗解释。 - 类型不仅仅只是描述了作用于一个对象集合上的约束条件,同时还有与其他类型之间的关系。两个类型之间可以有相似的特性和行为,但是其中一个类型或者一些类型可能比另一个类型有了更多的特性或者是更为扩展的特性,并且可以处理更多的消息。继承使用积累些和导出类型的概念来表示这种类型之间的相似性。一个积累些包含其所有导出类型所共享的特性行为。可以创建一个积累些来表示系统中某些对象的核心概念,从基类型中导出其他类型,表示此核心可以被实现的各种不同方式。
关于继承的若干点注意项:
- 当继承现有对象时,也就创造了新的类型。这个新的类型不仅包括现有类型的所有成员(尽管private成员被隐藏起来,并且不能访问),而且它更重要的是复制了基类的接口。换言之:可以发送给基类对象的消息,都能发送给其子类。所以意味着导出类与基类具有相同的类型,比如:自行车还是车,圆形是一种形状,所以车能提供的服务,自行车也能提供。
- 因为基类和导出类具有相同的基础接口,当对象接收到特定的消息时,必须有某些代码去执行。如果导出类对于继承的接口不做任何改变,那么将会和父类有相同的行为。
- 有两种方法,可以是导出来和基类的行为产生差异:
- 1.直接在导出类中添加新的方法,不言而喻,导出来肯定具有了更多的特性
- 2.导出类重写基类的方法,此时,导出类和基类有相同的接口,却有不同的行为。比如:在android的View.class中的draw()方法是画出一个矩形,而RoundView继承view,但draw()则可以画出圆角。这就是通俗的解释了覆盖.覆盖某方法:表示,此时,为正在使用相同的接口方法,但是我希望在新类中表现出不同的行为,提供不同的服务。即:看山不是山,看水不是水。
- is-a 和 is-like-a的语义:纯粹的继承基类而不添加新的接口,不提供新的服务,那就是is-a的关系,中文为:是一个。但是有时候不得不在导出类中添加新的接口,提供新的服务,此时使用:is-like-a的语义更为恰当。比如:自行车,轿车是一种车,坦克,风火轮像一种车。
Java继承语法中的细节地方:
- 继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时们总是在继承,关键字extends。
- 在java语法中:当创建一个类时,除非已经明确指出要从其他类中继承,否则就是在隐式地从Java的标准根类Object进行继承。(看下图)
- 下图分别是AbstractList 和RoleList的继承图,可以看出:
- 在Java中其根类为Object.class
- 继承链可以很长,但是每一个Class只能有一个直接的父类,不能是三姓家奴。即所谓的单根继承
- 一个基类可以有很多子类,多子多孙。
AbstaractList的继承图.png

-
关于继承导出类初始化的问题:
- 当创建一个导出类的对象的时候,该对象其实包含了一个基类的子对象,这个子对象与你用基类直接创建出的对象时一样的。二者区别在于:后者来自于外部人为创建,而基类子对象被包装在导出类对象内部不被外人看见。
- 如果继承链很长,其实在构建最底层的导出类的对象时,Java会自动调用其所有父类的构造函数创造出继承链上所有基类的对象。
- 调用构造函数也是有讲究的,如果构造函数无参即调用默认无参构造函数,但如基类中构造函数都有参,此时需要在导出类中使用super(参数)调用父类构造参数。下面看个例子:
<code>
class Game{
Game(int i){
System.out.println("this is Game Construc 001");
}
}
class BoardGame extends Game{
BoardGame(int i) {
super(i);
System.out.println("this is BoardGame Construc 002");
}
}
public class Chess extends BoardGame {
Chess(int i) {
super(i);
System.out.println("this is chess Construc 003");
}
public static void main(String []args){
new Chess(11);
}
}
</code>
输出为:
<code>
this is Game Construc 001
this is BoardGame Construc 002
this is chess Construc 003
</code>
- 注意:在写上面程序时,cheese的构造参数中如果没有super(i)这一行代码,Eclipse会报错。