Java 面向对象进阶:继承、多态、抽象与接口,OOP 的核心拼图

学习日期:2026-06-04

难度:⭐⭐⭐⭐ 进阶

一、继承(Inheritance):站在巨人的肩膀上

1.1 什么是继承?

子类继承父类,直接获得父类的属性和方法,还能扩展自己的新能力。

// 父类

class Animal {

String name;

int age;

void eat() {

System.out.println(name + "在吃东西");

}

}

// 子类:用 extends 继承

class Dog extends Animal {

void bark() {

System.out.println(name + ":汪汪汪!");

}

}

// 使用

Dog dog = new Dog();

dog.name = "旺财";

dog.eat(); // 旺财在吃东西(继承来的)

dog.bark(); // 旺财:汪汪汪!(自己的)

1.2 继承的本质

plaintext

Animal

/ \

Dog Cat

| |

GuideDog Persian

  • 子类拥有父类所有非 private 的成员
  • 子类可以新增自己的成员
  • 子类可以重写父类的方法
  • Java 只支持单继承(一个类只能有一个直接父类)

1.3 方法重写(Override)

子类对父类的方法"重新实现":

java

class Animal {

void speak() {

System.out.println("动物发出声音");

}

}

class Dog extends Animal {

@Override // 注解:标记这是重写,拼写错误时编译器会提醒

void speak() {

System.out.println("汪汪汪!");

}

}

class Cat extends Animal {

@Override

void speak() {

System.out.println("喵喵喵!");

}

}

重写规则:

  • ✅ 方法名、参数列表必须完全一致
  • ✅ 返回值类型相同或是父类返回值的子类
  • ✅ 访问权限不能比父类更严格(父类 public → 子类不能改 protected)
  • ❌ 不能重写 private 方法(子类根本看不到)
  • ❌ 不能重写 static 方法(那叫"隐藏",不是重写)

⚠️ 重写 vs 重载,别搞混!

表格

重写 Override 重载 Overload
发生在 父子类之间 同一个类中
方法名 相同 相同
参数列表 相同 不同
返回值 相同或子类 无关
关注点 行为不同实现 同名不同参数

1.4 super 关键字

super 指向父类this 指向当前对象

java

class Animal {

String name;

Animal(String name) {

this.name = name;

}

}

class Dog extends Animal {

String breed;

Dog(String name, String breed) {

super(name); // 调用父类构造方法,必须放第一行!

this.breed = breed;

}

void info() {

System.out.println("名字:" + super.name); // 访问父类属性

System.out.println("品种:" + this.breed); // 访问自己的属性

}

}

构造方法的调用顺序:

class A {

A() { System.out.println("A的构造方法"); }

}

class B extends A {

B() { System.out.println("B的构造方法"); }

}

class C extends B {

C() { System.out.println("C的构造方法"); }

}

new C();

// 输出:A的构造方法 → B的构造方法 → C的构造方法

💡 创建子类对象时,一定先调用父类构造方法。如果不显式写 super(...),编译器自动加 super()(即调用父类无参构造)。

1.5 final 关键字

表格

用法 含义
final 变量 常量,赋值后不能改
final 方法 不能被重写
final 不能被继承

java

final class MathUtils { // 不能被继承

static final double PI = 3.14159; // 常量

final double calc() { // 不能被重写

return 0;

}

}

二、多态(Polymorphism):同一指令,不同表现

2.1 核心概念

编译看左边,运行看右边——这是理解多态的关键口诀。

Animal animal = new Dog(); // 父类引用指向子类对象

animal.speak(); // 编译时看 Animal 有没有 speak(),运行时执行 Dog 的 speak()

// 输出:汪汪汪!

多态的三个前提:

  1. 有继承关系
  2. 有方法重写
  3. 父类引用指向子类对象

2.2 多态的威力

不使用多态——每个类型单独处理:

void feed(Dog d) { d.eat(); }

void feed(Cat c) { c.eat(); }

void feed(Bird b) { b.eat(); }

// 每增加一种动物,就要加一个方法!

使用多态——一个方法搞定所有:

void feed(Animal a) {

a.eat(); // 传什么动物,就调谁的 eat()

}

feed(new Dog()); // Dog 的 eat()

feed(new Cat()); // Cat 的 eat()

feed(new Bird()); // Bird 的 eat()

2.3 类型转换

向上转型(自动,安全):

Animal a = new Dog(); // 子类 → 父类,自动

a.eat(); // ✅ 可以调

// a.bark(); // ❌ 编译报错!父类引用看不到子类特有方法

向下转型(需要强转,有风险):

Animal a = new Dog();

Dog d = (Dog) a; // 父类 → 子类,强转

d.bark(); // ✅ 现在可以调了

// 危险!类型不对会报 ClassCastException

Animal b = new Cat();

Dog x = (Dog) b; // ❌ 运行时报错!Cat 不能转成 Dog

安全做法:先判断再转

Animal a = new Dog();

if (a instanceof Dog) {

Dog d = (Dog) a;

d.bark();

}

// Java 16+ 更简洁的写法:

if (a instanceof Dog d) { // 模式匹配,一步到位

d.bark();

}

三、抽象类(Abstract Class):只定规矩,不干实事

3.1 为什么需要抽象类?

有些方法在父类中没法给出具体实现,比如 Animal.speak()——每种动物叫法不同,写什么都没意义。

abstract class Animal {

String name;

// 抽象方法:只有声明,没有实现,留给子类去重写

abstract void speak();

// 抽象类也可以有普通方法

void eat() {

System.out.println(name + "在吃东西");

}

}

3.2 抽象类的规则

  • 有抽象方法的类必须声明为抽象类
  • 抽象类不能实例化(不能 new Animal()
  • 子类必须重写所有抽象方法,否则子类也得是抽象类

class Dog extends Animal {

Dog(String name) {

this.name = name;

}

@Override

void speak() { // 必须重写,否则编译报错

System.out.println("汪汪汪!");

}

}

3.3 抽象类 vs 普通类

表格

抽象类 普通类
实例化 ❌ 不能 ✅ 可以
抽象方法 ✅ 可以有 ❌ 不能
普通方法 ✅ 可以有 ✅ 可以
构造方法 ✅ 可以有 ✅ 可以
作用 定义模板,强制子类实现 直接使用

四、接口(Interface):行为的契约

4.1 什么是接口?

接口是一组行为规范的集合,定义"能做什么",不管"怎么做"。

interface Swimable {

void swim(); // 默认 public abstract,不需要写修饰符

}

interface Flyable {

void fly();

}

4.2 实现接口

class Duck extends Animal implements Swimable, Flyable {

Duck(String name) {

this.name = name;

}

@Override

void speak() {

System.out.println("嘎嘎嘎!");

}

@Override

public void swim() {

System.out.println(name + "在游泳");

}

@Override

public void fly() {

System.out.println(name + "在飞");

}

}

💡 一个类只能继承一个父类,但可以实现多个接口——这就是 Java 实现"多继承"效果的方式。

4.3 接口的新特性(Java 8+)

默认方法——接口可以有方法体了:

interface Swimable {

void swim();

// 默认方法:有实现,子类可以选择重写或直接用

default void rest() {

System.out.println("上岸休息一会儿");

}

}

class Duck implements Swimable {

@Override

public void swim() {

System.out.println("鸭子游泳");

}

// rest() 不用重写也能用

}

new Duck().rest(); // 上岸休息一会儿

静态方法

interface Swimable {

static void checkWater() {

System.out.println("检查水质...");

}

}

Swimable.checkWater(); // 通过接口名调用

常量——接口中的变量默认是 public static final

interface Config {

int MAX_SIZE = 100; // 等同于 public static final int MAX_SIZE = 100

}

4.4 接口的继承

接口可以继承多个接口:

interface A {

void methodA();

}

interface B {

void methodB();

}

interface C extends A, B { // 接口多继承

void methodC();

}

class Impl implements C {

public void methodA() { }

public void methodB() { }

public void methodC() { }

}

4.5 抽象类 vs 接口

表格

抽象类 接口
关键字 abstract class interface
构造方法 ✅ 可以有 ❌ 没有
成员变量 任意 只能是常量(public static final)
方法 抽象 + 普通都行 默认抽象;Java 8+ 可有 default/static
多继承 单继承 可多实现
设计意图 is-a 关系(是什么) can-do 关系(能做什么)

🎯 选择原则:如果是一组有层级关系的类,用抽象类(Animal → Dog/Cat);如果是一种能力/行为,用接口(Swimable、Flyable)。

五、内部类

5.1 成员内部类

定义在类内部,可以访问外部类的所有成员:

class Outer {

private String msg = "外部类的私有数据";

class Inner {

void show() {

System.out.println(msg); // 能访问外部类私有成员!

}

}

}

// 创建内部类对象需要先有外部类对象

Outer outer = new Outer();

Outer.Inner inner = outer.new Inner();

inner.show(); // 外部类的私有数据

5.2 静态内部类

加 static 的内部类,不依赖外部类对象:

java

class Outer {

static String msg = "静态数据";

static class StaticInner {

void show() {

System.out.println(msg); // 只能访问外部类的静态成员

}

}

}

Outer.StaticInner inner = new Outer.StaticInner(); // 不需要外部类对象

inner.show();

5.3 匿名内部类

没有名字的内部类,一次性使用,常见于接口/抽象类的快速实现:

java

interface Greeting {

void sayHello();

}

// 传统方式:写一个类实现接口

// 匿名内部类:当场实现

Greeting g = new Greeting() {

@Override

public void sayHello() {

System.out.println("你好!");

}

};

g.sayHello(); // 你好!

💡 匿名内部类在事件监听、回调、线程创建等场景非常常见,后续学 GUI 和多线程时会大量用到。

5.4 局部内部类

定义在方法内部的类,作用域仅限于该方法:

class Outer {

void method() {

class Local {

void show() {

System.out.println("我是局部内部类");

}

}

Local l = new Local();

l.show();

}

}

六、Object 类:所有类的祖宗

Java 中每个类都直接或间接继承 Object,它提供几个重要方法:

6.1 toString()

默认打印类名+哈希值,建议重写:

class Student {

String name;

int age;

Student(String name, int age) {

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Student{name='" + name + "', age=" + age + "}";

}

}

System.out.println(new Student("张三", 20));

// 不重写:Student@15db9742

// 重写后:Student{name='张三', age=20}

6.2 equals()

默认比较引用(地址),建议重写为比较内容:

class Student {

String name;

int age;

@Override

public boolean equals(Object obj) {

if (this == obj) return true; // 同一个对象

if (obj == null) return false; // null 不等于任何

if (getClass() != obj.getClass()) return false; // 类型不同

Student other = (Student) obj;

return age == other.age && name.equals(other.name);

}

}

new Student("张三", 20).equals(new Student("张三", 20)); // true

⚠️ == 比较引用(地址),equals() 比较内容(但前提是你重写了它)。

6.3 hashCode()

重写 equals 时必须同时重写 hashCode,否则 HashSet/HashMap 等会出 bug:

rride

public int hashCode() {

return Objects.hash(name, age);

}

七、实战练习

练习1:多态版动物合唱团

abstract class Animal {

String name;

Animal(String name) { this.name = name; }

abstract void speak();

}

class Dog extends Animal {

Dog(String name) { super(name); }

void speak() { System.out.println(name + ":汪!"); }

}

class Cat extends Animal {

Cat(String name) { super(name); }

void speak() { System.out.println(name + ":喵!"); }

}

class Cow extends Animal {

Cow(String name) { super(name); }

void speak() { System.out.println(name + ":哞!"); }

}

// 合唱:多态的体现

class Chorus {

static void perform(Animal[] animals) {

for (Animal a : animals) {

a.speak(); // 运行时自动调用各自的实现

}

}

}

Animal[] band = { new Dog("旺财"), new Cat("咪咪"), new Cow("大黄") };

Chorus.perform(band);

// 旺财:汪!

// 咪咪:喵!

// 大黄:哞!

练习2:接口实现多重能力

interface Chargeable {

void charge();

}

interface Playable {

void play();

}

class Phone implements Chargeable, Playable {

public void charge() { System.out.println("手机充电中..."); }

public void play() { System.out.println("刷视频~"); }

}

class Car implements Chargeable {

public void charge() { System.out.println("电车充电中..."); }

}

Chargeable[] devices = { new Phone(), new Car() };

for (Chargeable d : devices) {

d.charge(); // 同一个接口,不同实现

}

八、总结速查表

表格

概念 核心要点
继承 extends;单继承;子类获得父类非 private 成员
方法重写 @Override;方法签名一致;运行时执行子类版本
super 调用父类构造(必须第一行)、父类成员
final 修饰变量→常量,修饰方法→不可重写,修饰类→不可继承
多态 父类引用指向子类对象;编译看左,运行看右
向上/向下转型 向上自动、向下强转;instanceof 先判断再转
抽象类 abstract;不能实例化;可含抽象方法+普通方法
接口 interface/implements;可多实现;Java 8+ 支持 default/static 方法
抽象类 vs 接口 is-a 用抽象类,can-do 用接口
内部类 成员内部类、静态内部类、匿名内部类、局部内部类
Object toString/equals/hashCode 建议重写

继承让你复用,多态让你灵活,抽象类定模板,接口定契约——这四块拼图合在一起,才是完整的 OOP。从流程控制走到这里,你已经拿下了 Java 面向对象的全景图。下一步:异常处理、集合框架,冲!💪

更多推荐