分享

Java 继承(extends)详解

 oldzhoua 2019-03-14

一、继承问题的引出

继承性是面向对象的第二大主要特征。
下面首先编写两个程序:Person类、Student类。

Person类:

class Person { private String name ; private int age ; public void setName(String name) { this.name = name ; } public void setAge(int age) { this.age = age ; } public String getName() { return this.name ; } public int getAge(){ return this.age ; }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Student类:

class Student {         private String name ;         private int age ;         private String school ;         public void setName(String name) {                   this.name = name ;         }         public void setAge(int age) {                   this.age = age ;         }         public void setSchool(String school) {                   this.school = school ;         }         public String getName() {                   return this.name ;         }         public int getAge(){                   return this.age ;         }         public String getSchool() {                   return this.school ;         }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

以上两个程序通过代码的比较可以清楚的发现:代码之中存在了重复,而按照之前所学,这种重复是不可能消除的。

在所有的代码之中,最具有重复意义的就是链表类,针对于每一个简单Java类或者是说其他的任何类,都编写一个链表程序,代码量庞大,而且不方便维护。

二、继承的概念

继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

在Java之中,如果要实现继承的关系,可以使用如下的语法:

class 子类 extends 父类 {}
  • 1

子类又被称为派生类; 父类又被称为超类(Super Class)。

观察继承的基本实现:

package com.wz.extendsdemo;class Person {    private String name;    private int age;    public void setName(String name) {        this.name = name;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return this.name;    }    public int getAge() {        return this.age;    }}class Student extends Person { // Student类继承了Person类}public class TestDemo {    public static void main(String args[]) {        Student stu = new Student(); // 实例化的是子类        stu.setName('张三'); // Person类定义        stu.setAge(20); // Person类定义        System.out.println('姓名:' + stu.getName() + ',年龄:' + stu.getAge());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

运行结果:

姓名:张三,年龄:20
  • 1

通过代码可以发现,子类(Student)并没有定义任何的操作,而在主类中所使用的全部操作都是由Person类定义的,这证明:子类即使不扩充父类,也能维持父类的操作。

在子类之中扩充父类的功能:

package com.wz.extendsdemo;class Person {    private String name;    private int age;    public void setName(String name) {        this.name = name;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return this.name;    }    public int getAge() {        return this.age;    }}class Student extends Person { // Student类继承了Person类    private String school; // 子类的属性    public void setSchool(String school) {        this.school = school;    }    public String getSchool() {        return this.school;    }}public class TestDemo {    public static void main(String args[]) {        Student stu = new Student(); // 实例化的是子类        stu.setName('张三'); // Person类定义        stu.setAge(20); // Person类定义        stu.setSchool('清华大学'); // Student类扩充方法        System.out.println('姓名:' + stu.getName() + ',年龄:' + stu.getAge() + ',学校:' + stu.getSchool());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

运行结果:

姓名:张三,年龄:20,学校:清华大学
  • 1

以上的代码,子类对于父类的功能进行了扩充(扩充了一个属性和两个方法)。但是思考一下:子类从外表看是扩充了父类的功能,但是对于以上的代码,子类还有一个特点:子类实际上是将父类定义的更加的具体化的一种手段。父类表示的范围大,而子类表示的范围小

三、继承的限制

虽然继承可以进行类功能的扩充,但是其在定义的时候也是会存在若干种限制的。

限制一:一个子类只能够继承一个父类,存在单继承局限。
错误的写法:

class A {}class B {}class C extends A,B {}          // 一个子类继承了两个父类
  • 1
  • 2
  • 3

以上操作称为多重继承,实际上以上的做法就是希望一个子类,可以同时继承多个类的功能,但是以上的语法不支持而已,但是可以换种方式完成同样的操作。

正确的写法:

class A {}class B extends A {}class C extends B {}
  • 1
  • 2
  • 3

C实际上是属于(孙)子类,这样一来就相当于B类继承了A类的全部方法,而C类又继承了A和B类的方法,这种操作称为多层继承。

结论:Java之中只允许多层继承,不允许多重继承,Java存在单继承局限。

限制二:在一个子类继承的时候,实际上会继承父类之中的所有操作(属性、方法),但是需要注意的是,对于所有的非私有(no private)操作属于显式继承(可以直接利用对象操作),而所有的私有操作属于隐式继承(间接完成)。

package com.wz.extendsdemo;class A {    private String msg;    public void setMsg(String msg) {        this.msg = msg;    }    public String getMsg() {        return this.msg;    }}class B extends A {    public void print() {        //System.out.println(msg); // 错误: msg定义为private,不可见    }}public class TestDemo {    public static void main(String args[]) {        B b = new B();        b.setMsg('张三');        System.out.println(b.getMsg());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

此时对于A类之中的msg这个私有属性发现无法直接进行访问,但是却发现可以通过setter、getter方法间接的进行操作。

限制三:在继承关系之中,如果要实例化子类对象,会默认先调用父类构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。

package com.wz.extendsdemo;class A { public A() { // 父类无参构造 System.out.println('*************************') ; }}class B extends A { public B() { // 子类构造 System.out.println('#########################'); }}public class TestDemo { public static void main(String args[]) { B b = new B() ; // 实例化子类对象 }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

运行结果:

*************************#########################
  • 1
  • 2

这个时候虽然实例化的是子类对象,但是发现它会默认先执行父类构造,调用父类构造的方法体执行,而后再实例化子类对象,调用子类的构造方法。而这个时候,对于子类的构造而言,就相当于隐含了一个super()的形式:

class B extends A { public B() { // 子类构造 super(); // 调用父类构造 System.out.println('#########################'); }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在默认调用的是无参构造,而如果这个时候父类没有无参构造,则子类必须通过super()调用指定参数的构造方法:

package com.wz.extendsdemo;class A {    public A(String msg) { // 父类构造        System.out.println('*************************');    }}class B extends A {    public B() { // 子类构造        super('Hello'); // 调用父类构造        System.out.println('#########################');    }}public class TestDemo {    public static void main(String args[]) {        B b = new B(); // 实例化子类对象    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

运行结果:

*************************#########################
  • 1
  • 2

在任何的情况下,子类都逃不出父类构造的调用,很明显,super调用父类构造,这个语法和this()很相似:super调用父类构造时,一定要放在构造方法的首行上。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多