Java里的继承语法和面向对象的基本设计直接借鉴了C++。C++的类、继承、多态这些概念给Java提供了基础模版。
虽然采用了C++的继承机制,但是有摒弃了复杂的多继承,使用了单继承+接口的设计来避免多继承的菱形问题。
总体来说,Java的继承机制主要融合了C++的语法结构、Simula的OOP根基以及Smalltalk的纯对象模型。
同时通过接口借鉴了Objective-C的协议思想。
这样的设计既保留了经典OOP的清晰性,又通过简化(如单继承)和扩展(如接口)提升了代码的可维护性和安全性。
一、什么是继承?
在Java里,继承是指一个类可以获得另一个类的属性和方法。
通俗点讲,子类可以继承分类的功能,还能在此基础上拓展自己的行为。
package com.lazy.snail.day12;
/**
* @ClassName Animal
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Animal {
public void eat() {
System.out.println("吃东西");
}
}
Animal是定义的一个父类。
package com.lazy.snail.day12;
/**
* @ClassName Dog
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Dog extends Animal {
public void bark() {
System.out.println("汪汪汪");
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
dog.bark();
}
}
Dog是定义的一个子类,通过extends关键字继承了Animal类。
UML类图可以通过下面的方式查看:
UML类图如下:
可以清晰的看出Dog类继承自Animal。
当在main方法创建Dog对象时,不仅可以调用Animal中定义的eat方法,也可以调用Dog中定义的bark方法。
eat方法就是Dog从Animal中继承过来的。
二、继承语法规则
Java中使用extends关键字来表示继承关系。
Java只支持单继承,一个子类只能继承一个父类。
所有的类都隐式的继承Object类(java.lang.Object)。
class A {}
class B extends A {}
class C extends A,B {} // 编译错误,不支持
看一下Object类:
IDEA左侧的Structure按钮可以列出类的所有方法和属性。
Object类开头有这样一段类注释:
Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.
Object类是整个类继承体系的根。每个类都以Object作为超类(父类)。所有对象,包括数组,都实现这个类的方法。
Dog类和Animal类中并没有定义类似toString、equals、clone、wait、notify等方法,但是dog对象却可以直接调用这些方法,就是因为Object作为了所有类的父类。子类继承了这些方法。
三、super关键字
super是子类访问父类成员的一种方式,主要有下面三种用法:
1)访问父类的成员变量
package com.lazy.snail.day12;
/**
* @ClassName Animal
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Animal {
String name = "动物";
}
父类定义一个成员变量name赋值为"动物"。
package com.lazy.snail.day12;
/**
* @ClassName Dog
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Dog extends Animal {
String name = "狗子";
public void printName() {
System.out.println(this.name);
System.out.println(super.name);
}
}
子类Dog同样定义一个成员变量name赋值为"狗子"。
super.name就是访问的父类Animal的name属性。
2)调用父类的方法
package com.lazy.snail.day12;
/**
* @ClassName Animal
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Animal {
void run() {
System.out.println("动物在跑");
}
}
父类中定义一个run方法。
package com.lazy.snail.day12;
/**
* @ClassName Dog
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Dog extends Animal {
@Override
void run() {
super.run(); // 调用父类的 run()
System.out.println("狗在跑");
}
}
子类中使用super直接调用父类的run方法。
3)在构造方法中调用父类构造
package com.lazy.snail.day12;
/**
* @ClassName Animal
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Animal {
public Animal(String name) {
System.out.println("Animal 构造:" + name);
}
}
定义一个有参构造Animal(String name)。
package com.lazy.snail.day12;
/**
* @ClassName Dog
* @Description TODO
* @Author lazysnail
* @Date 2025/5/27 10:28
* @Version 1.0
*/
public class Dog extends Animal {
public Dog() {
super("狗子");
System.out.println("Dog 构造");
}
}
Dog中的无参构造,使用super调用Animal的有参构造。
四、子类构造方法
构造方法不是“自动继承”的。
子类构造方法必须负责调用父类构造方法。
如果父类没有无参构造方法,子类必须显式调用super(...),否则编译错误!
class A {
public A(String msg) {}
}
class B extends A {
// 编译错误:找不到默认构造方法 A()
public B() {}
}
五、方法的重写
子类可以重写(Override)父类的方法。在重写的时候有下面几个要求:
方法名相同,参数列表完全一致。
返回值类型相同或为其子类(协变返回类型)
子类方法访问权限不能比父类更严格
可使用 @Override 注解提高可读性
在第三章第二节中Dog类中的run实际就是对Animal中的run方法的重写。
Dog有自己特定的叫声,不想继承动物的泛性的叫声,所以通过重写来修改这种行为。
六、常见疑问
Q:构造方法能被继承吗?
不能。构造方法属于类本身,不会被子类继承。但可以在子类中调用。
Q:Java为什么只支持单继承?
为了避免砖石继承(菱形问题),Java通过接口机制代替多继承。
Q:子类能否继承 private 成员?
不能直接访问。但仍然继承了,只是不可见。可以通过公共方法访问。
结语
本文就不做文字性的小结了,直接上关键点总结:
| 关键词 | 关键点 |
|---|---|
| extends | 建立子类与父类的继承关系 |
| super | 子类访问父类的通道 |
| 构造顺序 | 先构造父类 → 再构造子类 |
| 方法重写 | 子类重新定义父类已有的方法 |
| 访问限制 | private 不可见,protected 子类可访问 |
| 单继承 | 一个类只能继承一个父类,避免复杂冲突 |
下一篇预告
Day13 | Java多态详解
如果你觉得这系列文章对你有帮助,欢迎关注专栏,我们一起坚持下去!
更多文章请关注我的公众号《懒惰蜗牛工坊》