Java 继承
继承是什么
继承通常用来表示类之间的 is-a
关系,比如猫是一种哺乳动物;汽车是一种机器,香樟树是一种植物等,继承符合人类的认知思维,比较好理解。
继承有单继承和多继承之分。Java 中的继承是单继承。继承的意义一是可以代码复用,相同的属性和方法可以抽取到父类之中,在子类中直接调用,避免重复的代码。
但是继承也有很多问题,如果继承层次如果过多,代码的阅读会非常复杂,且子类和父类高度耦合,修改父类代码有时会影响子类。也因此有些人支持多用组合少用继承。
但是不管怎么说,继承都是面向对象中的重要概念,需要详细的介绍一下。
为什么要继承
有时候,你会遇到大费周折的创建了一个类后,又要创建一个和它只有一点点差异的类,如果重写拷贝一份代码,这种感觉不太好受,我们希望能复制现有的类,然后在此基础上对齐进行增改,那感觉就十分美妙了。
继承相关概念
父类:又叫基类,超类,指最初的类,比如猫是一种动物中,动物是猫的父类。
子类:又叫派生类,继承类,比如猫是一种动物中,猫是子类,是父类动物的派生类。
举例:JDK 中 ArrayList
类继承了父类 AbstractList
,AbstractList
又继承了 AbstractCollection
。这里可以说 AbstractCollection
是 AbstractList
和 ArrayList
的父类,ArrayList
是两者的子类。
Java 的继承
Java 中的继承使用关键字 extends
表示,比如我们人类都需要吃饭睡觉,而学生作为人类的基础上,还要进行学习,用 Java 的继承表示就是下面的代码。
class Person {
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
class Student extends Person {
public void study() {
System.out.println("学习");
}
}
如果没有继承功能,那么这个吃饭睡觉的人类共同需求,就需要在遇到学生、老师、打工人等角色定义时重复一遍。
在 Java 中,子类会继承父类除了 private
权限的所有信息,比如属性、方法、接口等。
public static void main(String[] args) {
Student student = new Student();
student.eat(); // 吃饭
student.sleep(); // 睡觉
student.study(); // 学习
}
Java 继承的特点
单继承
在 Java 中,继承只能是单继承,用上面的例子来说,如果 Student
继承了 Person
,那么它就不能再继承其他任何类了。你想像下面这样,在 Java 中是不支持的。
class Person{
}
class ClassXX{
}
class Student extends Person,ClassXX{ // 报错,不支持
}
因为单继承的原因,所以在 Java 中,一个类最多只有一个父类,Object 类是所有类的父类。因此 Java 是单根层级结构,这让我们可以方便的使用 Object 类型来作为通用类型。
子类权限
子类可以调用父类的非 private 属性和方法,使用 super
关键词可以调用父类的属性或信息,使用 this
关键字调用当前子类的属性或信息。
class Student extends Person {
public Student() {
super(); // 调用父类无参构造
}
}
如果 sleep
方法是被 private
修饰,那么不能被继承。
class Person {
public void eat() {
System.out.println("吃饭");
}
private void sleep() {
System.out.println("睡觉");
}
}
class Student extends Person {
public void study() {
System.out.println("学习");
}
}
此时使用 Student
对象调用 sleep
是会报错的。
Student student = new Student();
student.eat();
student.sleep();
student.study();
// java: 找不到符号
// 符号: 方法 sleep()
方法重写
在继承时,子类可以重写父类的方法实现,比如下面的例子中,子类 Student
重写了父类中的 Sleep
方法。
class Person {
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
class Student extends Person {
public void study() {
System.out.println("学习");
}
@Override
public void sleep() {
System.out.println("上课时不能睡觉");
}
}
子类调用方法时会输出自己的方法实现结果。
Student student = new Student();
student.sleep(); // 上课时不能睡觉
多重继承
Java 虽然只能单继承,但是多重继承是被允许的,像下面这样。
子类和基类有相同类型
在方法 printSleep
中,指定参数类型为 Person
,但是我们传入 Person
的子类。
package com.wdbyte.oop;
public class JavaExtends {
public static void main(String[] args) {
Student student = new Student();
printSleep(student);
}
public static void printSleep(Person person){
person.sleep(); // 上课时不能睡觉
}
}
class Person {
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
class Student extends Person {
public void study() {
System.out.println("学习");
}
@Override
public void sleep() {
System.out.println("上课时不能睡觉");
}
}
Java 继承多个
从上面的介绍中可以发现,在 Java 中继承只能是单继承,如果有时我们需要多继承,可以通过接口的多继承特性来实现类的多继承。
interface One {
public void print_geek();
}
interface Two {
public void print_for();
}
interface Three extends One, Two {
public void print_geek();
}
class Teach implements Three{
@Override
public void print_for() {
}
@Override
public void print_geek() {
}
}
他们的继承关系图如下:
Java 继承的优点
- 复用性:继承允许子类继承父类的所有方法和属性,从而避免了重复编写类似的代码。
- 扩展性:通过继承,子类可以添加新的方法和属性,从而扩展父类的功能。
- 多态性:继承允许子类重写父类的方法,从而实现多态性,即同一个方法在不同情况下会呈现不同的行为。
- 代码逻辑清晰:通过继承,可以将代码结构化,并使代码逻辑更加清晰。
- 可维护性:通过继承,可以提高代码的可维护性,因为当需要修改某个方法或属性时,只需要修改父类即可,而不必修改每个子类。
Java 继承的缺点
- 牵一发而动全身:继承具有传递性,子类继承了父类的属性和方法,如果父类中某个属性或方法需要修改,那么可能会影响到所有继承了该父类的子类,导致代码的不稳定和难以维护。
- 继承的滥用:有些开发者可能过度使用继承,导致类之间的继承关系过于复杂,难以理解和维护。
- 破坏封装性:继承允许子类访问父类的 protected 成员变量和方法,虽然保护了一定的封装性,但是也可能破坏了类的封装性,导致代码的安全性下降。
- 耦合度高:类之间的继承关系会造成紧耦合,一个类的修改可能会影响到其他的类,造成不必要的麻烦。
- 限制类的设计:继承是静态的,一旦继承关系建立,就不能轻易地改变。如果后面需要修改继承关系,可能需要对大量的代码进行修改,增加了代码的维护成本。同时,也限制了类的设计和扩展。
is-a 和 is-like -a
通过介绍,可以发现在继承实现时,通常有两种区别。
- 简单继承,不增加新的方法,可能会对父类的中的方法进行方法重写。这时子类和父类符合
is-a
关系,也可以称为替换原则,这是一种比较理想的继承方式。 - 继承后增加新的方法,扩展原来类的功能,这时符合
is-like-a
关系,只能说明子类和父类很像,这种继承方式不如第一种好,会增加代码的复杂度。
一如既往,文章中代码存放在 Github.com/niumoo/javaNotes.