java[13]Java中的继承

继承

        类的继承性是面向对象语言的基本特性,多态性前提是继承性。 Java 支持继承性和多态性。

       Java 中的继承

       为了了解继承性,先看这样一个场景:一位面向对象的程序员小赵,在编程过程中需要描述和处理个人信息,于是定义了类 Person,如下所示:

//Person.java文件

packagecom.a51work6;

importjava.util.Date;

publicclass Person {

    //名字

    privateString name;

    //年龄
    
    privateint age;

    //出生日期

    privateDate birthDate;

    publicString getInfo() {

        return"Person [name=" + name

        +", age=" + age

        +", birthDate=" + birthDate + "]";

    }

}

一周以后,小赵又遇到了新的需求,需要描述和处理学生信息,于是他又定义了一个

新的类 Student,如下所示:

很多人会认为小赵的做法能够理解并相信这是可行的,但问题在于 Student Person两个类的结构太接近了,后者只比前者多了一个属性 school,却要重复定义其他所有的内

//Student.java文件

packagecom.a51work6;

importjava.util.Date;

publicclass Student {

    //所在学校

    publicString school;

    //名字

    privateString name;

    //年龄

    privateint age;
    
    //出生日期

    privateDate birthDate;

    publicString getInfo() {
    
        return"Person [name=" + name

        +", age=" + age

        +", birthDate=" + birthDate + "]";

    }

}


        容,实在让人不甘心 Java 提供了解决类似问题的机制,那就是类的继承,代码如下所示:

//Student.java文件

packagecom.a51work6;

importjava.util.Date;

    publicclass Student extends Person {

    //所在学校

    privateString school;


}

        Student 类继承了 Person类中的所有成员变量和方法,从上述代码可以见继承使用的关键字是 extends extends 后面的 Person 是父类。

        如果在类的声明中没有使用 extends 关键字指明其父类,则默认父类为 Object 类,java.lang.Object 类是 Java 的根类,所有 Java 类包括数组都直接或间接继承了Object 类,

        在 Object 类中定义了一些有关面向对象机制的基本方法,如 equals()toString() finalize()等方法。

        提示一般情况下,一个子类只能继承一个父类,这称为单继承,但有的情况下一个子类可以有多个不同的父类,这称为多重继承。在Java中,类的继承只能是单继承,而多重继承可以通过实现多个接口实现。也就是说,在Java中,一个类只能继承一个父类,但是可以实现多个接口。

扫描二维码关注公众号,回复: 2199349 查看本文章

        提示面向对象分析与设计(OOAD)时,会用到UML11,其中类图非常重要,用来描述系统静态结构。 Student继承Person的类图如图12-1所示。类图中的各个元素说明如图12-2所示,类用矩形表示,一般分为上、中、下三个部分,上部分是类名,中部分是成员变量,下部分是成员方法。实线+空心箭头表示继承关系,箭头指向父类,箭头末端是子类。 UML类图中还有很多关系,如图12-3所示,如图虚线+空心箭头表示实线关系,箭头指向接口, 箭头末端是实线类。

调用父类构造方法

        当子类实例化时,不仅需要初始化子类成员变量,也需要初始化父类成员变量,初始化父类成员变量需要调用父类构造方法,子类使用 super 关键字调用父类构造方法。

        

 

        父类 Person 代码如下:

//Person.java文件

packagecom.a51work6;

importjava.util.Date;

publicclass Person {

    //名字

    privateString name;

    //年龄

    privateint age;

    //出生日期

    privateDate birthDate;

    //三个参数构造方法

    publicPerson(String name, int age, Date d) {

        this.name= name;

        this.age= age;

        birthDate= d;

    }

    publicPerson(String name, int age) {

    //调用三个参数构造方法

        this(name,age, new Date());

    }

...

}

子类 Student 代码如下:

//Student.java文件

packagecom.a51work6;

importjava.util.Date;

publicclass Student extends Person {

    //所在学校

    privateString school;

    publicStudent(String name, int age, Date d, String school) {

        super(name,age, d); ①

        this.school= school;

    }

    publicStudent(String name, int age, String school) {

        //this.school = school;//编译错误
    
        super(name,age); ②

        this.school= school;

    }

    publicStudent(String name, String school) { // 编译错误 ③
    
    //super(name, 30);

        this.school= school;

    }

}

        在Student子类代码第行和第行是调用父类构造方法,代码第 super(name,age, d)语句是调用父类的Person(String name, int age, Date d)构造方法,代码第super(name, age)语句是调用父类的 Person(String name, int age)构造方法。

        提示super语句必须位于子类构造方法的第一行。代码第行构造方法由于没有 super 语句,编译器会试图调用父类默认构造方法(无参数构造方法),但是父类 Person 并没有默认构造方法,因此会发生编译错误。解决这个编译错误有三种办法:

        1在父类 Person中添加默认构造方法,子类 Student 会隐式调用父类的默认构造方法。

        2在子类 Studen构造方法添加 super 语句,显式调用父类构造方法, super 语句必须是第一条语句。

        3在子类 Studen构造方法添加 this 语句,显式调用当前对象其他构造方法, this 句必须是第一条语句。

 

        成员变量隐藏

        子类成员变量与父类一样,会屏蔽父类中的成员变量,称为成员变量隐藏。示例代码如下:

 //ParentClass.java文件

packagecom.a51work6;

classParentClass {
    //x成员变量

    intx = 10; ①

}

    classSubClass extends ParentClass {

        //屏蔽父类x成员变量

        intx = 20; ②

        publicvoid print() {

        //访问子类对象x成员变量

        System.out.println("x= " + x); ③

        //访问父类x成员变量

        System.out.println("super.x= " + super.x); ④

    }

}

调用代码如下:

//HelloWorld.java文件

packagecom.a51work6;

publicclass HelloWorld {

    publicstatic void main(String[] args) {

        //实例化子类SubClass

        SubClasspObj = new SubClass();

        //调用子类print方法

        pObj.print();

    }

}

        运行结果如下:

        x= 20

        super.x= 10

        上述代码第行是在 ParentClass 类声明 x 成员变量,那么在它的子类 SubClass 代码行也声明了 x 成员变量,它会屏蔽父类中的 x 成员变量。那么代码第行的 x 是子类中的 x 成员变量。如果要调用父类中的 x 成员变量,则需要 super 关键字,见代码第行的 super.x

   方法的覆盖(Override

        如果子类方法完全与父类方法相同,即:相同的方法名、相同的参数列表和相同的返回值,只是方法体不同,这称为子类覆盖(Override)父类方法。

        示例代码如下:

//ParentClass.java文件

packagecom.a51work6;

classParentClass {

        //x成员变量

    intx;

    protectedvoid setValue() { ①

        x= 10;

    }

}

classSubClass extends ParentClass {

    //屏蔽父类x成员变量

    intx;

    @Override

    publicvoid setValue() { // 覆盖父类方法 ②

        //访问子类对象x成员变量

        x= 20;

        //调用父类setValue()方法

        super.setValue();

    }

    publicvoid print() {

        //访问子类对象x成员变量

        System.out.println("x= " + x);

        //访问父类x成员变量

        System.out.println("super.x= " + super.x);
    
    }

}

调用代码如下:

//HelloWorld.java文件

packagecom.a51work6;

publicclass HelloWorld {

    publicstatic void main(String[] args) {

        //实例化子类SubClass

        SubClasspObj = new SubClass();

        //调用setValue方法

        pObj.setValue();

        //调用子类print方法

        pObj.print();

    }

}

运行结果如下:

x= 20

super.x= 10

        上述代码第行是在ParentClass 类声明 setValue 方法,那么在它的子类SubClass 代码第行覆盖父类中的setValue 方法,在声明方法时添加@Override 注解, @Override 注解不是方法覆盖必须的,它只是锦上添花,但添加@Override注解有两个好处:

        1. 提高程序的可读性。

        2. 编译器检查@Override注解的方法在父类中是否存在,如果不存在则报错。

        注意方法重写时应遵循的原则:

        1. 覆盖后的方法不能比原方法有更严格的访问控制(可以相同)。例如将代码第行访问控制public修改private,那么会发生编译错误,因为父类原方法是protected

        2. 覆盖后的方法不能比原方法产生更多的异常。

 

猜你喜欢

转载自blog.csdn.net/qq_38125626/article/details/81065994