跳转至

面向对象编程 (Object-Oriented Programming)

Java的核心思想,理解封装、继承、多态是编写高质量代码的基础

目录


1. 类与对象 (Class and Object)

1.1 类的定义 (Class Definition)

类是对象的模板,定义了对象的属性和行为。

/**
 * 学生类示例
 * Student Class Example
 * 
 * 类的组成:
 * 1. 成员变量(属性) - Member Variables (Fields)
 * 2. 构造器 - Constructors
 * 3. 成员方法 - Member Methods
 */
public class Student {

    // 1. 成员变量(实例变量) - Instance Variables
    private String name;        // 姓名
    private int age;            // 年龄
    private String studentId;   // 学号

    // 静态变量(类变量) - Static Variables
    private static int studentCount = 0; // 学生总数

    // 常量 - Constant
    private static final String SCHOOL_NAME = "清华大学";

    // 2. 构造器 - Constructors

    /**
     * 无参构造器 (No-Argument Constructor)
     * 如果不显式定义,Java会提供默认构造器
     */
    public Student() {
        studentCount++;
    }

    /**
     * 有参构造器 (Parameterized Constructor)
     */
    public Student(String name, int age) {
        this.name = name; // this关键字指向当前对象
        this.age = age;
        studentCount++;
    }

    /**
     * 全参构造器 (All-Args Constructor)
     */
    public Student(String name, int age, String studentId) {
        this(name, age); // 调用其他构造器,必须在第一行
        this.studentId = studentId;
    }

    // 3. 成员方法 - Member Methods

    /**
     * 实例方法 (Instance Method)
     * 需要通过对象调用
     */
    public void study() {
        System.out.println(name + " 正在学习...");
    }

    /**
     * 带参数和返回值的方法
     * Method with Parameters and Return Value
     */
    public int getAgeAfterYears(int years) {
        return this.age + years;
    }

    /**
     * 静态方法 (Static Method)
     * 可以通过类名直接调用
     */
    public static int getStudentCount() {
        return studentCount;
    }

    // Getter和Setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        // 可以在setter中添加验证逻辑
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0 && age < 150) {
            this.age = age;
        }
    }

    /**
     * 重写Object类的toString方法
     * Override toString() method
     */
    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + 
               ", studentId='" + studentId + "'}";
    }
}

1.2 对象的创建和使用 (Object Creation and Usage)

/**
 * 对象使用示例
 * Object Usage Example
 */
public class StudentTest {

    public static void main(String[] args) {
        // 创建对象 (Create Object)
        Student student1 = new Student();
        student1.setName("张三");
        student1.setAge(20);

        // 使用有参构造器创建对象
        Student student2 = new Student("李四", 22);

        // 调用实例方法
        student1.study(); // 输出:张三 正在学习...

        // 调用静态方法 - 通过类名调用(推荐)
        System.out.println("学生总数: " + Student.getStudentCount());

        // 访问对象信息
        System.out.println(student2.toString());

        // 对象比较
        Student student3 = new Student("李四", 22);
        System.out.println(student2 == student3); // false - 不同对象

        // 对象引用
        Student student4 = student2; // student4和student2指向同一对象
        System.out.println(student2 == student4); // true - 同一对象
    }
}

1.3 this 和 static 关键字

this 关键字

  • 指向当前对象的引用
  • 三种用法:
  • 访问成员变量:this.name
  • 调用其他构造器:this(...)
  • 返回当前对象:return this;
public class Person {
    private String name;

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

    public Person setName(String name) {
        this.name = name;
        return this; // 返回当前对象,支持链式调用
    }
}

static 关键字

  • 属于类而非对象
  • 可以修饰: 变量、方法、代码块、内部类
public class Counter {

    // 静态变量 - 所有对象共享
    private static int count = 0;

    // 实例变量 - 每个对象独立
    private int instanceCount = 0;

    // 静态代码块 - 类加载时执行一次
    static {
        System.out.println("Counter类被加载");
        count = 100;
    }

    // 实例代码块 - 每次创建对象时执行
    {
        System.out.println("创建Counter对象");
        instanceCount = 1;
    }

    // 静态方法
    public static void increment() {
        count++;
        // 注意:静态方法不能访问实例变量和实例方法
        // instanceCount++; // 编译错误!
    }

    // 实例方法可以访问静态成员
    public void print() {
        System.out.println("count=" + count + 
                         ", instanceCount=" + instanceCount);
    }
}

2. 封装 (Encapsulation)

封装是隐藏对象的内部实现细节,只暴露必要的接口。

2.1 访问修饰符 (Access Modifiers)

修饰符 同一类 同一包 子类 不同包 说明
private 最严格
默认(package-private) 包可见
protected 子类可见
public 公开
/**
 * 封装示例:银行账户
 * Encapsulation Example: Bank Account
 */
public class BankAccount {

    // private:隐藏内部数据
    private String accountNumber;
    private double balance;
    private String password;

    public BankAccount(String accountNumber, String password) {
        this.accountNumber = accountNumber;
        this.password = password;
        this.balance = 0.0;
    }

    /**
     * 存款方法 (Deposit)
     * 提供公开接口,内部进行验证
     */
    public boolean deposit(double amount) {
        if (amount <= 0) {
            System.out.println("存款金额必须大于0");
            return false;
        }

        balance += amount;
        System.out.println("存款成功,当前余额: " + balance);
        return true;
    }

    /**
     * 取款方法 (Withdraw)
     * 需要密码验证,体现封装的安全性
     */
    public boolean withdraw(String password, double amount) {
        // 验证密码
        if (!this.password.equals(password)) {
            System.out.println("密码错误");
            return false;
        }

        // 验证金额
        if (amount <= 0) {
            System.out.println("取款金额必须大于0");
            return false;
        }

        // 验证余额
        if (amount > balance) {
            System.out.println("余额不足");
            return false;
        }

        balance -= amount;
        System.out.println("取款成功,当前余额: " + balance);
        return true;
    }

    /**
     * 查询余额 (Check Balance)
     * 需要密码验证
     */
    public double getBalance(String password) {
        if (!this.password.equals(password)) {
            System.out.println("密码错误");
            return -1;
        }
        return balance;
    }

    // 只提供getter,不提供setter,保护账号不被修改
    public String getAccountNumber() {
        return accountNumber;
    }
}

2.2 封装的优势 ✅

  1. 数据安全:隐藏内部数据,防止非法访问
  2. 灵活性:修改内部实现不影响外部调用
  3. 可维护性:降低耦合,便于维护
  4. 数据验证:在setter中添加验证逻辑

3. 继承 (Inheritance)

继承是子类继承父类的属性和方法,实现代码复用。

3.1 继承的基本语法

/**
 * 父类:动物
 * Parent Class: Animal
 */
public class Animal {

    // protected:子类可以访问
    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 void makeSound() {
        System.out.println(name + " 发出声音");
    }
}

/**
 * 子类:狗
 * Child Class: Dog
 * 
 * extends关键字实现继承
 */
public class Dog extends Animal {

    // 子类特有的属性
    private String breed; // 品种

    /**
     * 子类构造器
     * 必须调用父类构造器
     */
    public Dog(String name, int age, String breed) {
        super(name, age); // 调用父类构造器,必须在第一行
        this.breed = breed;
    }

    /**
     * 方法重写 (Method Overriding)
     * 子类重新实现父类的方法
     */
    @Override // 注解,帮助编译器检查是否正确重写
    public void makeSound() {
        System.out.println(name + " 汪汪叫");
    }

    /**
     * 子类特有的方法
     * Dog-Specific Method
     */
    public void fetch() {
        System.out.println(name + " 正在捡球");
    }

    /**
     * 调用父类方法
     * Call Parent Method
     */
    public void parentMakeSound() {
        super.makeSound(); // 使用super调用父类方法
    }
}

/**
 * 子类:猫
 * Child Class: Cat
 */
public class Cat extends Animal {

    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void makeSound() {
        System.out.println(name + " 喵喵叫");
    }

    public void climb() {
        System.out.println(name + " 正在爬树");
    }
}

3.2 继承的使用

/**
 * 继承使用示例
 * Inheritance Usage Example
 */
public class InheritanceDemo {

    public static void main(String[] args) {
        // 创建子类对象
        Dog dog = new Dog("旺财", 3, "金毛");
        Cat cat = new Cat("小花", 2);

        // 调用继承的方法
        dog.eat();   // 继承自Animal
        dog.sleep(); // 继承自Animal

        // 调用重写的方法
        dog.makeSound(); // 输出:旺财 汪汪叫
        cat.makeSound(); // 输出:小花 喵喵叫

        // 调用子类特有的方法
        dog.fetch();
        cat.climb();

        // 向上转型 (Upcasting) - 子类对象赋给父类变量
        Animal animal1 = dog; // 自动转型
        Animal animal2 = cat;

        animal1.makeSound(); // 输出:旺财 汪汪叫(多态)
        // animal1.fetch(); // 编译错误!父类引用不能调用子类特有方法

        // 向下转型 (Downcasting) - 需要强制转型
        if (animal1 instanceof Dog) {
            Dog myDog = (Dog) animal1; // 强制转型
            myDog.fetch(); // 现在可以调用了
        }
    }
}

3.3 方法重写的规则 (Override Rules)

必须遵守的规则 ⚠️

  1. 方法名相同
  2. 参数列表相同
  3. 返回类型相同或是子类(协变返回类型)
  4. 访问修饰符不能更严格(可以更宽松)
  5. 不能抛出新的或更广的检查异常
class Parent {
    protected Object method() throws IOException {
        return new Object();
    }
}

class Child extends Parent {
    // ✅ 合法:返回类型是子类
    @Override
    public String method() throws IOException {
        return "Hello";
    }

    // ❌ 编译错误:访问修饰符更严格
    // @Override
    // private Object method() { }

    // ❌ 编译错误:抛出了新的检查异常
    // @Override
    // public Object method() throws SQLException { }
}

3.4 super 关键字

  • 调用父类构造器super(...)
  • 调用父类方法super.method()
  • 访问父类变量super.field
class Vehicle {
    protected int speed = 100;

    public Vehicle() {
        System.out.println("Vehicle构造器");
    }

    public void move() {
        System.out.println("交通工具移动");
    }
}

class Car extends Vehicle {
    private int speed = 200; // 隐藏父类变量

    public Car() {
        super(); // 调用父类构造器
        System.out.println("Car构造器");
    }

    public void displaySpeed() {
        System.out.println("子类speed: " + this.speed); // 200
        System.out.println("父类speed: " + super.speed); // 100
    }

    @Override
    public void move() {
        super.move(); // 先调用父类方法
        System.out.println("汽车在路上行驶");
    }
}

3.5 继承的注意事项 ⚠️

  1. Java只支持单继承:一个类只能继承一个父类
  2. 构造器不能被继承:但子类构造器必须调用父类构造器
  3. 私有成员不能被继承:但可以通过public/protected方法访问
  4. final类不能被继承final class CannotExtend { }
  5. final方法不能被重写public final void method() { }
  6. 避免在构造器中调用可重写的方法:可能导致意外行为

4. 多态 (Polymorphism)

多态是同一操作作用于不同对象,产生不同的执行结果。

4.1 多态的实现方式

  1. 方法重载 (Overload) - 编译时多态
  2. 方法重写 (Override) - 运行时多态

4.2 运行时多态示例

/**
 * 多态示例:图形计算
 * Polymorphism Example: Shape Calculation
 */
abstract class Shape {
    protected String name;

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

    // 抽象方法,子类必须实现
    public abstract double getArea();

    public abstract double getPerimeter();

    public void display() {
        System.out.println(name + " - 面积: " + getArea() + 
                         ", 周长: " + getPerimeter());
    }
}

/**
 * 圆形
 * Circle
 */
class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        super("圆形");
        this.radius = radius;
    }

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

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

/**
 * 矩形
 * Rectangle
 */
class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        super("矩形");
        this.width = width;
        this.height = height;
    }

    @Override
    public double getArea() {
        return width * height;
    }

    @Override
    public double getPerimeter() {
        return 2 * (width + height);
    }
}

/**
 * 多态演示
 * Polymorphism Demo
 */
public class PolymorphismDemo {

    public static void main(String[] args) {
        // 父类引用指向子类对象 - 多态的体现
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);

        // 同一方法调用,不同的执行结果
        circle.display();    // 调用Circle的实现
        rectangle.display(); // 调用Rectangle的实现

        // 多态数组
        Shape[] shapes = {
            new Circle(3),
            new Rectangle(5, 10),
            new Circle(7)
        };

        // 统一处理不同类型的对象
        double totalArea = 0;
        for (Shape shape : shapes) {
            totalArea += shape.getArea(); // 多态:运行时决定调用哪个方法
        }
        System.out.println("总面积: " + totalArea);
    }
}

4.3 多态的三个必要条件

  1. 继承:必须有子类继承父类或实现接口
  2. 重写:子类必须重写父类的方法
  3. 向上转型:父类引用指向子类对象

4.4 instanceof 运算符

/**
 * instanceof 使用示例
 * instanceof Operator Example
 */
public class InstanceofDemo {

    public static void main(String[] args) {
        Animal animal = new Dog("旺财", 3, "金毛");

        // 判断对象的实际类型
        if (animal instanceof Dog) {
            System.out.println("这是一只狗");
            Dog dog = (Dog) animal;
            dog.fetch();
        }

        // 继承关系也会返回true
        System.out.println(animal instanceof Animal); // true
        System.out.println(animal instanceof Dog);    // true
        System.out.println(animal instanceof Cat);    // false

        // null检查
        Animal nullAnimal = null;
        System.out.println(nullAnimal instanceof Animal); // false
    }
}

4.5 多态的优势 ✅

  1. 可扩展性:添加新类不需要修改现有代码
  2. 可维护性:统一接口,简化代码
  3. 灵活性:运行时动态绑定方法
  4. 解耦:降低类之间的耦合度

5. 接口 (Interface)

接口是一种抽象类型,定义了类应该实现的行为规范。

5.1 接口的定义和实现

/**
 * 飞行接口
 * Flyable Interface
 * 
 * 接口中的方法默认是public abstract
 * 接口中的变量默认是public static final
 */
public interface Flyable {

    // 常量 - 自动添加public static final
    int MAX_SPEED = 1000;

    // 抽象方法 - 自动添加public abstract
    void fly();

    void land();

    // Java 8+: 默认方法 (Default Method)
    default void takeOff() {
        System.out.println("起飞准备...");
        fly();
    }

    // Java 8+: 静态方法 (Static Method)
    static void checkWeather() {
        System.out.println("检查天气状况");
    }

    // Java 9+: 私有方法 (Private Method)
    private void log(String message) {
        System.out.println("日志: " + message);
    }
}

/**
 * 游泳接口
 * Swimmable Interface
 */
public interface Swimmable {
    void swim();
}

/**
 * 鸟类 - 实现飞行接口
 * Bird - Implements Flyable Interface
 */
public class Bird implements Flyable {

    private String name;

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

    @Override
    public void fly() {
        System.out.println(name + " 在天空飞翔");
    }

    @Override
    public void land() {
        System.out.println(name + " 降落到地面");
    }
}

/**
 * 鸭子 - 实现多个接口
 * Duck - Implements Multiple Interfaces
 * 
 * Java支持多接口实现,弥补单继承的限制
 */
public class Duck implements Flyable, Swimmable {

    private String name;

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

    @Override
    public void fly() {
        System.out.println(name + " 飞得不太高");
    }

    @Override
    public void land() {
        System.out.println(name + " 降落到水面");
    }

    @Override
    public void swim() {
        System.out.println(name + " 在水中游泳");
    }
}

5.2 接口的使用

/**
 * 接口使用示例
 * Interface Usage Example
 */
public class InterfaceDemo {

    public static void main(String[] args) {
        // 接口引用指向实现类对象
        Flyable bird = new Bird("麻雀");
        bird.fly();
        bird.takeOff(); // 调用默认方法

        // 静态方法通过接口名调用
        Flyable.checkWeather();

        // 多接口实现
        Duck duck = new Duck("唐老鸭");
        duck.fly();
        duck.swim();

        // 接口多态
        Flyable flyable = duck;
        flyable.fly();

        Swimmable swimmable = duck;
        swimmable.swim();
    }
}

5.3 接口的特点

  1. 接口不能被实例化
  2. 接口中的方法默认是public abstract
  3. 接口中的变量默认是public static final
  4. 一个类可以实现多个接口
  5. 接口可以继承多个接口
// 接口继承接口
public interface A {
    void methodA();
}

public interface B {
    void methodB();
}

// 接口可以继承多个接口
public interface C extends A, B {
    void methodC();
}

6. 抽象类 (Abstract Class)

抽象类是介于普通类和接口之间的一种类型。

6.1 抽象类的定义

/**
 * 抽象类:员工
 * Abstract Class: Employee
 * 
 * 抽象类特点:
 * 1. 不能被实例化
 * 2. 可以有抽象方法和普通方法
 * 3. 可以有成员变量
 * 4. 可以有构造器
 */
public abstract class Employee {

    // 成员变量
    protected String name;
    protected String id;
    protected double baseSalary;

    // 构造器
    public Employee(String name, String id, double baseSalary) {
        this.name = name;
        this.id = id;
        this.baseSalary = baseSalary;
    }

    // 抽象方法 - 子类必须实现
    public abstract double calculateSalary();

    // 普通方法 - 子类可以继承或重写
    public void displayInfo() {
        System.out.println("员工姓名: " + name);
        System.out.println("员工ID: " + id);
        System.out.println("工资: " + calculateSalary());
    }

    // 普通方法
    public String getName() {
        return name;
    }
}

/**
 * 全职员工
 * FullTimeEmployee
 */
public class FullTimeEmployee extends Employee {

    private double bonus;

    public FullTimeEmployee(String name, String id, 
                           double baseSalary, double bonus) {
        super(name, id, baseSalary);
        this.bonus = bonus;
    }

    @Override
    public double calculateSalary() {
        return baseSalary + bonus;
    }
}

/**
 * 兼职员工
 * PartTimeEmployee
 */
public class PartTimeEmployee extends Employee {

    private int hoursWorked;
    private double hourlyRate;

    public PartTimeEmployee(String name, String id, 
                           int hoursWorked, double hourlyRate) {
        super(name, id, 0); // 基本工资为0
        this.hoursWorked = hoursWorked;
        this.hourlyRate = hourlyRate;
    }

    @Override
    public double calculateSalary() {
        return hoursWorked * hourlyRate;
    }
}

6.2 抽象类 vs 接口

维度 抽象类 接口
关键字 abstract class interface
方法 可以有抽象和普通方法 主要是抽象方法(Java 8+可有默认方法)
变量 可以有实例变量 只能有常量(public static final)
构造器 可以有 不能有
继承 单继承 多实现
访问修饰符 任意 方法默认public
使用场景 有共同实现的父类 定义行为规范

6.3 何时使用抽象类 vs 接口?

使用抽象类 ✅

  • 多个类有共同的字段或方法
  • 需要非public成员
  • 需要提供部分实现

使用接口 ✅

  • 定义行为规范
  • 需要多重继承
  • 不同继承树的类具有相同行为
// 示例:抽象类提供部分实现
public abstract class HttpServlet {

    // 公共逻辑
    public void service(HttpRequest request, HttpResponse response) {
        String method = request.getMethod();
        if ("GET".equals(method)) {
            doGet(request, response);
        } else if ("POST".equals(method)) {
            doPost(request, response);
        }
    }

    // 子类实现具体逻辑
    protected abstract void doGet(HttpRequest request, HttpResponse response);
    protected abstract void doPost(HttpRequest request, HttpResponse response);
}

// 示例:接口定义行为规范
public interface Comparable<T> {
    int compareTo(T o);
}

7. 内部类 (Inner Classes)

内部类是定义在另一个类内部的类。

7.1 成员内部类 (Member Inner Class)

/**
 * 外部类
 * Outer Class
 */
public class Outer {

    private String outerField = "外部类字段";

    /**
     * 成员内部类
     * Member Inner Class
     * 
     * 特点:
     * 1. 可以访问外部类的所有成员(包括private)
     * 2. 需要先创建外部类对象才能创建内部类对象
     */
    public class Inner {

        private String innerField = "内部类字段";

        public void display() {
            // 可以直接访问外部类成员
            System.out.println("访问外部类字段: " + outerField);
            System.out.println("内部类字段: " + innerField);

            // 使用外部类名.this访问外部类实例
            System.out.println(Outer.this.outerField);
        }
    }

    public void test() {
        // 外部类可以直接创建内部类对象
        Inner inner = new Inner();
        inner.display();
    }
}

/**
 * 使用示例
 */
public class InnerClassDemo {
    public static void main(String[] args) {
        // 先创建外部类对象
        Outer outer = new Outer();

        // 通过外部类对象创建内部类对象
        Outer.Inner inner = outer.new Inner();
        inner.display();
    }
}

7.2 静态内部类 (Static Nested Class)

/**
 * 静态内部类示例
 * Static Nested Class Example
 */
public class OuterStatic {

    private static String staticField = "静态字段";
    private String instanceField = "实例字段";

    /**
     * 静态内部类
     * Static Nested Class
     * 
     * 特点:
     * 1. 不依赖外部类实例
     * 2. 只能访问外部类的静态成员
     * 3. 可以有静态成员
     */
    public static class StaticInner {

        private static int count = 0;

        public void display() {
            // 可以访问外部类的静态成员
            System.out.println("静态字段: " + staticField);

            // 不能访问外部类的实例成员
            // System.out.println(instanceField); // 编译错误!
        }
    }
}

/**
 * 使用示例
 */
public class StaticInnerDemo {
    public static void main(String[] args) {
        // 不需要外部类对象,直接创建
        OuterStatic.StaticInner inner = new OuterStatic.StaticInner();
        inner.display();
    }
}

7.3 局部内部类 (Local Inner Class)

/**
 * 局部内部类示例
 * Local Inner Class Example
 */
public class LocalInnerDemo {

    private String field = "外部类字段";

    public void method() {
        final String localVar = "局部变量"; // 必须是final或effectively final

        /**
         * 局部内部类
         * 定义在方法内部
         */
        class LocalInner {
            public void display() {
                System.out.println(field);     // 可以访问外部类成员
                System.out.println(localVar);  // 可以访问局部变量
            }
        }

        // 在方法内使用
        LocalInner inner = new LocalInner();
        inner.display();
    }
}

7.4 匿名内部类 (Anonymous Inner Class)

/**
 * 匿名内部类示例
 * Anonymous Inner Class Example
 */
public class AnonymousInnerDemo {

    public static void main(String[] args) {
        // 匿名内部类实现接口
        Flyable flyable = new Flyable() {
            @Override
            public void fly() {
                System.out.println("匿名类飞行");
            }

            @Override
            public void land() {
                System.out.println("匿名类降落");
            }
        };

        flyable.fly();

        // 匿名内部类继承抽象类
        Animal animal = new Animal("匿名动物", 0) {
            @Override
            public void makeSound() {
                System.out.println("未知声音");
            }
        };

        animal.makeSound();

        // Java 8 Lambda表达式可以替代单方法接口的匿名内部类
        // Runnable task = () -> System.out.println("Lambda表达式");
    }
}

7.5 内部类的应用场景

  1. 封装辅助类:工具类或辅助类只被外部类使用
  2. 事件处理:GUI编程中的事件监听器
  3. 迭代器实现:集合类的迭代器通常是内部类
  4. 回调机制:匿名内部类实现回调接口

8. 最佳实践 (Best Practices)

8.1 设计原则

  1. 单一职责原则 (SRP):一个类只负责一个功能
  2. 开闭原则 (OCP):对扩展开放,对修改关闭
  3. 里氏替换原则 (LSP):子类可以替换父类
  4. 接口隔离原则 (ISP):接口应该小而专注
  5. 依赖倒置原则 (DIP):依赖抽象而非具体实现

8.2 代码规范

// ✅ 好的实践
public class GoodPractice {

    // 1. 成员变量私有化
    private String name;

    // 2. 提供getter/setter
    public String getName() {
        return name;
    }

    public void setName(String name) {
        // 3. setter中进行验证
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }

    // 4. 重写equals和hashCode
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        GoodPractice that = (GoodPractice) obj;
        return Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    // 5. 重写toString
    @Override
    public String toString() {
        return "GoodPractice{name='" + name + "'}";
    }
}

9. 面试高频问题 (Frequently Asked Interview Questions)

Q1: 面向对象的三大特性是什么?⭐⭐⭐⭐⭐

答案:

  1. 封装 (Encapsulation)
  2. 隐藏内部实现,只暴露必要接口
  3. 通过private修饰符和public方法实现

  4. 继承 (Inheritance)

  5. 子类继承父类的属性和方法,实现代码复用
  6. 使用extends关键字

  7. 多态 (Polymorphism)

  8. 同一操作作用于不同对象,产生不同结果
  9. 通过方法重写和向上转型实现

Q2: 重载和重写的区别?⭐⭐⭐⭐⭐

答案:

维度 重载 (Overload) 重写 (Override)
位置 同一个类 父类和子类
方法名 相同 相同
参数列表 不同(重点) 相同
返回类型 可以不同 相同或子类
访问修饰符 可以不同 不能更严格
异常 可以不同 不能更广
发生时机 编译期 运行期
多态 编译时多态 运行时多态

Q3: 接口和抽象类的区别?⭐⭐⭐⭐⭐

答案:6.2节

Q4: 什么是多态?如何实现?⭐⭐⭐⭐⭐

答案:

多态是指同一操作作用于不同对象,产生不同的执行结果。

实现方式: 1. 继承:子类继承父类或实现接口 2. 重写:子类重写父类方法 3. 向上转型:父类引用指向子类对象

好处: - 可扩展性:添加新类不需要修改现有代码 - 灵活性:运行时动态绑定 - 可维护性:统一接口,简化代码

Q5: super和this的区别?⭐⭐⭐⭐

答案:

关键字 作用 用法
this 当前对象的引用 1. 访问成员变量
2. 调用构造器
3. 返回当前对象
super 父类对象的引用 1. 调用父类构造器
2. 调用父类方法
3. 访问父类变量

Q6: 静态方法能被重写吗?⭐⭐⭐⭐

答案:

不能。静态方法属于类而不属于对象,不存在运行时多态。子类可以定义同名静态方法,但这是**方法隐藏**,不是重写。

class Parent {
    public static void staticMethod() {
        System.out.println("父类静态方法");
    }
}

class Child extends Parent {
    // 方法隐藏,不是重写
    public static void staticMethod() {
        System.out.println("子类静态方法");
    }
}

// 调用时根据引用类型决定,不是实际对象类型
Parent p = new Child();
p.staticMethod(); // 输出:父类静态方法

Q7: 构造器可以被继承吗?⭐⭐⭐

答案:

不能。构造器不能被继承,但子类构造器必须调用父类构造器(显式或隐式)。

Q8: 内部类有哪些?各有什么特点?⭐⭐⭐⭐

答案:

类型 特点 使用场景
成员内部类 需要外部类实例,可访问外部类所有成员 紧密关联的辅助类
静态内部类 不需要外部类实例,只能访问静态成员 逻辑上分组的工具类
局部内部类 定义在方法内,作用域仅在方法内 临时使用的类
匿名内部类 没有名字,通常用于接口或抽象类的快速实现 事件处理、回调

Q9: 为什么要封装?⭐⭐⭐⭐

答案:

  1. 数据安全:隐藏内部数据,防止非法访问和修改
  2. 灵活性:修改内部实现不影响外部调用
  3. 可维护性:降低耦合,便于维护
  4. 数据验证:在setter中添加验证逻辑
  5. 只读/只写:通过提供或不提供getter/setter控制访问

Q10: Object类有哪些方法?⭐⭐⭐⭐

答案:

// Object类的主要方法
1. equals(Object obj)         - 比较对象是否相等
2. hashCode()                 - 返回对象的哈希码
3. toString()                 - 返回对象的字符串表示
4. clone()                    - 克隆对象
5. getClass()                 - 获取对象的Class对象
6. notify() / notifyAll()     - 线程通信
7. wait()                     - 线程等待
8. finalize()                 - 对象被GC前调用已过时

总结 (Summary)

本章介绍了Java面向对象编程的核心概念:

  • 类与对象:Java程序的基本单位
  • 封装:隐藏实现,保护数据
  • 继承:代码复用,建立类层次
  • 多态:灵活扩展,降低耦合
  • 接口:定义行为规范,支持多实现
  • 抽象类:提供部分实现的父类
  • 内部类:封装辅助类

面向对象是Java的核心,建议:

  1. 理解概念:深刻理解封装、继承、多态的本质
  2. 实践应用:多写代码,体会OOP的优势
  3. 设计模式:学习设计模式,提升设计能力
  4. 阅读源码:阅读JDK源码,学习优秀设计

下一篇: 03 - Java高级特性 →

返回目录: Java 语言基础导航 ←