面试专题:面向对象
面试专题:面向对象
概述
面向对象编程(OOP)是Java的核心特性,也是面试中必问的知识点。本章节将系统讲解面向对象的核心概念、设计原则以及常见面试题,帮助你全面掌握面向对象编程的精髓。
知识要点
1. 核心概念
1.1 封装
封装是将数据和方法组合在一个单元中的机制,它隐藏了对象的内部实现细节,只暴露必要的接口。
public class User {
    // 私有属性,外部无法直接访问
    private String name;
    private int 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) {
            this.age = age;
        } else {
            throw new IllegalArgumentException("年龄必须在0-150之间");
        }
    }
}1.2 继承
继承允许一个类获取另一个类的属性和方法。被继承的类称为父类,继承的类称为子类。
// 父类
public class Animal {
    public void eat() {
        System.out.println("动物会进食");
    }
}
// 子类
public class Dog extends Animal {
    // 重写父类方法
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    // 子类特有方法
    public void bark() {
        System.out.println("狗会汪汪叫");
    }
}1.3 多态
多态是指同一个方法可以有不同的实现方式。在Java中,多态通过方法重载和方法重写实现。
// 方法重载 - 同一个类中,方法名相同,参数不同
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public double add(double a, double b) {
        return a + b;
    }
}
// 方法重写 - 子类重写父类的方法
public class Animal {
    public void move() {
        System.out.println("动物会移动");
    }
}
public class Bird extends Animal {
    @Override
    public void move() {
        System.out.println("鸟会飞");
    }
}1.4 抽象
抽象是指忽略细节,关注本质。在Java中,抽象通过抽象类和接口实现。
// 抽象类
public abstract class Shape {
    // 抽象方法,没有实现
    public abstract double getArea();
    // 具体方法
    public void display() {
        System.out.println("这是一个形状");
    }
}
// 接口
public interface Flyable {
    void fly();
}2. 设计原则
2.1 单一职责原则
一个类应该只有一个引起它变化的原因。
// 违反单一职责原则
public class User {
    private String name;
    private int age;
    public void save() {
        // 保存用户到数据库
    }
    public void validate() {
        // 验证用户信息
    }
}
// 遵循单一职责原则
public class User {
    private String name;
    private int age;
    // 只包含用户相关的属性和方法
}
public class UserRepository {
    public void save(User user) {
        // 保存用户到数据库
    }
}
public class UserValidator {
    public boolean validate(User user) {
        // 验证用户信息
        return true;
    }
}2.2 开闭原则
对扩展开放,对修改关闭。
// 违反开闭原则
public class ShapeCalculator {
    public double calculateArea(Shape shape) {
        if (shape instanceof Circle) {
            return Math.PI * ((Circle) shape).getRadius() * ((Circle) shape).getRadius();
        } else if (shape instanceof Rectangle) {
            return ((Rectangle) shape).getWidth() * ((Rectangle) shape).getHeight();
        }
        // 每添加一个新形状,都需要修改这个方法
        return 0;
    }
}
// 遵循开闭原则
public abstract class Shape {
    public abstract double getArea();
}
public class Circle extends Shape {
    private double radius;
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}
public class Rectangle extends Shape {
    private double width;
    private double height;
    @Override
    public double getArea() {
        return width * height;
    }
}
public class ShapeCalculator {
    public double calculateArea(Shape shape) {
        return shape.getArea();
        // 不需要修改这个方法,只需要扩展新的形状类
    }
}2.3 里氏替换原则
子类可以替换父类,而不会改变程序的正确性。
// 违反里氏替换原则
public class Bird {
    public void fly() {
        System.out.println("鸟会飞");
    }
}
public class Ostrich extends Bird {
    @Override
    public void fly() {
        // 鸵鸟不会飞,违反了里氏替换原则
        throw new UnsupportedOperationException("鸵鸟不会飞");
    }
}
// 遵循里氏替换原则
public class Bird {
    public void move() {
        System.out.println("鸟会移动");
    }
}
public class Ostrich extends Bird {
    @Override
    public void move() {
        System.out.println("鸵鸟会跑");
    }
}2.4 接口隔离原则
客户端不应该被迫实现它不需要的接口。
// 违反接口隔离原则
public interface Animal {
    void eat();
    void fly();
    void swim();
}
public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    @Override
    public void fly() {
        // 狗不会飞,但是被迫实现这个方法
        throw new UnsupportedOperationException();
    }
    @Override
    public void swim() {
        System.out.println("狗会游泳");
    }
}
// 遵循接口隔离原则
public interface Eat {
    void eat();
}
public interface Fly {
    void fly();
}
public interface Swim {
    void swim();
}
public class Dog implements Eat, Swim {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    @Override
    public void swim() {
        System.out.println("狗会游泳");
    }
}2.5 依赖倒置原则
高层模块不应该依赖低层模块,它们都应该依赖抽象。
// 违反依赖倒置原则
public class Light {
    public void turnOn() {
        System.out.println("灯打开了");
    }
    public void turnOff() {
        System.out.println("灯关闭了");
    }
}
public class Switch {
    private Light light;
    public Switch(Light light) {
        this.light = light;
    }
    public void operate() {
        light.turnOn();
    }
}
// 遵循依赖倒置原则
public interface Switchable {
    void turnOn();
    void turnOff();
}
public class Light implements Switchable {
    @Override
    public void turnOn() {
        System.out.println("灯打开了");
    }
    @Override
    public void turnOff() {
        System.out.println("灯关闭了");
    }
}
public class Switch {
    private Switchable device;
    public Switch(Switchable device) {
        this.device = device;
    }
    public void operate() {
        device.turnOn();
    }
}知识扩展
设计思想
面向对象编程的核心设计思想是将现实世界中的事物抽象成对象,通过对象之间的交互来解决问题。它强调封装、继承、多态和抽象,这些概念帮助我们编写更加模块化、可维护和可扩展的代码。
避坑指南
- 过度继承:不要为了复用少量代码而创建深层次的继承关系,这会导致代码难以理解和维护。 
- 违反里氏替换原则:子类不应该修改父类的行为,否则会导致代码出现意外的错误。 
- 接口臃肿:不要创建包含太多方法的接口,这会导致实现接口的类变得非常复杂。 
- 忽视封装:不要直接暴露对象的内部状态,应该通过方法来访问和修改对象的状态。 
- 混合责任:一个类不应该承担太多的责任,否则会导致代码难以维护和测试。 
深度思考题
思考题: 什么是面向对象编程?它有什么优点?
回答: 面向对象编程是一种编程范式,它将现实世界中的事物抽象成对象,通过对象之间的交互来解决问题。
优点:
- 模块化:每个对象都是一个独立的模块,便于代码的维护和复用。
- 可扩展性:通过继承和多态,可以方便地扩展现有代码。
- 可维护性:对象封装了数据和方法,隐藏了内部实现细节,使得代码更加易于维护。
- 可读性:面向对象编程的代码更加接近自然语言,易于理解。
- 可测试性:每个对象都是一个独立的单元,便于进行单元测试。
思考题: 什么是多态?它在Java中有哪些实现方式?
回答: 多态是指同一个方法可以有不同的实现方式。
在Java中,多态通过以下方式实现:
- 方法重载:同一个类中,方法名相同,参数不同。
- 方法重写:子类重写父类的方法。
- 接口实现:不同的类实现同一个接口的方法。
- 抽象类:子类实现抽象类的抽象方法。
思考题: 什么是设计模式?它与面向对象编程有什么关系?
回答: 设计模式是解决特定问题的最佳实践,它是面向对象编程的经验总结。
关系:
- 设计模式是面向对象编程的具体应用。
- 设计模式体现了面向对象编程的核心思想,如封装、继承、多态和抽象。
- 设计模式帮助我们编写更加模块化、可维护和可扩展的代码。
- 设计模式是面向对象编程的高级应用,它需要我们掌握面向对象编程的核心概念和设计原则。
