Skip to content

面向对象编程基础

面向对象与面向过程的区别

面向过程和面向对象是两种不同的编程思想,它们在解决问题时采用的方式截然不同。

面向过程编程

核心思想:把问题分解成一系列步骤,每个步骤用函数实现,按顺序调用这些函数来解决问题。

关注点是"怎么做",强调执行的流程和步骤。

java
// 面向过程思维:制作一杯咖啡
public class ProceduralCoffee {
    public static void main(String[] args) {
        // 步骤1: 烧水
        boilWater();
        // 步骤2: 研磨咖啡豆
        grindCoffeeBeans();
        // 步骤3: 冲泡咖啡
        brewCoffee();
        // 步骤4: 添加糖和奶
        addSugarAndMilk();
        
        System.out.println("咖啡制作完成");
    }
    
    static void boilWater() {
        System.out.println("烧开水到100度");
    }
    
    static void grindCoffeeBeans() {
        System.out.println("研磨20克咖啡豆");
    }
    
    static void brewCoffee() {
        System.out.println("用热水冲泡咖啡粉");
    }
    
    static void addSugarAndMilk() {
        System.out.println("加入糖和奶");
    }
}

面向对象编程

核心思想:将问题分解成一个个对象,通过对象之间的交互和协作来解决问题。

关注点是"谁来做",强调对象的职责和协作。

java
// 面向对象思维:制作一杯咖啡
public class OOPCoffee {
    public static void main(String[] args) {
        // 创建对象
        WaterHeater heater = new WaterHeater();
        CoffeeGrinder grinder = new CoffeeGrinder();
        CoffeeMaker maker = new CoffeeMaker();
        
        // 对象协作完成任务
        Water water = heater.heat();
        CoffeePowder powder = grinder.grind(20);
        Coffee coffee = maker.brew(water, powder);
        coffee.addSugar(2);
        coffee.addMilk(50);
        
        System.out.println("咖啡制作完成: " + coffee);
    }
}

// 热水器对象
class WaterHeater {
    public Water heat() {
        System.out.println("热水器:加热水到100度");
        return new Water(100);
    }
}

// 咖啡研磨机对象  
class CoffeeGrinder {
    public CoffeePowder grind(int grams) {
        System.out.println("研磨机:研磨" + grams + "克咖啡豆");
        return new CoffeePowder(grams);
    }
}

// 咖啡机对象
class CoffeeMaker {
    public Coffee brew(Water water, CoffeePowder powder) {
        System.out.println("咖啡机:冲泡咖啡");
        return new Coffee();
    }
}

// 水对象
class Water {
    private int temperature;
    public Water(int temperature) {
        this.temperature = temperature;
    }
}

// 咖啡粉对象
class CoffeePowder {
    private int grams;
    public CoffeePowder(int grams) {
        this.grams = grams;
    }
}

// 咖啡对象
class Coffee {
    private int sugar = 0;
    private int milk = 0;
    
    public void addSugar(int spoons) {
        this.sugar = spoons;
        System.out.println("添加" + spoons + "勺糖");
    }
    
    public void addMilk(int ml) {
        this.milk = ml;
        System.out.println("添加" + ml + "ml奶");
    }
}

两种思想的对比

mermaid
graph TB
    subgraph 面向过程
        A1[问题] --> A2[分解为步骤]
        A2 --> A3[步骤1:函数1]
        A2 --> A4[步骤2:函数2]
        A2 --> A5[步骤3:函数3]
        A3 --> A6[按顺序执行]
        A4 --> A6
        A5 --> A6
        A6 --> A7[得到结果]
    end
    
    subgraph 面向对象
        B1[问题] --> B2[抽象成对象]
        B2 --> B3[对象1:属性+方法]
        B2 --> B4[对象2:属性+方法]
        B2 --> B5[对象3:属性+方法]
        B3 --> B6[对象协作]
        B4 --> B6
        B5 --> B6
        B6 --> B7[得到结果]
    end
    
    classDef procedureStyle fill:#4A90E2,stroke:none,color:#fff
    classDef oopStyle fill:#50C878,stroke:none,color:#fff
    
    class A1,A2,A3,A4,A5,A6,A7 procedureStyle
    class B1,B2,B3,B4,B5,B6,B7 oopStyle

主要区别:

对比维度面向过程面向对象
关注点执行步骤和流程对象的职责和协作
代码组织以函数为单位以类/对象为单位
数据和行为分离(数据结构+函数)封装在一起
复用方式函数复用继承、组合
扩展性修改困难易于扩展
适用场景简单算法、工具函数复杂系统、企业应用

面向对象的三大特征

面向对象有三个核心特征:封装、继承、多态。

1. 封装(Encapsulation)

定义:将对象的属性和行为封装在一起,对外隐藏内部实现细节,只暴露必要的接口。

目的:降低耦合度,提高安全性和可维护性。

java
// 封装示例:银行账户
public class BankAccount {
    // 私有属性,外部无法直接访问
    private String accountNumber;
    private String ownerName;
    private double balance;
    private String password;
    
    public BankAccount(String accountNumber, String ownerName, String password) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.password = password;
        this.balance = 0.0;
    }
    
    // 提供公共方法来操作私有数据
    public boolean deposit(double amount) {
        if (amount <= 0) {
            System.out.println("存款金额必须大于0");
            return false;
        }
        balance += amount;
        System.out.println("成功存入:" + amount + ",当前余额:" + balance);
        return true;
    }
    
    public boolean withdraw(double amount, String pwd) {
        // 密码验证
        if (!password.equals(pwd)) {
            System.out.println("密码错误");
            return false;
        }
        
        // 余额验证
        if (amount > balance) {
            System.out.println("余额不足");
            return false;
        }
        
        balance -= amount;
        System.out.println("成功取出:" + amount + ",当前余额:" + balance);
        return true;
    }
    
    // 只读访问
    public double getBalance(String pwd) {
        if (!password.equals(pwd)) {
            throw new SecurityException("密码错误");
        }
        return balance;
    }
    
    public String getAccountNumber() {
        return accountNumber;
    }
}

// 使用示例
class EncapsulationDemo {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("6222021234567890", "张三", "123456");
        
        // ❌ 无法直接访问私有字段
        // account.balance = 1000000;  // 编译错误
        
        // ✅ 必须通过公共方法操作
        account.deposit(5000);
        account.withdraw(2000, "123456");
        
        double balance = account.getBalance("123456");
        System.out.println("账户余额:" + balance);
    }
}

封装的优势:

  • 数据安全:外部无法直接修改内部状态
  • 灵活修改:可以修改内部实现而不影响外部调用
  • 易于维护:逻辑集中,便于理解和修改

2. 继承(Inheritance)

定义:子类可以继承父类的属性和方法,实现代码复用。

目的:避免重复代码,建立类之间的层次关系。

java
// 父类:动物
public class Animal {
    protected String name;
    protected int age;
    protected String color;
    
    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 void makeSound() {
        System.out.println(name + "发出声音");
    }
}

// 子类:狗
class Dog extends Animal {
    private String breed;  // 品种
    
    public Dog(String name, int age, String breed) {
        super(name, age);  // 调用父类构造器
        this.breed = breed;
    }
    
    // 重写父类方法
    @Override
    public void makeSound() {
        System.out.println(name + "汪汪汪!");
    }
    
    // 子类特有方法
    public void wagTail() {
        System.out.println(name + "摇尾巴");
    }
    
    public void fetch() {
        System.out.println(name + "去捡球");
    }
}

// 子类:猫
class Cat extends Animal {
    private boolean isIndoor;  // 是否室内猫
    
    public Cat(String name, int age, boolean isIndoor) {
        super(name, age);
        this.isIndoor = isIndoor;
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + "喵喵喵~");
    }
    
    public void climbTree() {
        if (!isIndoor) {
            System.out.println(name + "爬树");
        } else {
            System.out.println(name + "是室内猫,不会爬树");
        }
    }
}

// 使用示例
class InheritanceDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("旺财", 3, "金毛");
        dog.eat();        // 继承自Animal
        dog.makeSound();  // 重写的方法
        dog.wagTail();    // Dog特有方法
        
        System.out.println();
        
        Cat cat = new Cat("小白", 2, true);
        cat.sleep();      // 继承自Animal  
        cat.makeSound();  // 重写的方法
        cat.climbTree();  // Cat特有方法
    }
}

继承的关系图:

mermaid
graph TB
    Animal[Animal<br/>动物基类<br/>name, age<br/>eat, sleep, makeSound]
    
    Dog[Dog<br/>狗<br/>breed<br/>makeSound重写<br/>wagTail, fetch]
    
    Cat[Cat<br/>猫<br/>isIndoor<br/>makeSound重写<br/>climbTree]
    
    Bird[Bird<br/>鸟<br/>canFly<br/>makeSound重写<br/>fly]
    
    Animal --> Dog
    Animal --> Cat
    Animal --> Bird
    
    classDef parentStyle fill:#4A90E2,stroke:none,color:#fff
    classDef childStyle fill:#50C878,stroke:none,color:#fff
    
    class Animal parentStyle
    class Dog,Cat,Bird childStyle

继承的特点:

  • Java只支持单继承,一个类只能有一个直接父类
  • 子类继承父类的所有非private成员
  • 子类可以重写(Override)父类的方法
  • 使用super关键字调用父类方法和构造器

3. 多态(Polymorphism)

定义:同一个操作作用于不同的对象,可以产生不同的执行结果。

实现条件:

  1. 有继承或接口实现关系
  2. 子类重写父类方法
  3. 父类引用指向子类对象
java
// 多态示例:支付系统
public class PolymorphismDemo {
    public static void main(String[] args) {
        // 父类引用指向不同的子类对象
        PaymentMethod payment1 = new AlipayPayment();
        PaymentMethod payment2 = new WeChatPayment();
        PaymentMethod payment3 = new CreditCardPayment();
        
        // 同样的调用,不同的实现
        processPayment(payment1, 100.0);
        processPayment(payment2, 200.0);
        processPayment(payment3, 300.0);
    }
    
    // 多态的应用:统一处理不同的支付方式
    public static void processPayment(PaymentMethod payment, double amount) {
        System.out.println("\n开始支付...");
        if (payment.validate()) {
            payment.pay(amount);
            System.out.println("支付完成");
        } else {
            System.out.println("支付验证失败");
        }
    }
}

// 父类:支付方式
abstract class PaymentMethod {
    protected String accountId;
    
    public abstract boolean validate();
    public abstract void pay(double amount);
}

// 子类1:支付宝
class AlipayPayment extends PaymentMethod {
    @Override
    public boolean validate() {
        System.out.println("验证支付宝账户");
        return true;
    }
    
    @Override
    public void pay(double amount) {
        System.out.println("使用支付宝支付:¥" + amount);
        System.out.println("扫码完成,从余额宝扣款");
    }
}

// 子类2:微信支付
class WeChatPayment extends PaymentMethod {
    @Override
    public boolean validate() {
        System.out.println("验证微信账户");
        return true;
    }
    
    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付:¥" + amount);
        System.out.println("人脸识别完成,从零钱扣款");
    }
}

// 子类3:信用卡
class CreditCardPayment extends PaymentMethod {
    @Override
    public boolean validate() {
        System.out.println("验证信用卡信息");
        return true;
    }
    
    @Override
    public void pay(double amount) {
        System.out.println("使用信用卡支付:¥" + amount);
        System.out.println("输入密码,银行扣款成功");
    }
}

多态的优势:

mermaid
graph LR
    A[多态的优势] --> B[可扩展性]
    A --> C[可维护性]
    A --> D[灵活性]
    
    B --> B1[新增支付方式<br/>无需修改现有代码]
    C --> C1[统一接口<br/>降低理解成本]
    D --> D1[运行时决定<br/>动态绑定]
    
    classDef benefitStyle fill:#50C878,stroke:none,color:#fff
    classDef detailStyle fill:#4A90E2,stroke:none,color:#fff
    
    class A benefitStyle
    class B,C,D benefitStyle
    class B1,C1,D1 detailStyle

方法重载vs方法重写:

特性重载(Overload)重写(Override)
发生位置同一个类中父子类之间
方法名相同相同
参数列表必须不同必须相同
返回类型可以不同必须相同或是子类型
访问修饰符可以不同不能更严格
绑定时机编译期绑定(静态)运行期绑定(动态)
是否多态争议(一般认为不是)
java
public class OverloadVsOverride {
    
    // 方法重载示例
    public void print(int num) {
        System.out.println("整数:" + num);
    }
    
    public void print(String str) {
        System.out.println("字符串:" + str);
    }
    
    public void print(int num, String str) {
        System.out.println("整数:" + num + ",字符串:" + str);
    }
}

class Parent {
    public void show() {
        System.out.println("Parent show");
    }
}

class Child extends Parent {
    // 方法重写示例
    @Override
    public void show() {
        System.out.println("Child show");
    }
}

面向对象的五大设计原则(SOLID)

这五个原则是面向对象设计的基石,帮助我们编写可维护、可扩展的代码。

1. 单一职责原则(Single Responsibility Principle)

核心:一个类应该只有一个引起它变化的原因,只负责一项职责。

java
// ❌ 违反单一职责:一个类做太多事
class BadUserService {
    public void register(User user) {
        // 验证用户数据
        if (user.getName() == null || user.getEmail() == null) {
            throw new IllegalArgumentException("用户信息不完整");
        }
        
        // 保存到数据库
        saveToDatabase(user);
        
        // 发送欢迎邮件  
        sendEmail(user.getEmail(), "欢迎注册");
        
        // 记录日志
        writeLog("用户注册:" + user.getName());
    }
    
    private void saveToDatabase(User user) { /* ... */ }
    private void sendEmail(String to, String content) { /* ... */ }
    private void writeLog(String message) { /* ... */ }
}

// ✅ 符合单一职责:职责分离
class UserValidator {
    public void validate(User user) {
        if (user.getName() == null || user.getEmail() == null) {
            throw new IllegalArgumentException("用户信息不完整");
        }
    }
}

class UserRepository {
    public void save(User user) {
        // 保存到数据库
        System.out.println("保存用户到数据库:" + user.getName());
    }
}

class EmailService {
    public void sendWelcomeEmail(String email) {
        // 发送欢迎邮件
        System.out.println("发送欢迎邮件到:" + email);
    }
}

class Logger {
    public void log(String message) {
        System.out.println("[LOG] " + message);
    }
}

class GoodUserService {
    private UserValidator validator = new UserValidator();
    private UserRepository repository = new UserRepository();
    private EmailService emailService = new EmailService();
    private Logger logger = new Logger();
    
    public void register(User user) {
        validator.validate(user);
        repository.save(user);
        emailService.sendWelcomeEmail(user.getEmail());
        logger.log("用户注册:" + user.getName());
    }
}

class User {
    private String name;
    private String email;
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    public String getName() { return name; }
    public String getEmail() { return email; }
}

2. 开闭原则(Open-Closed Principle)

核心:对扩展开放,对修改关闭。应该通过扩展来实现变化,而不是修改已有代码。

java
// ❌ 违反开闭原则:每次新增需要修改原有代码
class BadDiscountCalculator {
    public double calculate(String customerType, double amount) {
        if ("VIP".equals(customerType)) {
            return amount * 0.8;  // 8折
        } else if ("SVIP".equals(customerType)) {
            return amount * 0.7;  // 7折
        } else if ("NORMAL".equals(customerType)) {
            return amount;        // 原价
        }
        // 新增会员类型需要修改这里
        return amount;
    }
}

// ✅ 符合开闭原则:通过扩展添加新功能
interface DiscountStrategy {
    double calculateDiscount(double amount);
    String getDescription();
}

class VIPDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount * 0.8;
    }
    
    @Override
    public String getDescription() {
        return "VIP会员8折优惠";
    }
}

class SVIPDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount * 0.7;
    }
    
    @Override
    public String getDescription() {
        return "超级VIP会员7折优惠";
    }
}

class NormalDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount;
    }
    
    @Override
    public String getDescription() {
        return "普通会员无折扣";
    }
}

// 新增会员类型,只需添加新类,无需修改现有代码
class GoldDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount * 0.85;
    }
    
    @Override
    public String getDescription() {
        return "黄金会员85折优惠";
    }
}

class GoodDiscountCalculator {
    public double calculate(DiscountStrategy strategy, double amount) {
        return strategy.calculateDiscount(amount);
    }
}

3. 里氏替换原则(Liskov Substitution Principle)

核心:子类必须能够替换其父类,且不影响程序的正确性。

java
// ❌ 违反里氏替换原则
class Bird {
    public void fly() {
        System.out.println("鸟在飞翔");
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("企鹅不会飞!");
    }
}

// 这里会出问题
void makeBirdFly(Bird bird) {
    bird.fly();  // 如果传入Penguin会抛异常
}

// ✅ 符合里氏替换原则:重新设计继承关系
abstract class AbstractBird {
    public abstract void move();
}

class FlyingBird extends AbstractBird {
    @Override
    public void move() {
        fly();
    }
    
    public void fly() {
        System.out.println("在天空飞翔");
    }
}

class SwimmingBird extends AbstractBird {
    @Override
    public void move() {
        swim();
    }
    
    public void swim() {
        System.out.println("在水中游泳");
    }
}

class Sparrow extends FlyingBird {
    // 麻雀会飞
}

class GoodPenguin extends SwimmingBird {
    // 企鹅会游泳
}

// 现在可以安全替换
void makeBirdMove(AbstractBird bird) {
    bird.move();  // 无论是麻雀还是企鹅都能正常工作
}

4. 接口隔离原则(Interface Segregation Principle)

核心:客户端不应该依赖它不需要的接口,应该使用多个专门的接口,而不是一个总接口。

java
// ❌ 违反接口隔离:接口过于臃肿
interface BadWorker {
    void work();
    void eat();
    void sleep();
    void attendMeeting();
    void writeReport();
}

class HumanWorker implements BadWorker {
    public void work() { System.out.println("人类工作"); }
    public void eat() { System.out.println("人类吃饭"); }
    public void sleep() { System.out.println("人类睡觉"); }
    public void attendMeeting() { System.out.println("参加会议"); }
    public void writeReport() { System.out.println("写报告"); }
}

class RobotWorker implements BadWorker {
    public void work() { System.out.println("机器人工作"); }
    public void eat() { /* 机器人不吃饭,但被迫实现 */ }
    public void sleep() { /* 机器人不睡觉,但被迫实现 */ }
    public void attendMeeting() { /* 机器人不开会,但被迫实现 */ }
    public void writeReport() { System.out.println("生成报告"); }
}

// ✅ 符合接口隔离:拆分成小接口
interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

interface Sleepable {
    void sleep();
}

interface Meetable {
    void attendMeeting();
}

interface Reportable {
    void writeReport();
}

class GoodHumanWorker implements Workable, Eatable, Sleepable, Meetable, Reportable {
    public void work() { System.out.println("人类工作"); }
    public void eat() { System.out.println("人类吃饭"); }
    public void sleep() { System.out.println("人类睡觉"); }
    public void attendMeeting() { System.out.println("参加会议"); }
    public void writeReport() { System.out.println("写报告"); }
}

class GoodRobotWorker implements Workable, Reportable {
    public void work() { System.out.println("机器人工作"); }
    public void writeReport() { System.out.println("生成报告"); }
    // 不需要实现eat、sleep等接口
}

5. 依赖倒置原则(Dependency Inversion Principle)

核心:高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。

java
// ❌ 违反依赖倒置:高层依赖低层具体实现
class MySQLDatabase {
    public void saveData(String data) {
        System.out.println("保存到MySQL:" + data);
    }
}

class BadOrderService {
    private MySQLDatabase database = new MySQLDatabase();
    
    public void createOrder(String orderData) {
        // 业务逻辑
        database.saveData(orderData);
        // 如果要换成MongoDB,需要修改这里的代码
    }
}

// ✅ 符合依赖倒置:依赖抽象接口
interface Database {
    void save(String data);
    String query(String id);
}

class MySQL implements Database {
    @Override
    public void save(String data) {
        System.out.println("MySQL保存:" + data);
    }
    
    @Override
    public String query(String id) {
        return "从MySQL查询:" + id;
    }
}

class MongoDB implements Database {
    @Override
    public void save(String data) {
        System.out.println("MongoDB保存:" + data);
    }
    
    @Override
    public String query(String id) {
        return "从MongoDB查询:" + id;
    }
}

class Redis implements Database {
    @Override
    public void save(String data) {
        System.out.println("Redis缓存:" + data);
    }
    
    @Override
    public String query(String id) {
        return "从Redis查询:" + id;
    }
}

class GoodOrderService {
    private Database database;  // 依赖抽象
    
    // 通过构造器注入
    public GoodOrderService(Database database) {
        this.database = database;
    }
    
    public void createOrder(String orderData) {
        // 业务逻辑
        database.save(orderData);
        // 可以轻松切换数据库实现,无需修改代码
    }
}

class DIPDemo {
    public static void main(String[] args) {
        // 可以灵活切换实现
        GoodOrderService service1 = new GoodOrderService(new MySQL());
        service1.createOrder("订单1");
        
        GoodOrderService service2 = new GoodOrderService(new MongoDB());
        service2.createOrder("订单2");
        
        GoodOrderService service3 = new GoodOrderService(new Redis());
        service3.createOrder("订单3");
    }
}

SOLID原则总结:

mermaid
graph TB
    SOLID[SOLID原则]
    
    SOLID --> S[单一职责<br/>一个类只做一件事]
    SOLID --> O[开闭原则<br/>对扩展开放<br/>对修改关闭]
    SOLID --> L[里氏替换<br/>子类可替换父类]
    SOLID --> I[接口隔离<br/>小而专的接口]
    SOLID --> D[依赖倒置<br/>依赖抽象不依赖具体]
    
    S --> S1[易维护<br/>职责清晰]
    O --> O1[易扩展<br/>稳定性高]
    L --> L1[继承正确<br/>多态可靠]
    I --> I1[接口精简<br/>依赖最小]
    D --> D1[低耦合<br/>易测试]
    
    classDef principleStyle fill:#4A90E2,stroke:none,color:#fff
    classDef benefitStyle fill:#50C878,stroke:none,color:#fff
    
    class SOLID,S,O,L,I,D principleStyle
    class S1,O1,L1,I1,D1 benefitStyle

这五大原则相辅相成,共同指导我们编写高质量的面向对象代码。在实际开发中,应该灵活运用,避免过度设计。

更新: 2025-12-04 17:34:27
原文: https://www.yuque.com/u22210564/zoxfmt/doc-02-05

Java 后端面试知识库