LESSON 02 · OOP 三大特性

Java 面向对象
三大特性 + 抽象 vs 接口

这一节我们彻底搞懂 Java 的"对象思维"——为什么要有"类"和"对象"?封装、继承、多态到底在解决什么问题?

学 OOP 的关键:不要死记"三个特性是什么",而要理解为什么需要它们。每讲一个特性,我会先告诉你"它解决了什么痛点"。
1

为什么需要"面向对象"?

💡 动机:把数据 + 操作数据的方法绑在一起

在写 Java 程序之前,先理解 OOP 解决了什么问题。

🏫 现实例子:管理一个学校的学生

❌ 不用 OOP(散装数据)

每个学生是一堆散装变量:
name1, age1, score1
name2, age2, score2

→ 数据乱、容易错、扩展难
✅ 用 OOP(学生 = 一个对象)

把"学生"定义成一个
class Student { ... }

每个具体学生是这个类的对象
Student s = new Student(...)
01

封装

Encapsulation

把数据藏起来,只暴露必要的方法。外部不能乱改。

🎁 像一个上锁的盒子
02

继承

Inheritance

子类复用父类代码,不用从零写。子类还能扩展新能力。

👨‍👩‍👧 孩子继承父母的特征
03

多态

Polymorphism

同一句代码,根据对象实际类型,表现出不同行为。

🔌 USB 接口,插啥是啥
2

类和对象:先搞懂这个

💡 类 = 模板,对象 = 模板做出来的东西

在讲三大特性之前,必须先理解 Java 里"类"和"对象"的关系。

// 类(Class)= 模板/图纸,定义一类事物长啥样、能干啥
public class Student {
    String name;
    int age;

    public void introduce() {
        System.out.println("我叫 " + name + ",今年 " + age + " 岁");
    }
}

// 对象(Object)= 根据类创建的具体实例
public class Main {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.name = "张三";
        s1.age = 20;
        s1.introduce();

        Student s2 = new Student();
        s2.name = "李四";
        s2.age = 22;
        s2.introduce();
    }
}
记住这个比喻:类是手机设计图,对象是流水线上的真手机。一张图纸可以做 N 部手机。
3

封装:把数据藏起来

💡 动机:防止外部乱改数据,统一管理操作

如果不封装,别人可以随便改:s1.age = -100,明显不合理。

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }

    public void setAge(int age) {
        if (age < 0 || age > 150) {
            System.out.println("年龄不合法!");
            return;
        }
        this.age = age;
    }
}
修饰符本类同包子类其他类
private
default
protected
public
4

继承:子类复用父类

💡 动机:避免重复代码,把公共部分抽出来

假如有 DogCatBird,它们都有 name 字段和 eat() 方法。重复写太蠢,抽出 Animal 父类。

public class Animal {
    String name;

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

    public void eat() {
        System.out.println(name + " 在吃东西");
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void eat() {
        super.eat();
        System.out.println(name + " 吃得很快");
    }

    public void bark() {
        System.out.println("汪汪!");
    }
}

继承层级:子类继承父类所有 public/protected 成员

Animal
父类
Dog
extends Animal
Cat
extends Animal
关键规则:Java 里一个类只能继承一个父类(单继承)。想要"多继承"效果?用接口
5

多态:同一句代码,不同表现

💡 动机:写出能处理"任何子类"的通用代码

假设你有一个方法要喂一群动物,动物有狗有猫各叫各的。多态让你写一个方法喂所有动物

// 多态写法:父类引用 a 可以指向任何子类对象
Animal a = new Dog("旺财");
a.eat();   // 执行的是 Dog 的 eat(),不是 Animal 的!

// 这就是多态的威力:写一个方法处理所有 Animal
public static void feed(Animal a) {
    System.out.print("主人喂食 → ");
    a.eat();   // 自动根据 a 实际类型调用对应的 eat
}

feed(new Dog("旺财"));    // → Dog 的 eat
feed(new Cat("小花"));    // → Cat 的 eat

点下面的按钮,看实际执行结果:

// 选上面任意一个,看实际执行
Animal a = new Dog("旺财");
a.eat(); // 多态:实际调用 Dog.eat()
// 输出 ↓
→ 点按钮看输出
6

抽象类 vs 接口 ⭐ 面试必问

💡 动机:定义"规范"——"子类必须实现这些方法"

有时候父类不知道具体怎么实现某个方法(比如 Animal 的 sound(),不同动物叫声不一样),就让父类只声明不实现

// 抽象类:用 abstract 关键字修饰
public abstract class Animal {
    protected String name;

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

    public void sleep() {
        System.out.println(name + " 睡觉中");
    }

    public abstract void sound();  // 抽象方法:没方法体
}

class Cat extends Animal {
    public Cat(String name) { super(name); }

    @Override
    public void sound() {
        System.out.println("喵~");
    }
}
// 接口:用 interface 关键字
public interface Swimmable {
    void swim();                  // 默认 public abstract

    default void dive() {         // Java 8+ 可以有默认方法
        System.out.println("潜水!");
    }
}

// 一个类可以实现多个接口
class Duck implements Swimmable, Flyable {
    @Override
    public void swim() {
        System.out.println("鸭子在游泳");
    }
}
对比项抽象类接口
关键字abstract classinterface
继承方式extends,单继承implements,可多实现
字段可以有普通字段只能 public static final
方法抽象 + 具体方法都可以Java 8 前只能抽象;之后可有 default/static 方法
构造器✅ 可以有❌ 不能有
使用场景子类和父类本质相同给类添加能力
小白记法:
• 抽象类 = "我们都是一家人(都是动物)" → 用 extends
• 接口 = "我会新技能(会飞)" → 用 implements

你的作业(分步走)

  1. 写一个 Dogname 字段(private),构造器,getNamebark() 方法打印"汪汪!"
  2. 加继承:写一个 Animal 父类(有 nameeat()),让 Dog 继承它。在 Dog 里 @Override eat()
  3. 体验多态:写一个方法 feed(Animal a),调用 a.eat()
  4. 玩接口:写一个 Swimmable 接口(有 swim() 方法),让 Duck 类实现它
  5. 把代码贴给我,我帮你 review