前言

本篇文章将介绍Java继承的相关内容


关于继承

简而言之,继承主要解决的问题就是:共性抽取

代码定义:

//父类:也称作基类、超类
public class 父类名称 {
    // ...
}
//子类:也称作派生类
public class 子类名称 extends 父类名称 {
    // ...
}

最高父类其实就是一个普通类,这点在代码定义时就可以看出来;

在继承的关系中,“子类就是一个父类”。也就是说,子类可以被当做父类看待。(例如:父类是员工,其子类有经理、业务员。那么可以有这样的表达:“经理、业务员是员工”)

在继承关系中:

  • 子类可以拥有父类的“内容”

  • 子类还可以定义除父类之外的专有内容

  • Java语言是单继承的:一个类的直接父类只能有唯一一个

  • Java语言可以多级继承

class A {}
Class B extends A {}
class C extends B {}
  • 一个父类可以拥有很多个子类:
class A {}
class B extends A {}
class C extends A {}

用法

基本使用

定义父类Father

public class Father {
    String father_string = "这是父类变量";
    public void father_method() {
        System.out.println("这是父类方法");
    }
}

定义子类Son

public class Son extends Father {
    String son_string = "这是子类变量";
    public void son_method() {
        System.out.println("这是子类方法");
    }
}

使用类Demo

public class Demo {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println(son.father_string);  //访问父类变量
        System.out.println(son.son_string);  //访问子类变量
        son.father_method();  //访问父类方法
        son.son_method();  //访问子类方法
    }
}

运行结果:

这是父类变量
这是子类变量
这是父类方法
这是子类方法

成员变量访问

  • 局部变量:局部变量名
  • 本类的成员变量:this.成员变量名
  • 父类的成员变量:super.成员变量名

定义父类Father

public class Father {
    String words = "父类变量";
}

定义子类Son

public class Son extends Father {
    String words = "子类变量";

    public void son_method() {
        String words = "局部变量";
        System.out.println(words);  //局部变量
        System.out.println(this.words);  //子类变量
        System.out.println(super.words);  //父类变量
    }
}

使用类Demo

public class Demo {
    public static void main(String[] args) {
        Son son = new Son();
        son.son_method();  //访问子类方法
    }
}

运行结果:

局部变量
子类变量
父类变量

成员方法访问

基本使用

访问规则:创建的对象是谁,就优先用谁的方法,如果没有,则向上找父类

重写

在继承关系当中,方法名称一样,参数列表也一样。

注意区别重写与重载:

  • 重写(Override):方法的名称一样,参数列表也一样
  • 重载(Overload):方法的名称一样,参数列表不一样

注解@Override:写在方法前面,用来检测是不是有效的正确重写。(也可以不写,但建议书写,起到检测作用)

定义父类Father

public class Father {
    public void method() {
        System.out.println("父类重名方法执行");
    }
}

定义子类Son

public class Son extends Father {
    @Override
    public void method() {
        System.out.println("子类重名方法执行");
    }
}

使用类Demo

public class Demo {
    public static void main(String[] args) {
        Son son = new Son();
        son.method();  //访问子类方法
    }
}

运行结果:

子类重名方法执行

重写注意事项:

  • 如果父类方法访问修饰符为private子类就不能重写该方法
  • 子类方法的抛出异常范围必须小于等于父类方法的抛出异常范围
  • 子类方法的返回值必须小于等于父类方法的返回值范围
    java.lang.Object类是所有类的公共最高父类,而java.lang.String类就是Object的子类
  • 子类方法的权限必须大于等于父类方法的权限修饰符
    public > protected > (default) > private(注:(default)不是关键字default,而是什么都不写,留空)

构造方法访问

基本使用

继承关系中,构造方法的访问特点:

  • 子类构造方法当中有一个默认隐含的super();调用,所以一定是先调用的父类构造方法,后执行的子类构造方法
  • 子类构造方法可以通过super关键字来调用父类重载构造方法
  • super的父类构造调用,必须是子类构造方法的第一个语句(不能一个子类构造调用多次super构造)

定义父类Father

public class Father {
    public Father() {
        System.out.println("父类无参构造方法");
    }
}

定义子类Son

public class Son extends Father {
    public Son() {
        System.out.println("子类无参构造方法");
    }
}

使用类Demo

public class Demo {
    public static void main(String[] args) {
        Son son = new Son();
    }
}

运行结果:

父类无参构造方法
子类无参构造方法

super

用法:

  • 在子类的成员方法中,访问父类的成员变量
  • 在子类的成员方法中,访问父类的成员方法
  • 在子类的构造方法中,访问父类的构造方法

定义父类Father

public class Father {
    String string = "父类变量";

    public Father() {
        System.out.println("父类无参构造方法");
    }

    public Father(String string) {
        this.string = string;
        System.out.println("父类有参构造方法");
    }
}

定义子类Son

public class Son extends Father {
    String string = "子类变量";

    public Son() {
        super("调用父类的有参构造方法");
        System.out.println("子类无参构造方法");
    }

    public Son(String string) {
        this.string = string;
        System.out.println("子类有参构造方法");
    }
}

使用类Demo

public class Demo {
    public static void main(String[] args) {
        Son son = new Son();
    }
}

运行结果:

父类有参构造方法
子类无参构造方法

this

用法:

  • 在本类的成员方法中,访问本类的成员变量
  • 在本类的成员方法中,访问本类的另一个成员方法
  • 在本类的构造方法中,访问本类的另一个构造方法

注意:

  • this(...)调用也必须是构造方法的第一个语句,且是唯一的一个
  • super(...)this(...)两种构造调用,不能同时使用

使用类Demo

public class Demo {
    int num;
    String words;

    public Demo() {
        this(1);
        System.out.println("无参构造方法");
    }

    public Demo(int num) {
        this(1,"参数");
        this.num = num;
        System.out.println("仅含一个参数的构造方法");
    }

    public Demo(int num, String words) {
        this.num = num;
        this.words = words;
        System.out.println("全参构造方法");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
    }
}

运行结果:

全参构造方法
仅含一个参数的构造方法
无参构造方法

抽象(abstract)

如果父类中的方法不确定如何具体实现方法体的内容,那么就应该考虑使用抽象方法

例如:父类为Animal,其有两个子类DogCat。在Animal父类中定义了一个Eat(...){...}的方法,但是由于狗啃骨头、猫吃鱼,这两个子类的吃行为是不一样的,无法在父类的方法体中进行准确定义,这时就需要将Eat(...)定义为抽象方法

概念:

  • 抽象方法:用abstract关键字修饰,然后去掉方法体,直接分号结束
    public abstract void method(...);
  • 抽象类:抽象方法所在的类,必须是抽象类才行。在class之前abstract关键字修饰

使用时需要注意:

  • 不能直接new抽象类对象
  • 必须用一个子类来继承抽象父类
  • 子类必须覆盖重写抽象父类当中所有的抽象方法,否则该子类也必须为抽象类
  • 抽象类需要创建子类对象进行使用
  • 一个抽象类不一定含有抽象方法,只要保证抽象方法所在的类是抽象类即可(在一些特殊场景下有用途)

定义父类Father

public abstract class Father {
    public abstract void method();

    public void normalMethod() {
        System.out.println("普通的成员方法(子类不用重写此方法)");
    }
}

定义子类Son

public class Son extends Father {
    @Override
    public void method() {
        System.out.println("重写父类的抽象方法");
    }
}

使用类Demo

public class Demo {
    public static void main(String[] args) {
        Son son = new Son();
        son.method();
    }
}

运行结果:

重写父类的抽象方法