Java 继承和多态
继承和多态是面向对象编程的核心特性。
继承
基本概念
继承允许创建一个类基于现有类,继承父类的属性和方法。
语法
// 父类
public class Animal {
public String name;
public int age;
public void eat() {
System.out.println("正在吃饭");
}
public void sleep() {
System.out.println("正在睡觉");
}
}
// 子类
public class Dog extends Animal {
public String breed;
// 子类特有方法
public void bark() {
System.out.println("汪汪叫");
}
}
使用继承
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "旺财";
dog.breed = "金毛";
dog.eat(); // 继承的方法
dog.sleep(); // 继承的方法
dog.bark(); // 子类特有的方法
}
}
super 关键字
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
}
public class Dog extends Animal {
public String breed;
public Dog(String name, String breed) {
super(name); // 调用父类构造方法
this.breed = breed;
}
}
方法重写
子类可以重写父类的方法:
public class Animal {
public void eat() {
System.out.println("正在吃饭");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("正在吃狗粮");
}
}
重写规则
- 方法名相同
- 参数列表相同
- 返回类型相同或兼容
- 访问权限不能比父类更严格
多态
同一接口,不同实现:
// 父类引用指向子类对象
Animal animal = new Dog();
animal.eat(); // 调用 Dog 的 eat 方法
向上转型和向下转型
// 向上转型(自动)
Animal animal = new Dog();
// 向下转型(需要强制转换)
Dog dog = (Dog) animal;
多态的应用
public class Animal {
public void speak() {
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("汪汪");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("喵喵");
}
}
public class Main {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat()};
for (Animal animal : animals) {
animal.speak(); // 多态调用
}
}
}
final 关键字
// final 类(不能被继承)
public final class String {
}
// final 方法(不能被重写)
public class Parent {
public final void method() {
}
}
// final 变量(常量)
public class Constants {
public static final double PI = 3.14159;
}
抽象类和抽象方法
抽象类
public abstract class Animal {
public String name;
// 抽象方法(没有方法体)
public abstract void speak();
// 普通方法
public void eat() {
System.out.println("正在吃饭");
}
}
实现抽象类
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("汪汪");
}
}
接口
定义接口
public interface Flyable {
// 接口中的方法默认是抽象的
void fly();
// 常量
int MAX_SPEED = 100;
}
实现接口
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟儿在飞翔");
}
}
多实现
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("鸭子在飞");
}
@Override
public void swim() {
System.out.println("鸭子在游泳");
}
}
接口的默认方法(Java 8+)
public interface Flyable {
void fly();
// 默认方法
default void display() {
System.out.println("这是一个飞行器");
}
}
接口的静态方法(Java 8+)
public interface Flyable {
static int getMaxSpeed() {
return 100;
}
}
接口的私有方法(Java 9+)
public interface Flyable {
default void display() {
printInfo();
}
private void printInfo() {
System.out.println("飞行器信息");
}
}
Object 类详解
在 Java 中,所有类都直接或间接继承自 java.lang.Object 类。Object 类是 Java 类层次结构的根类。
Object 类的方法
| 方法 | 说明 |
|---|---|
toString() | 返回对象的字符串表示 |
equals(Object obj) | 判断两个对象是否相等 |
hashCode() | 返回对象的哈希码 |
getClass() | 返回对象的运行时类 |
clone() | 创建并返回对象的副本 |
finalize() | 垃圾回收时调用(已废弃) |
notify() | 唤醒等待的线程 |
notifyAll() | 唤醒所有等待的线程 |
wait() | 让线程等待 |
toString() 方法
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 重写 toString 方法
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// 使用
Person p = new Person("张三", 20);
System.out.println(p); // 自动调用 toString()
// 输出: Person{name='张三', age=20}
// 不重写时的默认输出
// 输出: Person@15db9742(类名@哈希码)
equals() 和 hashCode()
import java.util.Objects;
public class Person {
private String name;
private int age;
// 重写 equals 方法
@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 && Objects.equals(name, person.name);
}
// 重写 hashCode 方法(equals 和 hashCode 必须同时重写)
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
// 使用
Person p1 = new Person("张三", 20);
Person p2 = new Person("张三", 20);
System.out.println(p1 == p2); // false(不同对象)
System.out.println(p1.equals(p2)); // true(内容相同)
// hashCode 相同是 equals 为 true 的必要条件
System.out.println(p1.hashCode() == p2.hashCode()); // true
getClass() 方法
Person p = new Person("张三", 20);
// 获取运行时类
Class<?> clazz = p.getClass();
System.out.println(clazz.getName()); // "Person"
System.out.println(clazz.getSimpleName()); // "Person"
// 判断对象类型
if (p.getClass() == Person.class) {
System.out.println("是 Person 类");
}
// 使用 instanceof(推荐)
if (p instanceof Person) {
System.out.println("是 Person 实例");
}
// Java 16+ 模式匹配
if (p instanceof Person person) {
System.out.println(person.getName());
}
clone() 方法
// 要使用 clone(),类必须实现 Cloneable 接口
public class Person implements Cloneable {
private String name;
private int age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// 使用
Person p1 = new Person("张三", 20);
Person p2 = (Person) p1.clone();
System.out.println(p1 == p2); // false(不同对象)
System.out.println(p1.equals(p2)); // true(内容相同)
方法重写 vs 方法隐藏
方法重写(Override)
实例方法可以被重写,运行时根据实际对象类型调用:
class Parent {
public void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
@Override
public void show() {
System.out.println("Child show");
}
}
Parent p = new Child();
p.show(); // "Child show"(多态)
方法隐藏(Hide)
静态方法不能被重写,只能被隐藏:
class Parent {
public static void show() {
System.out.println("Parent static show");
}
}
class Child extends Parent {
// 这是方法隐藏,不是重写!
public static void show() {
System.out.println("Child static show");
}
}
Parent p = new Child();
p.show(); // "Parent static show"(根据引用类型)
Child c = new Child();
c.show(); // "Child static show"
字段隐藏
字段也不能被重写,只能被隐藏:
class Parent {
public String name = "Parent";
}
class Child extends Parent {
public String name = "Child"; // 字段隐藏
}
Parent p = new Child();
System.out.println(p.name); // "Parent"(字段没有多态)
Child c = new Child();
System.out.println(c.name); // "Child"
重写 vs 隐藏对比
| 特性 | 重写(Override) | 隐藏(Hide) |
|---|---|---|
| 适用对象 | 实例方法 | 静态方法、字段 |
| 多态 | 有 | 无 |
| 运行时行为 | 根据实际对象类型 | 根据引用类型 |
| @Override 注解 | 可以使用 | 不能使用 |
对象类型判断
instanceof 运算符
// 基本用法
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// Java 16+ 模式匹配
if (obj instanceof String s) {
System.out.println(s.length());
}
// 类型判断链
if (obj instanceof String s) {
System.out.println("字符串: " + s);
} else if (obj instanceof Integer i) {
System.out.println("整数: " + i);
} else if (obj instanceof Double d) {
System.out.println("浮点数: " + d);
} else {
System.out.println("未知类型");
}
getClass() vs instanceof
class Animal {}
class Dog extends Animal {}
Animal animal = new Dog();
// instanceof:判断是否是某个类或其子类的实例
System.out.println(animal instanceof Animal); // true
System.out.println(animal instanceof Dog); // true
// getClass():精确判断类型
System.out.println(animal.getClass() == Animal.class); // false
System.out.println(animal.getClass() == Dog.class); // true
继承的注意事项
1. 构造方法不能被继承
class Parent {
public Parent(String name) {
// ...
}
}
class Child extends Parent {
public Child() {
super("default"); // 必须显式调用父类构造方法
}
// 不能这样写:
// Child(String name) = Parent(name); // 错误!
}
2. 私有成员不能直接访问
class Parent {
private String secret = "密码";
}
class Child extends Parent {
public void show() {
// System.out.println(secret); // 错误!无法访问私有成员
// 只能通过父类提供的公共方法访问
// System.out.println(getSecret()); // 如果父类有 getter
}
}
3. 访问权限规则
重写方法的访问权限不能比父类更严格:
class Parent {
public void method() {}
protected void doSomething() {}
}
class Child extends Parent {
@Override
public void method() {} // OK:public -> public
// @Override
// private void doSomething() {} // 错误!不能更严格
@Override
public void doSomething() {} // OK:protected -> public(更宽松)
}
4. 异常抛出规则
重写方法不能抛出更宽泛的受检异常:
class Parent {
public void method() throws IOException {}
}
class Child extends Parent {
@Override
public void method() throws FileNotFoundException {} // OK:子类异常
// @Override
// public void method() throws Exception {} // 错误:更宽泛的异常
@Override
public void method() {} // OK:不抛出异常
}
小结
本章我们学习了:
- 继承的基本概念
- super 关键字的使用
- 方法重写
- 多态
- 向上转型和向下转型
- final 关键字
- 抽象类和抽象方法
- 接口和多实现
- 接口的默认方法、静态方法和私有方法
- Object 类详解(toString、equals、hashCode、getClass、clone)
- 方法重写 vs 方法隐藏
- 字段隐藏
- 对象类型判断(instanceof、getClass)
- 继承的注意事项
练习
- 创建一个图形类层次结构(父类 Shape,子类 Circle、Rectangle)
- 创建一个动物类层次结构,实现多态
- 创建一个实现了多个接口的类
- 使用抽象类实现一个支付系统
- 重写 Person 类的 toString、equals 和 hashCode 方法
- 演示方法隐藏和方法重写的区别
- 使用 instanceof 模式匹配处理不同类型的对象