-
1 Java继承
-
2 Java多态
1、继承概念
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
2、继承举例
为了更好地了解继承性,先看这样一个场景:一位面向对象的程序员小赵,在编程过程中需要描述和处理个人信息,于是定义了类Person,如下所示:
//Person.java文件
import java.util.Date;
public class Person {
private String name; // 名字
private int age; // 年龄
private Date birthDate; // 出生日期
public String getInfo() {
return "Person [name=" + name
+ ", age=" + age
+ ", birthDate=" + birthDate + "]";
}
}
一周以后,该程序员又遇到了新的需求,需要描述和处理学生信息,于是他又定义了一个新的类Student,如下所示:
//Student.java文件
import java.util.Date;
public class Student {
public String school; // 所在学校
private String name; // 名字
private int age; // 年龄
private Date birthDate; // 出生日期
public String getInfo() {
return "Person [name=" + name
+ ", age=" + age
+ ", birthDate=" + birthDate + "]";
}
}
很多人会认为小赵的做法能够理解并相信这是可行的,但问题在于Student和Person两个类的结构太接近了,后者只比前者多了一个属性school,却要重复定义其他所有的内容,实在让人“不甘心”。Java提供了解决类似问题的机制,那就是类的继承,代码如下所示:
//Student.java文件
import java.util.Date;
public class Student extends Person {
private String school; // 所在学校
}
Student类继承了Person类中的所有成员变量和方法,从上述代码可见继承使用的关键字是extends,extends后面的Person是父类。
如果在类的声明中没有使用extends关键字指明其父类,则默认父类为Object类,java.lang.Object类是Java的根类,所有Java类包括数组都直接或间接继承了Object类,在Object类中定义了一些有关面向对象机制的基本方法,如equals()、toString()和finalize()等方法。
Tips:一般情况下,一个子类只能继承一个父类,这称为“单继承”,但有的情况下一个子类可以有多个不同的父类,这称为“多重继承”。在Java中,类的继承只能是单继承,而多重继承可以通过实现多个接口实现。也就是说,在Java中,一个类只能继承一个父类,但是可以实现多个接口。
Tips:面向对象分析与设计(OOAD)时,会用到下面的UML图,其中类图非常重要,用来描述系统静态结构。Student继承Person的类图如下图2所示。类图中的各个元素说明如图2所示,类用矩形表示,一般分为上、中、下三个部分,上部分是类名,中部分是成员变量,下部分是成员方法。实线+空心箭头表示继承关系,箭头指向父类,箭头末端是子类。UML类图中还有很多关系,如图虚线+空心箭头表示实线关系,箭头指向接口,箭头末端是实线类。

继承的特性:
(1)子类拥有父类非private的属性,方法。
(2)子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
(3)子类可以用自己的方式实现父类的方法。
(4)Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
(5)提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
3、调用父类构造方法
当子类实例化时,不仅需要初始化子类成员变量,也需要初始化父类成员变量,初始化父类成员变量需要调用父类构造方法,子类使用super关键字调用父类构造方法。下面看一个示例,现有父类Person和子类。Student,它们类图如下图所示:

//Person.java文件
import java.util.Date;
public class Person {
private String name; // 名字
private int age; // 年龄
private Date birthDate; // 出生日期
// 三个参数构造方法
public Person(String name, int age, Date d) {
this.name = name;
this.age = age;
birthDate = d;
}
public Person(String name, int age) {
this(name, age, new Date()); // 调用三个参数构造方法
}
...
}
子类Student代码如下:
//Student.java文件
import java.util.Date;
public class Student extends Person {
private String school; // 所在学校
public Student(String name, int age, Date d, String school) {
super(name, age, d);
this.school = school;
}
public Student(String name, int age, String school) {
// this.school = school; //编译错误 super语句必须位于子类构造方法的第一行。
super(name, age);
this.school = school;
}
public Student(String name, String school) { // 编译错误,没有super语句,编译器会试图调用父类默认构造方法(无参数构造方法),但是父类Person并没有默认构造方法,因此会发生编译错误。
// super(name, 30);
this.school = school;
}
}
解决这个编译错误有三种办法:
(1)在父类Person中添加默认构造方法,子类Student会隐式调用父类的默认构造方法。
(2)在子类Studen构造方法添加super语句,显式调用父类构造方法,super语句必须是第一条语句。
(3)在子类Studen构造方法添加this语句,显式调用当前对象其他构造方法,this语句必须是第一条语句。
4、成员变量隐藏和方法覆盖
4.1 成员变量隐藏
子类成员变量与父类一样,会屏蔽父类中的成员变量,称为“成员变量隐藏”。示例代码如下:
//ParentClass.java文件
class ParentClass {
int x = 10; // x成员变量 那么在它的子类SubClass代码第11行也声明了x成员变量,
// 则它会屏蔽父类中的x成员变量
}
class SubClass extends ParentClass {
// 屏蔽父类x成员变量
int x = 20;
public void print() {
// 访问子类对象x成员变量
System.out.println("x = " + x);
// 访问父类x成员变量
System.out.println("super.x = " + super.x);
}
}
调用代码如下:
//HelloWorld.java文件
public class HelloWorld {
public static void main(String[] args) {
//实例化子类SubClass
SubClass pObj = new SubClass();
//调用子类print方法
pObj.print();
}
}
运行结果如下:
x = 20 super.x = 10


