Skip to content

面向对象

类与对象

什么是面向对象?

面向对象(OOP) 是一种编程思想,将现实世界抽象为对象,通过对象之间的交互来解决问题。

┌─────────────────────────────────────────────────────────────┐
│                      面向对象 vs 面向过程                      │
├─────────────────────────────────────────────────────────────┤
│  面向过程                      │  面向对象                    │
│  ─────────────                │  ─────────────              │
│  • 以函数为单位               │  • 以对象为单位              │
│  • 数据和操作分离             │  • 数据和操作封装在一起        │
│  • 适合简单逻辑               │  • 适合复杂业务逻辑            │
│  • 示例:C语言                │  • 示例:Java、C++            │
└─────────────────────────────────────────────────────────────┘

面向对象的三大特征

  • 封装:保护内部数据,暴露必要接口
  • 继承:复用已有代码,实现类型层次
  • 多态:同一接口,不同实现

类的定义

类的基本结构

java
public class Person {
    // 成员变量(属性)
    private String name;
    private int age;

    // 构造方法 - 用于创建对象
    public Person() {
    }

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

    // 成员方法(行为)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄不合法");
        }
        this.age = age;
    }

    // 重写 toString 方法 - 调试友好
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

创建对象

java
// 使用无参构造
Person p1 = new Person();
p1.setName("张三");
p1.setAge(25);

// 使用有参构造
Person p2 = new Person("李四", 30);

封装

为什么要封装?

┌─────────────────────────────────────────┐
│              封装的作用                  │
├─────────────────────────────────────────┤
│  1. 隐藏实现细节    →  保护数据安全       │
│  2. 提供公共访问    →  统一接口规范       │
│  3. 便于修改维护    →  代码更健壮         │
│  4. 提高复用性      →  模块独立可重用     │
└─────────────────────────────────────────┘

封装的实现

原则:将属性私有化(private),提供公共 getter/setter

java
public class User {
    // ❌ 错误:属性直接暴露
    public String username;
    public String password;

    // ✅ 正确:封装属性
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        // 密码加密存储
        this.password = encrypt(password);
    }

    public boolean verifyPassword(String input) {
        return this.password.equals(encrypt(input));
    }
}

💡 面试点:IDE 自动生成的 getter/setter 和手写有什么区别?

java
// IDE 生成的简单 getter/setter
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

// 实际项目中可能需要增强版(加入业务逻辑)
public void setName(String name) {
    if (name == null || name.trim().isEmpty()) {
        throw new IllegalArgumentException("姓名不能为空");
    }
    this.name = name.trim();
}

继承

继承的基本概念

                    ┌─────────────────┐
                    │    Animal        │
                    │  - name          │
                    │  - age           │
                    │  + eat()         │
                    │  + sleep()       │
                    └────────┬────────┘

            ┌────────────────┴────────────────┐
            │                                 │
┌───────────▼───────────┐     ┌──────────────▼──────────────┐
│        Dog            │     │          Cat                │
│   - breed             │     │   - color                  │
│   + bark()            │     │   + meow()                 │
│   + catchBall()       │     │   + scratch()             │
└───────────────────────┘     └─────────────────────────────┘

Dog 和 Cat 继承了 Animal,复用了 name、age、eat()、sleep()

继承的实现

java
// 父类
public class Animal {
    protected String name;
    protected int age;

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

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

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

// 子类
public class Dog extends Animal {
    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);  // 调用父类构造
        this.breed = breed;
    }

    public void bark() {
        System.out.println(name + "汪汪叫");
    }

    // 重写父类方法
    @Override
    public void eat() {
        System.out.println(name + "正在吃狗粮");
    }
}

super 关键字

java
public class Dog extends Animal {
    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);           // 调用父类构造方法
        this.breed = breed;
    }

    public void show() {
        super.eat();                // 调用父类的 eat 方法
        System.out.println("品种:" + breed);
    }

    @Override
    public void eat() {
        System.out.println("Dog 重写的 eat");
        super.eat();                // 调用父类的 eat 方法
    }
}

💡 面试点:this 和 super 的区别?

区别thissuper
指向当前对象父类对象
用途调用本类成员调用父类成员
构造this() 调用本类其他构造super() 调用父类构造
位置必须在构造器首行必须在构造器首行

多态

多态的三要素

┌─────────────────────────────────────────────────────────────┐
│                       多态的三要素                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 继承关系          →  父子类之间                          │
│                                                             │
│  2. 方法重写          →  子类重写父类方法                     │
│                                                             │
│  3. 父类引用指向子类对象  →  Animal dog = new Dog();         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

向上转型(自动)

java
// 父类引用指向子类对象
Animal animal = new Dog("旺财", 3, "金毛");

animal.eat();    // 调用的是 Dog 重写后的 eat
animal.sleep();  // 调用的是 Animal 的 sleep

// ❌ 编译错误:animal 是 Animal 类型,不能调用子类独有方法
animal.bark();   // 编译错误!

向下转型(强制)

java
Animal animal = new Dog("旺财", 3, "金毛");

// ✅ 向下转型
Dog dog = (Dog) animal;
dog.bark();  // 现在可以调用了

// ⚠️ 危险:类型转换异常
Animal cat = new Cat("咪咪", 2, "白色");
// Dog dog2 = (Dog) cat;  // ClassCastException!

instanceof 关键字

💡 面试高频:向下转型前一定要用 instanceof 判断!

java
public void makeSound(Animal animal) {
    if (animal instanceof Dog) {
        Dog dog = (Dog) animal;
        dog.bark();
    } else if (animal instanceof Cat) {
        Cat cat = (Cat) animal;
        cat.meow();
    }
}

// Java 16+ 新写法 (Pattern Matching)
public void makeSound(Animal animal) {
    if (animal instanceof Dog dog) {
        dog.bark();
    } else if (animal instanceof Cat cat) {
        cat.meow();
    }
}

抽象类与接口

抽象类

抽象类:用 abstract 修饰,包含抽象方法的类

java
// 抽象类
public abstract class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    // 抽象方法 - 没有方法体,子类必须重写
    public abstract double area();
    public abstract double perimeter();

    // 具体方法 - 子类可选重写
    public void print() {
        System.out.println("颜色:" + color);
    }
}

// 子类
public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

抽象类特点

  • 不能实例化(不能 new Shape()
  • 可以包含抽象方法和具体方法
  • 单继承(一个类只能继承一个抽象类)
  • 构造方法可以被子类调用

接口

接口:一种契约,定义行为规范

java
// 接口 - Java 8+ 支持 default 和 static 方法
public interface Drawable {
    // 常量(默认 public static final)
    int MAX_SIZE = 1000;

    // 抽象方法
    void draw();

    // Java 8+ default 方法 - 提供默认实现
    default void print() {
        System.out.println("打印图形");
    }

    // Java 8+ static 方法
    static Drawable create() {
        return new Circle("红色", 5);
    }
}

// 实现类
public class Rectangle implements Drawable {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

抽象类 vs 接口

💡 面试高频对比

特性抽象类接口
继承单继承多实现
构造方法可以有不能有
属性任意类型只能是常量
方法任意方法抽象/ default/ static
实现关键字extendsimplements
设计理念"是什么"(is-a)"能做什么"(can-do)
java
// 抽象类 - 表达"是什么"
public abstract class Flyable {
    protected double speed;

    public abstract void fly();
}

public class Bird extends Flyable {
    @Override
    public void fly() {
        System.out.println("鸟儿飞翔");
    }
}

// 接口 - 表达"能做什么"
public interface Swimable {
    void swim();
}

public interface Divable {
    void dive();
}

public class Duck extends Flyable implements Swimable, Divable {
    @Override
    public void fly() { }

    @Override
    public void swim() { }

    @Override
    public void dive() { }
}

内部类

成员内部类

java
public class Outer {
    private String outerVar = "外部类变量";

    public class Inner {
        private String innerVar = "内部类变量";

        public void show() {
            System.out.println(outerVar);  // 可以访问外部类私有成员
            System.out.println(innerVar);
        }
    }
}

// 使用
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();

静态内部类

java
public class Outer {
    private static String staticVar = "静态变量";

    public static class StaticInner {
        public void show() {
            // 只能访问外部类静态成员
            System.out.println(staticVar);
        }
    }
}

// 使用 - 不需要创建外部类对象
Outer.StaticInner inner = new Outer.StaticInner();

局部内部类

java
public class Outer {
    public void method() {
        // 定义在方法内部的类
        class LocalInner {
            private String name = "局部内部类";

            public void show() {
                System.out.println(name);
            }
        }

        LocalInner inner = new LocalInner();
        inner.show();
    }
}

匿名内部类

💡 面试高频:匿名内部类的应用场景

java
// 场景1:简化实现接口
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("线程任务执行中");
    }
};
new Thread(task).start();

// 场景2:简化继承抽象类
abstract class Animal {
    abstract void cry();
}

Animal dog = new Animal() {
    @Override
    void cry() {
        System.out.println("汪汪汪");
    }
};

// 场景3:事件监听 (Android 开发常见)
// button.setOnClickListener(new View.OnClickListener() {
//     @Override
//     public void onClick(View v) {
//         // 处理点击事件
//     }
// });

关键字总结

this 关键字

java
public class Person {
    private String name;

    public Person(String name) {
        // this 区分成员变量和局部变量
        this.name = name;
    }

    public Person getThis() {
        // this 返回当前对象引用
        return this;
    }
}

static 关键字

详见 基础语法 中的 static 详解

final 关键字

详见 基础语法 中的 final 详解


总结

面向对象核心知识点

概念面试频率实战重要性
封装⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
继承⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
多态⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
抽象类 vs 接口⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
内部类⭐⭐⭐⭐⭐⭐⭐⭐

⚠️ 易错点提醒

  1. 构造方法不能被继承(子类通过 super 调用)
  2. private 成员不能被继承
  3. 向上转型安全,向下转型需要 instanceof
  4. 抽象类有构造方法但不能实例化
  5. 接口属性默认 public static final,方法默认 public abstract