继承的定义与使用、方法覆写、super关键字

文章目录

  • 1. 继承
    • 1.1 了解继承
    • 1.2 继承的概念
    • 1.3 继承的实现
    • 1.4 继承的限制
    • 1.5 继承总结
  • 2. 方法覆写
    • 2.1 方法的覆写(重点)[研究抽象类和接口的第一步]
      • 2.1.1 基本概念
      • 2.1.2 面试题:请解释重载(overload)和覆写(override)
    • 2.2 属性的覆写
  • 3. super关键字
      • 3.1.1 访问父类构造方法
      • 3.1.2 访问父类成员变量和成员方法

1. 继承

1.1 了解继承

 定义两个类(人类、学生类):

class Person{
    private String name;
    private int age;

    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return this.age;
    }
    public void setAge(int age){
        this.age = age;
    }
}
class Student{
    private String name;
    private int age;
    private String school;

    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return this.age;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String getSchool(){
        return this.school;
    }
    public void setSchool(String school){
        this.school = school;
    }
}

以上程序即使我们之前一直采用的模式,单独的Java类,含有大量重复性代码。不仅代码上重复,而且从概念上讲,学生一定是人,学生和人相比学生更加具体,学生类描述的范围更小,具备的属性更多,具有的方法也更多。
这个时候要想消除结构定义上的重复,就要用到继承。

1.2 继承的概念

  在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使用多种事物之间形成一种关系体系。
举例:
  学生和教师都属于人,程序中便可以描述为学生和教师继承人,同理,中学生和大学生继承学生,而讲师和教授继承教师。这些人之间形成一个继承体系,具体实现如下图所示。
继承的定义与使用、方法覆写、super关键字_第1张图片
 在Java中,类的继承是指在一个现有类的基础上去创建一个新的类,创建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法

1.3 继承的实现

在Java中,继承使用extends关键字来实现,定义语法如下:

class 子类 extends 父类

关于继承定义的说明:
 子类在一些书上也被称为派生类,父类也被称为超类(Super Class)
举例:继承的基本实现

class Person{
    private String name;
    private int age;

    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return this.age;
    }
    public void setAge(int age){
        this.age = age;
    }
}
// 定义了一个Student子类继承了Person父类
class Student extends Person{
}

public class Test{
    public static void main(String[] args){
        Student stu = new Student();
        stu.setName("zhangsan");
        stu.setAge(18);
        System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge());
    }
}

 通过上述代码可以发现,当发生自类继承关系之后,子类可以直接继承父类的操作,可以实现代码的重用。子类最低也维持和父类相同的功能。子类可以进行功能的扩充。例如:扩充属性和方法。
举例:子类进行功能的扩充

// 定义了一个Student子类继承了Person父类
class Student extends Person{
    //扩充新的属性
    private String school;

    public String getSchool(){
        return this.school;
    }
    public void setSchool(String school){
        this.school = school;
    }
}
public class Test{
    public static void main(String[] args){
        Student stu = new Student();
        stu.setName("zhangsan");
        stu.setAge(18);
        stu.setSchool("University");
        System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge()+",学校:"+stu.getSchool());
    }
}

继承的主要作用:对类进行扩充以及代码重用

1.4 继承的限制

  • 子类对象在进行实例化前一定会首先实例化父类对象。默认调用父类的构造方法后再调用子类构造方法进行子类对象实例化。

举例:观察子类对象创建

class Person{
    public Person(){
        Sustem.out.println("**Person类对象产生**");
    }
}
class Student extends Person{
    public Student(){
        super();    //此语句在无参时写不写都一样,但有参必须写
        System.out.println("**Student类对象的产生**");
    }
}
public class Test{
    public static void main(String[] args){
        new Student();
    }
}

运行结果如下所示:
在这里插入图片描述
 以上代码我们发现,没有任何一条语句调用 父类构造方法。因此子类对象实例化之前一定先实例化父类对象。
注意:

① 实际上在子类的构造方法之中,相当于隐含了一个语句super();
② 如果父类里没有提供无参构造,就必须使用super()明确指出你要调用的父类构造方法。

  • Java只允许单继承,不允许多继承。(一个子类只能继承一个父类)
    要想在Java中实现类似的"多继承",要么多层继承,要么使用内部类。

举例:错误的多继承

class A{}
class B{}
class C extends A,B{}

C同时继承A和B的主要目的是同时拥有A和B中的操作,为了实现这样的目的,可以采用多层继承的形式完成。

class A{}
class B extends A{}
class C extends B{}

多层继承的层数不建议太多。类的继承关系最多3层。
总结:Java不允许多重继承,但允许多层继承

  • 在进行继承时,子类会继承父类的所有结构。(包含私有属性、构造方法、普通方法)。
  • 显示继承:所有的非私有操作属于显示继承(可以直接调用)
  • 隐式继承:所有的私有操作属于隐式继承(不可以直接调用,需要通过其他形式调用)

举例:显示继承与隐式继承

class Person{
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}
class Student extends Person{
    public void fun(){
        System.out.println(getName());
    }
}
public class Test{
    public static void main(String[] args){
        Student stu = new Student();
        stu.setName("zhangsan");
        System.out.println(stu.getName());
        stu.fun();
    }
}

此时父类中的属性的确被子类所继承了,但发现子类能够使用的是所有非private操作,而所有的非private操作无法直接使用,所以称为隐式继承。

1.5 继承总结

  1. 继承语法(目的):扩展已有类的功能,使代码重用
  2. 子类对象的实例化流程:不管如何操作,一定要先实例化父类对象。
  3. 不允许多重继承,只允许多层继承。

2. 方法覆写

在上面学习过继承之后,我们就可能遇到下列问题:
  如果子类中定义了与父类相同的方法或属性的时候,这样的操作就称为覆写(override)

2.1 方法的覆写(重点)[研究抽象类和接口的第一步]

2.1.1 基本概念

  如果子类定义了与父类方法名称、参数类型及个数完全相同的方法。但是被覆写不能够拥有比父类更为严格(>=)的访问控制权限。
举例: 简单的覆写

class Person{
    public void print(){
        System.out.println("[Person]类的print方法");
    }
}
class Student extends Person{
    public void print(){
        System.out.println("[Student]类的print方法");
    }
}
public class Test{
    public static void main(String[] args){
        new Student().print();
    }
}

运行结果如下所示:
在这里插入图片描述
判断调用的方法到底是父类方法还是子类方法:

  1. 看new在哪里(当前使用的对象是通过哪一个类new的)
  2. 调用的方法有没有被子类覆写,如果被覆写,调用的一定是被覆写后的方法。

在进行方法覆写的时候,有明确要求:被覆写的方法不能拥有比父类更为严格的访问控制权限。
三种访问控制权限的比较:private < default(什么也不用写,包访问权限) < public
 意味着如果父类使用public进行方法声明,那么子类也必须使用public;如果父类使用default,那么子类可以使用default或者public。
举例: 错误的覆写

class Person{
    public void print(){
        System.out.println("[Person]类的print方法");
    }
}
class Student extends Person{
    //默认为default权限,比public更加严格的访问权限
    void print(){
        System.out.println("[Student]类的print方法");
    }
}
public class Test{
    public static void main(String[] args){
        new Student().print();
    }
}

继承的定义与使用、方法覆写、super关键字_第2张图片
建议:

  • 写方法时,尽量使用public
  • 写属性时,尽量使用private

问题: 如果现在父类方法使用private定义,子类中使用public覆写,可以吗?
举例: 父类使用private定义的方法,子类中使用public覆写

class Person{
    public void fun(){
        this.print();
    }
    //如果现在父类中使用了private定义,那么就表示该方法只能被父类使用,子类无法使用。换言之,子类根本不知道有这样的方法
    private void print(){
        System.out.println("[Person]类的print方法");
    }
}
class Student extends Person{
    //这时候定义的方法只是子类定义的新方法,并没有和父类方法有任何关系
     public void print(){
        System.out.println("[Student]类的print方法");
    }
}
public class Test{
    public static void main(String[] args){
        new Student().fun();
    }
}

运行结果如下所示:
在这里插入图片描述
总结:
  如果父类中定义的方法使用了private定义,那么就表示该方法只能被父类使用,子类无法使用。换而言之。父类中被private修饰的,只能被父类使用,子类无法使用。这时子类中定义的与父类的同名方法是新方法,和父类方法并没有任何关系。

2.1.2 面试题:请解释重载(overload)和覆写(override)

区别 重载(overload) 覆写(override)
概念 方法名称相同,参数的类型及个数不同 方法名称、返回值类型、参数的类型及个数
范围 一个类 继承关系
限制 没有权限要求 被覆写的方法不能拥有比子类更严格的访问控制权限

重载时尽量保持方法返回值类型一致。

2.2 属性的覆写

当子类定义了和父类属性名称完全形同的时候,就成为属性的覆盖。
举例:属性覆盖

class Person{
    public String info = "Person";
}
class Student extends Person{
    //属性覆盖
    public String info = "Student";
}
public class Test{
    public static void main(String[] args){
        System.out.println(new Student().info);
    }
}

运行结果如下:
在这里插入图片描述
按照就近原则,取得是被覆盖的属性。
 这种操作本身就没有什么意义,其核心的原因在于:类中属性都要求使用private封装,一旦封装了,子类不知道父类具有什么属性,那么也就不存在属性覆盖的问题。

3. super关键字

 当子类重写父类方法后,子类对象将无法访问父类中被重写的方法。为了解决这一问题,Java中专门提供了一个super关键字用于访问分类成员。
 上面在子类对象实例化时,我们使用过super(),当时主要的作用是子类调用父类构造方法时才使用的。那么在进行覆写的操作过程中,子类也可以使用super()方法/super.属性明确调用父类中的方法或属性。

3.1.1 访问父类构造方法

super([参数1, 参数2, ……])
  • 当子类调用父类无参构造时,super()可写可不写,表示调用父类的无参构造
  • 当子类调用父类有参构造时,super(参数列表)
    注意:
(1)必须要写,要告诉编译器当前调用的是有参构造
(2)子类构造方法中调用父类构造方法必须是第一行语句
(3)this与super不能同时使用

举例:使用super调用父类构造方法

class Person{
    private String name;
    public Person(){}
    public Person(String name)
    {
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
}
class Student extends Person{
    private String school;
    public Student(String name, String school){
        super(name);
        this.school = school;
    }
    public void print(){
        System.out.println("我是" + this.school + "的" + getName());
    }
}
public class Test{
    public static void main(String[] args){
        new Student("张三", "星光小学").print();
    }
}

使用super()调用父类构造方法的代码必须位于子类构造方法的第一行,且参数列表必须与调用父类构造方法的参数列表一致。

3.1.2 访问父类成员变量和成员方法

super.成员变量	//表示明确调用父类中被覆写的属性
super.成员方法([参数1, 参数2, ……])	//用于子类中明确调用被覆写的方法

举例:使用super调用父类的同名方法

class Person{
    public void print(){
        System.out.println("父类的print方法");
    }
}
class Student extends Person{
    public void print(){
        super.print();
        System.out.println("子类的print方法");
    }
}
public class Test{
    public static void main(String[] args){
        new Student().print();
    }
}

在这里插入图片描述
举例: 使用supe调用父类属性

class Person{
public String info = "Person";
}
class Student extends Person{
    public String info = "Student";
    public void print(){
        System.out.println(super.info);
        System.out.println(this.info);
    }
}
public class Test{
    public static void main(String[] args){
        new Student().print();
    }
}

在这里插入图片描述
 通过上述代码,我们可以发现super和this在使用上非常的相似,但是两者最大的区别就是super是子类访问父类的操作,this是本类访问处理的操作

区别 this super
概念 访问本类中的属性和方法 由子类访问父类中的属性、方法
查找范围 先查找本类,如果本类没有就调用父类 不查找本类而直接调用父类定义
特殊 表示当前对象

总结:

  1. 能使用super.方法()一定要标记出是父类操作。
  2. 子类覆写父类的方法是因为父类的方法功能不足才需要覆写。
  3. 方法覆写的时候使用的就是public权限。

你可能感兴趣的:(Java)