Appearance
第7课:继承
🎯 学习目标
- 理解继承的概念和作用
- 掌握 extends 关键字
- 理解 super 关键字
- 掌握方法重写
- 理解 Object 类
📖 一、继承基础
C vs Java 继承
C 语言:
c
// C: 无继承,需手动组合
struct Animal {
char name[50];
};
struct Dog {
struct Animal animal; // 组合
char breed[50];
};Java:
java
// Java: 原生支持继承
class Animal {
String name;
}
class Dog extends Animal {
String breed;
}1. 继承语法
java
// 父类(超类、基类)
public class Animal {
String name;
int age;
public void eat() {
System.out.println(name + " is eating");
}
}
// 子类(派生类)
public class Dog extends Animal {
String breed;
public void bark() {
System.out.println(name + " is barking");
}
}
// 使用
Dog dog = new Dog();
dog.name = "旺财"; // 继承自 Animal
dog.age = 3; // 继承自 Animal
dog.breed = "哈士奇"; // Dog 自己的属性
dog.eat(); // 继承的方法
dog.bark(); // Dog 自己的方法2. 继承的特点
- ✅ 子类继承父类的所有非 private 成员
- ✅ Java 只支持单继承(一个类只能有一个直接父类)
- ✅ 支持多层继承(A → B → C)
- ❌ 不支持多重继承(C++ 支持)
java
// ✅ 单继承
class A { }
class B extends A { }
// ✅ 多层继承
class C extends B { }
// ❌ 多重继承(编译错误)
class D extends A, B { } // 错误📖 二、super 关键字
1. 访问父类成员
java
public class Animal {
String name = "动物";
public void eat() {
System.out.println("Animal eating");
}
}
public class Dog extends Animal {
String name = "狗";
public void printName() {
System.out.println(name); // 狗(子类的)
System.out.println(super.name); // 动物(父类的)
}
public void eat() {
super.eat(); // 调用父类方法
System.out.println("Dog eating");
}
}2. 调用父类构造方法
java
public class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Dog extends Animal {
String breed;
public Dog(String name, int age, String breed) {
super(name, age); // 调用父类构造方法(必须是第一条语句)
this.breed = breed;
}
}⚠️ 注意:
super()必须是构造方法的第一条语句- 如果不写,编译器自动调用
super()(无参构造) - 如果父类没有无参构造,子类必须显式调用有参构造
java
public class Animal {
public Animal(String name) { // 只有有参构造
}
}
public class Dog extends Animal {
public Dog() {
// super(); // ❌ 错误:父类没有无参构造
super("Dog"); // ✅ 正确
}
}📖 三、方法重写(Override)
1. 重写规则
java
public class Animal {
public void makeSound() {
System.out.println("Animal sound");
}
}
public class Dog extends Animal {
@Override // 可选注解,编译时检查
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
// 使用
Animal dog = new Dog();
dog.makeSound(); // Woof!(调用子类的方法)
Animal cat = new Cat();
cat.makeSound(); // Meow!2. 重写的要求
- ✅ 方法名相同
- ✅ 参数列表相同
- ✅ 返回类型相同(或子类)
- ✅ 访问权限不能更严格
- ❌ 不能重写 private、static、final 方法
java
public class Parent {
public void method() { }
}
public class Child extends Parent {
// ✅ 正确:访问权限相同
public void method() { }
// ❌ 错误:访问权限更严格
// protected void method() { }
}3. 重写 vs 重载
| 特性 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 关系 | 父类和子类 | 同一个类 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 相同 | 不同 |
| 返回类型 | 相同 | 可以不同 |
| 访问修饰符 | 不能更严格 | 无要求 |
📖 四、Object 类
1. Object 类简介
- Java 中所有类的根父类
- 如果没有 extends,默认继承 Object
- 提供了通用方法
java
public class MyClass { }
// 等价于
public class MyClass extends Object { }2. Object 类的常用方法
java
public class Object {
// 返回对象的字符串表示
public String toString() { }
// 比较两个对象是否相等
public boolean equals(Object obj) { }
// 返回哈希码
public int hashCode() { }
// 返回 Class 对象
public final Class<?> getClass() { }
// 克隆对象
protected Object clone() { }
}3. 重写 toString()
java
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 不重写时
// Person p = new Person("Alice", 25);
// System.out.println(p); // Person@15db9742(默认格式)
// 重写后
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// 使用
Person p = new Person("Alice", 25);
System.out.println(p); // Person{name='Alice', age=25}4. 重写 equals()
java
public class Person {
String name;
int age;
@Override
public boolean equals(Object obj) {
// 1. 检查是否是同一个对象
if (this == obj) return true;
// 2. 检查是否为 null 或类型不同
if (obj == null || getClass() != obj.getClass()) {
return false;
}
// 3. 比较属性
Person person = (Person) obj;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}📖 五、访问控制
| 修饰符 | 同一类 | 同一包 | 子类 | 任意位置 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| 默认 | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
java
public class Parent {
private int a = 1; // 子类不可访问
int b = 2; // 同包子类可访问
protected int c = 3; // 子类可访问
public int d = 4; // 都可访问
}
public class Child extends Parent {
public void test() {
// System.out.println(a); // ❌ 错误
System.out.println(b); // ✅ 同包
System.out.println(c); // ✅ protected
System.out.println(d); // ✅ public
}
}⚠️ 常见陷阱
陷阱1:为了复用代码而滥用继承
继承表达“is-a”关系,不应只为了复用几行代码。很多场景组合比继承更稳。
陷阱2:重写方法破坏父类约定
子类重写方法必须遵守父类方法的语义,否则多态调用会出现意外行为。
陷阱3:忘记调用 super
构造器、初始化逻辑或重写方法中,如果父类有必要逻辑,需要显式调用 super。
陷阱4:protected 滥用
protected 不是“更安全的 public”。暴露给子类的成员也会形成长期兼容负担。
🆚 Java vs C 对比
| 特性 | C | Java 继承 |
|---|---|---|
| 继承 | 无语言级继承 | 单继承 |
| 多态 | 函数指针模拟 | 虚方法动态绑定 |
| 基类 | 无统一根类型 | Object |
| 复用方式 | 组合 struct + 函数 | 继承、组合、接口 |
对 C 程序员来说,Java 继承像编译器和运行时内置的“结构体嵌套 + 虚函数表”机制,但更强调类型关系和多态契约。
💡 最佳实践
1. 遵循 IS-A 关系
java
// ✅ Dog IS-A Animal(狗是一种动物)
class Dog extends Animal { }
// ❌ Car IS-A Engine?(车不是引擎,应该用组合)
class Car {
Engine engine; // 组合
}2. 不要为了复用而继承
java
// ❌ 不好:为了复用 Stack 的方法
class MyList extends Stack { }
// ✅ 好:组合
class MyList {
private Stack stack = new Stack();
}3. 父类应该是抽象的概念
java
// ✅ 好
class Animal { } // 抽象概念
class Dog extends Animal { }
// ❌ 不好
class Dog { }
class Husky extends Dog { } // 太具体了📝 练习
完成 练习/Ex07_Inheritance.java:
- 继承基本使用
- super 关键字
- 方法重写
- Object 类方法
- 访问控制
- 综合:员工管理系统
🎓 下一步
- 第8课:多态 - 向上转型、向下转型、动态绑定