一、Java中的继承 1.extends关键字 前面我们已经学习过了什么是继承,那么在Java里面如何来表达继承的关系呢?就是使用extends关键字,比如:经理这个类继承雇员这个类,示例如下: public class Employee { //雇员 String name; //姓名 Date hireDate; //雇佣日期 Date dateOfBirth; //生日 String jobTitle; //职位 int grade; //... } public class Manager extends Employee { String department; //部门 Employee[] subordinates; //下属 //... } 在这样的定义中,Manager类被定义,具有 Employee 所拥有的所有变量及方法。所有这些变量和方法都是从父类的定义中继承来的。所有的程序员需要做的是定义额外特征或规定将适用的变化。
注意:这种方法是在维护和可靠性方面的一个伟大进步。如果在Employee类中进行修改,那么,Manager类就会自动修改,而不需要程序员做任何工作,除了对它进行编译。
2.初始化子类必先初始化父类 在Java编程语言中,对象的初始化是非常结构化的,这样做是为了保证安全。在前面的模块中,看到了当一个特定对象被创建时发生了什么。由于继承性,对象被完成,而且下述行为按顺序发生: (1)存储空间被分配并初始化到0值 (2)进行显式初始化 (3)调用构造方法 (4)层次中的每个类都会发生最后两个步骤,是从最上层开始。Java技术安全模式要求在子类执行任何东西之前,描述父类的一个对象的各个方面都必须初始化。因此,Java编程语言总是在执行子构造方法前调用父类构造方法的版本。 有继承的类在运行的时候,一定要记得:初始化子类必先初始化父类,这是Java程序的一个基本运行过程。比如: 第1行publicclass Child extends Parent{ 第2行 private String name = "Java私塾"; 第3行 privateint age = 2; 第4行 public Child(){ 第5行 age = 1000; //期望能到1000年,呵呵 第6行 } 第7行 publicstaticvoid main(String[] args) { 第8行 Child t = new Child(); //创建子类的对象 第9行 System.out.println(t.name+"的年龄是"+t.age+"年"); 第10行 } 第11行} 第12行class Parent{ 第13行 privateint num = 1; 第14行 public Parent(){ 第15行 System.out.println("现在初始化父类"); 第16行 } 第17行 publicvoid test(){ 第18行 System.out.println("这是父类的test方法"); 第19行 } 第20行}
上述类的基本运行顺序是: (1) 先运行到第7行,这是程序的入口 (2) 然后运行到第8行,这里要new一个Child,就要调用Child的构造方法 (3) 就运行到第4行,注意:初始化子类必先初始化父类 (4) 要先初始化父类,所以运行到第14行 (5) 然后是第13行,初始化一个类,必须先初始化它的属性 (6) 然后是第15行 (7) 然后是第16行,表示父类初始化完成 (8) 然后是回到子类,开始初始化属性,因此运行到第2行,然后是第3行 (9) 子类属性初始化完过后,才回到子类的构造方法,执行里面的代码,也就是第5行 (10) 然后是第6行,表示new一个Child实例完成 (11) 然后回到main方法中执行第9行 (12) 然后是第10行
运行结果是: 现在初始化父类 Java私塾的年龄是1000年
3.单继承性 单继承性:当一个类从一个唯一的类继承时,被称做单继承性。单继承性使代码更可靠。接口提供多继承性的好处,而且没有(多继承的)缺点。 Java编程语言允许一个类仅能继承一个其它类,即一个类只能有一个父类。这个限制被称做单继承性。单继承性与多继承性的优点是面向对象程序员之间广泛讨论的话题。Java编程语言加强了单继承性限制而使代码更为可靠,尽管这样有时会增加程序员的工作。后面会学到一个被叫做接口(interface)的语言特征,它允许多继承性的大部分好处,而不受其缺点的影响。
使用继承性的子类的一个例子如图所示:
4.构造方法不能被继承 尽管一个子类从父类继承所有的方法和变量,但它不继承构造方法,掌握这一点很重要。 一个类能得到构造方法,只有两个办法。或者写构造方法,或者根本没有写构造方法,类有一个默认的构造方法。
5.关键字super 关键字super可被用来引用该类的父类,它被用来引用父类的成员变量或方法。父类行为被调用,就好象该行为是本类的行为一样,而且调用行为不必发生在父类中,它能自动向上层类追溯。
super关键字的功能: (1)点取父类中被子类隐藏了的数据成员 (2)点取已经覆盖了的方法 (3)作为方法名表示父类构造方法 例如: public class Employee { private String name; private int salary; public String getDetails() { return "Name: " + name + "\nSalary: " + salary; } } public class Manager extends Employee { private String department;
public String getDetails() { return super.getDetails() + //调用父类的方法 "\nDepartment: " + department; } } 请注意,super.method()格式的调用,如果对象已经具有父类类型,那么它的方法的整个行为都将被调用,也包括其所有负面效果。该方法不必在父类中定义,它也可以从某些祖先类中继承。也就是说可以从父类的父类去获取,具有追溯性,一直向上去找,直到找到为止,这是一个很重要的特点。
6.调用父类构造方法 在许多情况下,使用默认构造方法来对父类对象进行初始化。 当然也可以使用super来显示调用父类的构造方法。
public class Employee { String name; public Employee(String name) { this.name = name; } } public class Manager extends Employee { String department; public Manager(String name, String department) { super(name); this.department = department; } } 注意:无论是super(…)还是this(…),都必须放在构造方法的第一句。(所以不会同时出现在一个方法中的,也没有这个必要。) 通常要定义一个带参数的构造方法,并要使用这些参数来控制一个对象的父类部分的构造。可能通过从子类构造方法的第一行调用关键字super的手段调用一个特殊的父类构造方法作为子类初始化的一部分。要控制具体的构造方法的调用,必须给super()提供合适的参数。当不调用带参数的super时,缺省的父类构造方法(即,带0个参数的构造方法)被隐含地调用。在这种情况下,如果没有缺省的父类构造方法,将导致编译错误。 public class Employee { String name; public Employee(String name) { this.name = name; } } public class Manager extends Employee { String department; public Manager(String name, String department) { super(name); // 调用父类参数为String类型的构造方法 this.department = department; } } 当被使用时,super或this的方法调用必须被放在构造方法的第一句。显然,两者不能被放在一个单独行中,但这种情况事实上不是一个问题。如果写一个构造方法,它既没有调用super(…)也没有调用this(…),编译器自动插入一个调用到父类构造方法中,而不带参数。其它构造方法也能调用super(…)或this(…),调用一个static方法和构造方法的数据链。最终发生的是父类构造方法(可能几个)将在链中的任何子类构造方法前执行。 Java私塾跟我学系列——JAVA篇 网址:http://www.javass.cn 电话:010-68434236 |