Java面向对象编程之 继承性 | Java OOP Inheritance

Made by Mike_Zhang


(二次更新:2021-09-18)

Java主题:


OOP Introduction

请阅读本博客的Java面向对象编程详细介绍 - Java OOP Detailed Introduction


继承性(Inheritance)

类的继承是OOP中非常重要的概念, 它的核心思想是扩展某一个父类, 得到一个子类. 子类继承类它的父类的属性和方法, 子类也可以有新的不同于父类的属性和方法, 也可以重写父类中的一些方法. 举例来说, 定义一个父类为三角形类, 而直角三角形类就是三角形类的子类, 子类拥有父类具有的属性和方法, 直角三角形类也拥有其父类不具有的属性和方法, 比如说直角三角形类一个角为90度 这一属性就是直角三角形类特有的属性,同样也可以重写父类具有的属性和方法。

extends是Java中用来表明继承关系的关键字

以下为例子:

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
45
46
47
48
49
50
51
52
53
54
55
56
public class Triangle { // 三角形类
public Triangle(){ //父类构造方法
System.out.println("This is Triangle");
}
public Triangle(int i){ //父类有参构造方法
System.out.println("A angle of the Triangle is " + i);
}
private float triangleAngleA, triangleAngleB, triangleAngleC;

public void setAngle(){
System.out.println("Please set triangleAngleA, B and C");
}
protected void getTriangleAngle(){ //成员方法
System.out.println("triangleAngleA + triangleAngleB + triangleAngleC = 180");
}
}

class RightTriangle extends Triangle{ //定义直角三角形子类继承三角形父类
public RightTriangle(){ //子类构造方法
System.out.println("This is Right Triangle");
}
public RightTriangle(int i){ //子类有参构造方法
super(i);
System.out.println("A angle of the Right Triangle is " + i);
}
public void setRightAngle(){ //在子类中添加新的成员方法
System.out.println("triangleAngleC is 90");
}
public void getTriangleAngle(){ //重写父类中的成员方法
System.out.println("triangleAngleC = 90, and ");
super.getTriangleAngle(); //调用父类方法
}
}

class IsoscelesRightTriangle extends RightTriangle{ //定义等腰直角三角形子类继承直角三角形父类
public IsoscelesRightTriangle(){ //子类构造方法
System.out.println("This is Isosceles Right Triangle");
}
public IsoscelesRightTriangle(int i){ //子类有参构造方法
super(i);
System.out.println("A angle of the Isosceles Right Triangle is " + i);
}
public void setIsoscelesRightAngle(){ //在子类中添加新的成员方法
System.out.println("triangleAngleA and B are 45, triangleAngleC is 90");
}
public void getTriangleAngle(){ //重写父类中的成员方法
System.out.println("triangleAngleA & B = 45, and ");
super.getTriangleAngle(); //调用父类方法
}
public static void main(String[] args){ // 主函数 main()
IsoscelesRightTriangle x = new IsoscelesRightTriangle(); // 创建一个等腰直角三角形类的方法
x.setAngle(); // 调用三角形类的方法
x.setRightAngle(); // 调用直角三角形类的方法
x.setIsoscelesRightAngle(); // 调用本类的方法
}
}

运行结果:

1
2
3
4
5
6
This is Triangle
This is Right Triangle
This is Isosceles Right Triangle
Please set triangleAngleA, B and C
triangleAngleC is 90
triangleAngleA and B are 45, triangleAngleC is 90

1. 被继承的成员

当一子类继承父类后,父类的实例变量将成为子类的一部分,父类的属性和方法可以被继承到子类,但是构造方法static initializerinstance initializer并不是实例变量,所以不可以被继承
判断实例变量是否可以继承到子类中,需要根据子类对父类实例变量的可访问性(accessibility)。子类继承了父类的某一成员,说明子类是可以访问到这一成员的。


2. 重写方法

子类只能重写能够从父类中继承的方法,否则,如private修饰的方法不能被重写。
重写的方法必须有相同的Signature (i.e. method name, method argument type list),如:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Hero{
public void run(int x){ … }

}
public class Wizard extends Hero{
public void run(int x){ … } // overriding, same signature, a new version of the method in superclass

}
public class Knight extends Hero{
public void run(float x){ … } // overloading, different signature, redefined a new method

}


3. 权限修饰符

一般来说,为了保护继承性,成员属性修饰为private,成员方法修饰为public。尽管protected修饰的属性和方法可以在继承类间访问,但是最好还是让成员属性被private修饰。
子类在重写父类中的方法时, 权限的修改范围只能从小到大, protected修饰的方法只能重写成public, 而不能重写成private;


4. super关键字

super关键字与this关键字的作用十分类似。

注意到例子三个类中都有getTriangleAngle()方法,因为子类可以使用或者修改父类中的方法。但是不能直接使用getTriangleAngle()语句调用父类方法,这样会造成自我循环调用的错误。需要使用super关键字来引用到目前子类的父类,如super.getTriangleAngle(),意思为调用目前子类的父类的getTriangleAngle()方法。但是只能调用public和protected修饰的方法。


5. 构造方法

在Java中构造方法不会被继承,构造方法分为无参构造方法有参构造方法

5.1 无参构造方法

当子类创建对象时,会先调用其父类的无参构造方法。而父亲必须包含一个无参构造方法,否则子类无法隐形调用其父类的无参构造方法

例子中,mian()中创建了IsoscelesRightTriangle类的x对象,它会调用其父类RightTriangle的构造方法,而在RightTriangle被调用时,需要先调用其父类Triangle的构造方法,一层层向上
因此最先完成调用的是最大的父类Triangle的构造方法,因此先输出This is Triangle,再完成调用子类RightTriangle的构造方法,输出This is Right Triangle,最后完成调用子类IsoscelesRightTriangle的构造方法,输出This is Isosceles Right Triangle一层层向下

因此,运行后输出为:

1
2
3
4
5
6
This is Triangle // 调用Triangle类的无参构造方法
This is Right Triangle // 调用RightTriangle类的无参构造方法
This is Isosceles Right Triangle // 调用IsoscelesRightTriangle类的无参构造方法
Please set triangleAngleA, B and C
triangleAngleC is 90
triangleAngleA and B are 45, triangleAngleC is 90

注意:
子类构造方法第一句语句一直都是调用某一构造方法,可以是:

  1. 子类构造方法显性重载,使用this关键字;
  2. 对其父类构造方法显性调用,使用super关键字;
  3. 隐性调用父类的无参构造方法,当没有显性调用构造方法时,编译器会自动调用,使用super关键字,如:

    就算子类IsoscelesRightTriangle没有构造方法,Java编译器也会自动调用其父类的构造方法。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A{
public A(){System.out.print(“1”);}
public A(int x){System.out.print(“2”);}
}
class B extends A{
public B(){
// NO super(); inserted by the compiler for an explicit constructor this(1); exists
this(1); // explicit constructor
System.out.print(“3”);
}
public B(float x){
// super(); // inserted by the compiler
System.out.print(“4”);
}
}
class C extends B{
public C(){
// super(); // inserted by the compiler
System.out.print(“5”);
}
}

C cObj = new C();的输出为1435


5.2 有参构造方法

父类无参构造方法能够被自动调用,但是调用父类有参构造方法需要在子类构造方法中用super关键字表明,若无,则不会调用。如下:

修改例子中的main()方法,其余不变,以测试有参构造方法:

1
2
3
4
5
6
public static void main(String[] args){ // 主函数 main()
IsoscelesRightTriangle x = new IsoscelesRightTriangle(45); // 有参构造方法
x.setAngle(); // 调用三角形类的方法
x.setRightAngle(); // 调用直角三角形类的方法
x.setIsoscelesRightAngle(); // 调用本类的方法
}

输出结果:

1
2
3
4
5
6
A angle of the Triangle is 45 // 调用Triangle类的有参构造方法
A angle of the Right Triangle is 45 // 调用RightTriangle类的有参构造方法
A angle of the Isosceles Right Triangle is 45 // 调用IsoscelesRightTriangle类的有参构造方法
Please set triangleAngleA, B and C
triangleAngleC is 90
triangleAngleA and B are 45, triangleAngleC is 90

注意:
在子类中,调用其父类构造方法的语句 必须是 此子类构造方法体中的 第一行被执行的语句 ,否则会报错,例子:

1
2
3
4
public IsoscelesRightTriangle(int i){ //子类有参构造方法
System.out.println("A angle of the Isosceles Right Triangle is " + i);
super(i); //没有写在构造方法的最前面
}

报错:
Call to 'super()' must be first statement in constructor body


6. Object

就算某一个类没有确切表明继承了父类,此类也已经自动的继承了java.lang.Object类,此为Java中的统一父类(Universal Superclass)。


参考

B. Eckel, Thinking in java. Upper Saddle River, N.Y. Prentice Hall, 2014.
M. Goodrich, R. Tamassia, and A. O’reilly, Data Structures and Algorithms in Java, 6th Edition. John Wiley & Sons, 2014.


写在最后

Java中OOP相关的知识是十分重要的, 会继续更新.
最后,希望大家一起交流,分享,指出问题,谢谢!


原创文章,转载请标明出处
Made by Mike_Zhang




感谢你的支持

Java面向对象编程之 继承性 | Java OOP Inheritance
https://ultrafish.io/post/Java-oop-inheritance/
Author
Mike_Zhang
Posted on
September 13, 2020
Licensed under