Java--继承

前言

几个月不看就又忘了,看了一遍顺便记录一下。


关于继承

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

代码定义:

  • 父类:也称作基类超类

    1
    2
    3
    public class 父类名称 {
    // ...
    }
  • 子类:也称作派生类

    1
    2
    3
    public class 子类名称 extends 父类名称 {
    // ...
    }

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

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

在继承关系中:

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

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

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

  4. Java语言可以多级继承

    1
    2
    3
    class A {}
    Class B extends A {}
    class C extends B {}
  5. 一个父类可以拥有很多个子类:

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

用法

基本使用

定义父类Fu

1
2
3
4
5
6
public class Fu {
String Fu_string = "这是父类变量";
public void Fu_method() {
System.out.println("这是父类方法");
}
}

定义子类Zi

1
2
3
4
5
6
public class Zi extends Fu{
String Zi_string = "这是子类变量";
public void Zi_method() {
System.out.println("这是子类方法");
}
}

使用类Demo

1
2
3
4
5
6
7
8
9
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println(zi.Fu_string); //访问父类变量
System.out.println(zi.Zi_string); //访问子类变量
zi.Fu_method(); //访问父类方法
zi.Zi_method(); //访问子类方法
}
}

运行结果:

1
2
3
4
这是父类变量
这是子类变量
这是父类方法
这是子类方法

成员变量访问

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

定义父类Fu

1
2
3
public class Fu {
String words = "父类变量";
}

定义子类Zi

1
2
3
4
5
6
7
8
9
10
11
public class Zi extends Fu{
String words = "子类变量";

public void Zi_method() {
String words = "局部变量";

System.out.println(words); //局部变量
System.out.println(this.words); //子类变量
System.out.println(super.words); //父类变量
}
}

使用类Demo

1
2
3
4
5
6
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.Zi_method();
}
}

运行结果:

1
2
3
局部变量
子类变量
父类变量

成员方法访问

基本使用

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

重写

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

注意区别重写与重载:

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

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

定义父类Fu

1
2
3
4
5
public class Fu {
public void method() {
System.out.println("父类重名方法执行");
}
}

定义子类Zi

1
2
3
4
5
6
public class Zi extends Fu{
@Override
public void method() {
System.out.println("子类重名方法执行");
}
}

使用类Demo

1
2
3
4
5
6
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}

运行结果:

1
子类重名方法执行

在继承关系中定义方法需要注意:

  • 子类方法的返回值必须小于等于父类方法的返回值范围

    java.lang.Object类是所有类的公共最高父类,而java.lang.String类就是Object的子类

  • 子类方法的权限必须大于等于父类方法的权限修饰符

    public > protected > (default) > private(注:(default)不是关键字default,而是什么都不写,留空)

构造方法访问

基本使用

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

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

定义父类Fu

1
2
3
4
5
public class Fu {
public Fu() {
System.out.println("父类无参构造方法");
}
}

定义子类Zi

1
2
3
4
5
public class Zi extends Fu{
public Zi() {
System.out.println("子类无参构造方法");
}
}

使用类Demo

1
2
3
4
5
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
}
}

运行结果:

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

Super

用法:

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

定义父类Fu

1
2
3
4
5
6
7
8
9
10
11
12
public class Fu {
String string = "父类变量";

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

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

定义子类Zi

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Zi extends Fu{
String string = "子类变量";

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

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

使用类Demo

1
2
3
4
5
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
}
}

运行结果:

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

this

用法:

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

注意:

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

Demo

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
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();
}
}

运行结果:

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

抽象(abstract)

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

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

概念:

  • 抽象方法:用abstract关键字修饰,然后去掉方法体,直接分号结束

    public abstract void method(...);

  • 抽象类:抽象方法所在的类,必须是抽象类才行。在class之前用abstract关键字修饰

使用时需要注意:

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

定义抽象父类Fu

1
2
3
4
5
6
7
public abstract class Fu {
public abstract void method();

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

定义子类Zi

1
2
3
4
5
6
public class Zi extends Fu {
@Override
public void method() {
System.out.println("重写父类的抽象方法");
}
}

使用类Demo

1
2
3
4
5
6
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}

运行结果:

1
重写父类的抽象方法

后记