知识模块
☕ Java 知识模块
一、Java 基础
面向对象(OOP)

面向对象(OOP)

面向对象编程(Object-Oriented Programming)是 Java 的核心思想,其三大特性是封装、继承、多态。掌握这三大特性是理解 Java 面向对象编程的基础。


一、封装(Encapsulation)

1.1 什么是封装

封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。它是面向对象编程的基础特性,核心思想是"高内聚、低耦合"。

┌─────────────────────────────────────┐
│            外部调用者               │
│                 ↓                   │
│     ┌───────────────────────┐      │
│     │   public 方法(接口)  │      │
│     └───────────────────────┘      │
│                 ↑                   │
│     ┌───────────────────────┐      │
│     │  private 属性(隐藏)  │      │
│     └───────────────────────┘      │
└─────────────────────────────────────┘

1.2 封装的实现方式

使用访问修饰符

修饰符同一类同一包子类(不同包)其他类
public
protected
default
private

标准封装模式:JavaBean

public class Person {
    // 1. 私有化属性
    private String name;
    private int age;
    
    // 2. 无参构造器
    public Person() {}
    
    // 3. 有参构造器
    public Person(String name, int age) {
        this.name = name;
        this.setAge(age);  // 调用 setter 进行校验
    }
    
    // 4. getter 方法
    public String getName() {
        return name;
    }
    
    // 5. setter 方法(包含校验逻辑)
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        // 在 setter 中进行数据校验
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄必须在 0-150 之间");
        }
        this.age = age;
    }
}

1.3 封装的优势

优势说明
数据安全隐藏敏感数据,防止外部随意修改
数据校验在 setter 中添加校验逻辑,保证数据合法性
灵活性内部实现可自由修改,不影响外部调用
可维护性降低代码耦合度,便于维护和扩展

1.4 常见面试题

Q1: 为什么属性要设为 private?

A:

  1. 安全性:防止外部直接修改属性值,避免非法数据
  2. 封装性:隐藏实现细节,只暴露必要的访问接口
  3. 可控性:可以在 getter/setter 中添加校验、日志、权限控制等逻辑

Q2: 什么是 JavaBean?有什么要求?

A: JavaBean 是符合特定规范的 Java 类:

  • 所有属性私有化(private
  • 提供无参构造器
  • 提供 public 的 getter/setter 方法
  • 实现 Serializable 接口(可选,用于序列化)

二、继承(Inheritance)

2.1 什么是继承

继承是指子类继承父类的属性和方法,并可以扩展新的功能。它是代码复用的重要手段,使用 extends 关键字实现。

        ┌───────────┐
        │   Animal  │  (父类/基类/超类)
        │  + name   │
        │  + eat()  │
        └─────┬─────┘
              │ extends
        ┌─────┴─────┐
   ┌────┴────┐ ┌────┴────┐
   │   Dog   │ │   Cat   │  (子类/派生类)
   │ + bark()│ │ + meow()│
   └─────────┘ └─────────┘

2.2 继承的特点

// 父类
public class Animal {
    protected String name;
    
    public void eat() {
        System.out.println(name + " 正在吃东西");
    }
}
 
// 子类继承父类
public class Dog extends Animal {
    
    public Dog(String name) {
        this.name = name;  // 继承自父类的属性
    }
    
    public void bark() {
        System.out.println(name + " 正在汪汪叫");
    }
    
    // 重写父类方法
    @Override
    public void eat() {
        System.out.println(name + " 正在啃骨头");
    }
}

2.3 继承的规则

规则说明
单继承一个类只能有一个直接父类(但可以多层继承)
构造器不继承子类不继承父类的构造器,必须调用 super()
私有成员不继承private 成员不能被子类访问
访问权限不能更严格重写方法时,访问权限不能比父类更严格

2.4 super 关键字

super 用于在子类中访问父类的成员:

public class Child extends Parent {
    private String name = "子类名称";
    
    public void show() {
        System.out.println(this.name);    // 子类的 name
        System.out.println(super.name);   // 父类的 name
        super.method();                    // 调用父类的方法
    }
    
    public Child() {
        super();  // 调用父类构造器(必须在第一行)
    }
}

2.5 方法重写(Override)

重写是子类重新定义父类的方法,需满足以下条件:

条件说明
方法签名相同方法名、参数列表必须完全一致
返回类型兼容返回类型相同或是父类方法返回类型的子类
访问权限放宽子类方法的访问权限不能更严格
不能重写 finalfinal 方法不能被重写
不能重写 staticstatic 方法属于类,不存在重写
public class Animal {
    public Animal create() {
        return new Animal();
    }
}
 
public class Dog extends Animal {
    // 协变返回类型:返回父类方法返回类型的子类
    @Override
    public Dog create() {
        return new Dog();
    }
}

2.6 常见面试题

Q1: Java 为什么是单继承?

A:

  • 避免歧义:如果多继承,两个父类有同名方法时,子类不知道调用哪个
  • 简化设计:单继承使类层次结构更清晰,降低复杂度
  • 解决方案:可以通过接口多实现来实现类似多继承的效果

Q2: 重写(Override)和重载(Overload)的区别?

A:

对比项重写(Override)重载(Overload)
发生位置父子类之间同一个类中
方法签名必须相同必须不同(参数列表)
返回类型相同或协变无关
访问权限不能更严格无关
多态性运行时多态编译时多态

Q3: 构造器能被重写吗?

A: 不能。构造器不属于普通方法,不参与继承,因此不能被重写。但子类可以通过 super() 调用父类构造器。


三、多态(Polymorphism)

3.1 什么是多态

多态是指同一个行为具有多个不同表现形式或形态的能力。在 Java 中,多态是同一个接口,使用不同的实例而执行不同操作。

        ┌───────────┐
        │  Animal   │
        │  + shout()│
        └─────┬─────┘

    ┌─────────┼─────────┐
    │         │         │
┌───┴───┐ ┌───┴───┐ ┌───┴───┐
│  Dog  │ │  Cat  │ │  Pig  │
│shout()│ │shout()│ │shout()│
│ 汪汪  │ │ 喵喵  │ │ 哼哼  │
└───────┘ └───────┘ └───────┘

Animal a = new Dog();  a.shout();  // 汪汪
Animal a = new Cat();  a.shout();  // 喵喵
Animal a = new Pig();  a.shout();  // 哼哼

3.2 多态的前提

  1. 继承关系:必须存在父子类关系
  2. 方法重写:子类必须重写父类的方法
  3. 向上转型:父类引用指向子类对象

3.3 多态的实现

// 父类
public abstract class Animal {
    public abstract void shout();
}
 
// 子类
public class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪!");
    }
}
 
public class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵!");
    }
}
 
// 多态使用
public class Test {
    public static void main(String[] args) {
        // 向上转型:父类引用指向子类对象
        Animal dog = new Dog();
        Animal cat = new Cat();
        
        // 同一个方法调用,不同的执行结果
        dog.shout();  // 汪汪汪!
        cat.shout();  // 喵喵喵!
        
        // 作为参数传递
        animalShout(new Dog());  // 汪汪汪!
        animalShout(new Cat());  // 喵喵喵!
    }
    
    // 多态的应用:参数类型使用父类
    public static void animalShout(Animal animal) {
        animal.shout();
    }
}

3.4 向上转型与向下转型

Animal animal = new Dog();  // 向上转型(自动)
 
// 向下转型(需要强转,可能抛出 ClassCastException)
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;  // 安全转型
    dog.shout();
}
转型类型说明安全性
向上转型子类 → 父类(自动)✅ 安全
向下转型父类 → 子类(强转)⚠️ 可能不安全

3.5 instanceof 关键字

instanceof 用于判断对象是否是特定类的实例:

Animal animal = new Dog();
 
System.out.println(animal instanceof Dog);     // true
System.out.println(animal instanceof Animal);  // true
System.out.println(animal instanceof Cat);     // false
 
// Java 14+ 模式匹配
if (animal instanceof Dog dog) {
    dog.shout();  // 自动转型
}

3.6 多态的应用场景

场景示例
方法参数void feed(Animal a) 可以接收任何动物子类
集合存储List<Animal> animals 可以存储各种动物
工厂模式根据类型返回不同的子类实例
策略模式不同策略实现同一接口

3.7 成员变量不具备多态性

重要:多态只针对方法,成员变量不具备多态性

public class Parent {
    public String name = "父类";
    public void show() {
        System.out.println("父类方法");
    }
}
 
public class Child extends Parent {
    public String name = "子类";  // 隐藏父类属性
    
    @Override
    public void show() {
        System.out.println("子类方法");
    }
}
 
public class Test {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.name);  // "父类" - 变量看编译类型
        p.show();                    // "子类方法" - 方法看运行类型
    }
}

3.8 常见面试题

Q1: 多态的底层原理是什么?

A: 多态通过**动态绑定(运行时绑定)**实现:

  1. 编译时:检查父类是否有该方法,确定方法签名
  2. 运行时:根据对象的实际类型,从方法表中找到真正要执行的方法

Q2: static 方法能体现多态吗?

A: 不能。static 方法属于类,不参与多态:

  • static 方法在编译时就确定了调用哪个方法(静态绑定)
  • 即使子类有同名 static 方法,也不是重写,而是隐藏
Parent p = new Child();
p.staticMethod();  // 调用 Parent 的静态方法(看编译类型)

Q3: private 方法能被重写吗?

A: 不能。private 方法对子类不可见,不存在重写。如果子类定义了同名方法,只是普通的新方法,不是重写。


四、三大特性关系

┌─────────────────────────────────────────────────────┐
│                   面向对象三大特性                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│   ┌──────────┐          ┌──────────┐               │
│   │   封装   │ ───────→ │   继承   │               │
│   │ (安全性) │   基础    │  (复用性) │               │
│   └──────────┘          └────┬─────┘               │
│                              │                      │
│                              ↓                      │
│                       ┌──────────┐                 │
│                       │   多态   │                 │
│                       │ (扩展性) │                 │
│                       └──────────┘                 │
│                                                     │
│  封装 → 继承 → 多态:层层递进,相辅相成              │
└─────────────────────────────────────────────────────┘
特性核心作用关键字/机制
封装隐藏细节,保护数据private、getter/setter
继承代码复用,建立关系extendssuper
多态统一接口,灵活扩展向上转型、重写

五、综合面试题

Q1: 谈谈你对面向对象三大特性的理解?

A:

特性理解
封装将数据和操作数据的方法绑定在一起,通过访问修饰符隐藏内部实现,只暴露必要接口。好处是安全性、可维护性、灵活性
继承子类继承父类的属性和方法,实现代码复用。好处是减少重复代码,建立类之间的层次关系。Java 是单继承
多态父类引用指向子类对象,同一方法调用有不同实现。好处是提高代码扩展性和灵活性,是开闭原则的基础

Q2: 以下代码输出什么?

public class A {
    public String name = "A";
    public void print() { System.out.println("A"); }
}
 
public class B extends A {
    public String name = "B";
    @Override
    public void print() { System.out.println("B"); }
}
 
public class Test {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.name);
        a.print();
    }
}

A:

A    // 成员变量不具备多态性,看编译类型
B    // 方法具有多态性,看运行类型

Q3: 如何设计一个不能被继承的类?

A:

// 方法1:使用 final 修饰类
public final class CannotBeExtended {
    // ...
}
 
// 方法2:私有化构造器 + 静态工厂(可以防止继承,但不能实例化)
public class CannotBeExtended {
    private CannotBeExtended() {}
    
    public static CannotBeExtended create() {
        return new CannotBeExtended();
    }
}

六、最佳实践

6.1 封装原则

  • 所有属性设为 private(除非有特殊原因)
  • 提供 public 的 getter/setter(按需提供)
  • 在 setter 中添加数据校验逻辑

6.2 继承原则

  • 遵循 IS-A 关系(Dog IS-A Animal ✅)
  • 优先使用组合而非继承(Has-A 优先于 Is-A)
  • 父类设计要考虑扩展点(模板方法模式)

6.3 多态原则

  • 面向接口编程,而非面向实现编程
  • 多用向上转型,少用向下转型
  • 合理使用 instanceof 进行类型判断

七、抽象类与接口

7.1 抽象类(Abstract Class)

什么是抽象类

抽象类是用 abstract 修饰的类,不能被实例化,主要用于被继承。它可以包含抽象方法和具体方法。

// 抽象类
public abstract class Animal {
    protected String name;
    
    // 具体方法(有实现)
    public void sleep() {
        System.out.println(name + " 正在睡觉");
    }
    
    // 抽象方法(无实现,子类必须重写)
    public abstract void makeSound();
    
    // 构造方法(供子类调用)
    public Animal(String name) {
        this.name = name;
    }
}
 
// 子类必须实现所有抽象方法
public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + " 汪汪叫");
    }
}

抽象类的特点

特点说明
不能实例化new Animal() ❌ 编译错误
可以有构造器供子类调用 super()
可以有抽象方法子类必须实现
可以有具体方法子类可以直接继承使用
可以有成员变量包括普通变量和常量
单继承一个类只能继承一个抽象类

抽象方法

// 抽象方法:只有声明,没有实现
public abstract void draw();
 
// 规则:
// 1. 抽象方法必须在抽象类中
// 2. 抽象方法不能是 private(子类无法重写)
// 3. 抽象方法不能是 static(属于实例)
// 4. 抽象方法不能是 final(需要被重写)

7.2 接口(Interface)

什么是接口

接口是一种完全抽象的类型,定义了一组行为规范。JDK 8 之前,接口只能包含常量和抽象方法。

// 接口定义
public interface Flyable {
    // 常量(默认 public static final)
    int MAX_HEIGHT = 10000;
    
    // 抽象方法(默认 public abstract)
    void fly();
}
 
// 实现接口
public class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟儿飞翔,最高 " + MAX_HEIGHT + " 米");
    }
}
 
// 多实现
public class Superman implements Flyable, Runnable {
    @Override
    public void fly() {
        System.out.println("超人飞行");
    }
    
    @Override
    public void run() {
        System.out.println("超人奔跑");
    }
}

JDK 8 新特性:默认方法与静态方法

public interface MyInterface {
    // 抽象方法
    void abstractMethod();
    
    // 默认方法(JDK 8+):有实现,子类可选择重写
    default void defaultMethod() {
        System.out.println("接口默认方法");
    }
    
    // 静态方法(JDK 8+):属于接口,只能通过接口名调用
    static void staticMethod() {
        System.out.println("接口静态方法");
    }
}
 
// 使用
public class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println("实现抽象方法");
    }
    
    // defaultMethod() 可以不重写,直接使用
}
 
// 调用
MyClass obj = new MyClass();
obj.abstractMethod();   // 实现抽象方法
obj.defaultMethod();    // 接口默认方法(可重写)
MyInterface.staticMethod();  // 接口静态方法

JDK 9 新特性:私有方法

public interface MyInterface {
    default void method1() {
        commonLogic();  // 调用私有方法
        System.out.println("方法1");
    }
    
    default void method2() {
        commonLogic();  // 复用私有方法
        System.out.println("方法2");
    }
    
    // 私有方法(JDK 9+):供默认方法复用
    private void commonLogic() {
        System.out.println("公共逻辑");
    }
    
    // 私有静态方法
    private static void staticCommonLogic() {
        System.out.println("静态公共逻辑");
    }
}

接口的特点

特点说明
不能实例化new Flyable() ❌ 编译错误
没有构造器接口没有构造方法
多实现一个类可以实现多个接口
多继承接口可以继承多个接口
方法默认 public接口方法默认是 public abstract
变量默认常量变量默认是 public static final

7.3 抽象类 vs 接口

对比项抽象类接口
关键字abstract classinterface
继承单继承(extends多实现(implements
构造器✅ 可以有❌ 不能有
成员变量任意类型只能是 public static final
抽象方法✅ 可以有✅ 默认都是
具体方法✅ 可以有✅ JDK 8+ 默认方法
静态方法✅ 可以有✅ JDK 8+ 支持
私有方法✅ 可以有✅ JDK 9+ 支持
设计目的代码复用,模板设计定义行为规范,解耦

7.4 使用场景

什么时候用抽象类?

  • 需要共享代码:多个子类有共同实现
  • 需要成员变量:保存状态
  • 需要非 public 成员:控制访问
  • IS-A 关系强:如 Dog IS-A Animal
// 抽象类:模板方法模式
public abstract class AbstractProcessor {
    // 模板方法:定义算法骨架
    public final void process() {
        validate();      // 公共逻辑
        doProcess();     // 子类实现
        log();           // 公共逻辑
    }
    
    private void validate() {
        System.out.println("参数校验");
    }
    
    // 抽象方法:子类实现
    protected abstract void doProcess();
    
    private void log() {
        System.out.println("记录日志");
    }
}

什么时候用接口?

  • 需要多实现:一个类需要多种能力
  • 定义行为规范:不关心具体实现
  • 解耦合:面向接口编程
  • 标记接口:如 SerializableCloneable
// 接口:能力/行为定义
public interface Flyable { void fly(); }
public interface Swimmable { void swim(); }
public interface Runnable { void run(); }
 
// 一个类可以有多种能力
public class Duck implements Flyable, Swimmable, Runnable {
    @Override
    public void fly() { System.out.println("鸭子飞"); }
    
    @Override
    public void swim() { System.out.println("鸭子游泳"); }
    
    @Override
    public void run() { System.out.println("鸭子跑"); }
}

7.5 接口继承接口

public interface A {
    void methodA();
}
 
public interface B {
    void methodB();
}
 
// 接口多继承
public interface C extends A, B {
    void methodC();
}
 
// 实现 C 需要实现所有方法
public class Impl implements C {
    @Override
    public void methodA() { }
    
    @Override
    public void methodB() { }
    
    @Override
    public void methodC() { }
}

7.6 默认方法冲突解决

当一个类实现多个接口,且这些接口有同名的默认方法时:

public interface A {
    default void show() {
        System.out.println("A");
    }
}
 
public interface B {
    default void show() {
        System.out.println("B");
    }
}
 
// 解决方案1:必须重写
public class C implements A, B {
    @Override
    public void show() {
        A.super.show();  // 调用 A 的默认方法
        // 或 B.super.show();
        // 或自己实现
    }
}
 
// 解决方案2:继承优先于接口
public class D extends BaseClass implements A {
    // 如果 BaseClass 有 show(),则优先使用
    // 如果没有,则使用 A 的默认方法
}

7.7 常见面试题

Q1: 抽象类可以没有抽象方法吗?

A: 可以。抽象类不一定必须有抽象方法,但有抽象方法的类必须是抽象类。

// 合法:抽象类没有抽象方法
public abstract class MyClass {
    public void normalMethod() {
        System.out.println("普通方法");
    }
}

Q2: 接口能继承抽象类吗?

A: 不能。接口只能继承接口,不能继承类(包括抽象类)。

Q3: 以下代码是否正确?

public abstract class Test {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

A: 正确。抽象类可以有 main 方法,因为 main 是静态方法,不依赖实例。

Q4: 抽象类和接口的设计理念有什么不同?

A:

设计理念抽象类接口
本质是什么(IS-A)能做什么(CAN-DO)
关注点代码复用行为规范
耦合度紧耦合(继承关系)松耦合(实现关系)
扩展性单继承限制多实现灵活

Q5: 如何实现多继承的效果?

A: Java 通过接口多实现来实现类似多继承的效果:

// 一个类可以同时拥有多种能力
public class Superman implements Flyable, Swimmable, Runnable {
    // 同时实现多个接口
}

八、总结

特性一句话总结面试关键词
封装藏起来,留接口private、getter/setter、JavaBean
继承拿过来,加功能extends、super、重写、单继承
多态同接口,不同实现向上转型、动态绑定、instanceof
抽象类半成品模板abstract、不能实例化、单继承
接口行为契约规范interface、多实现、默认方法

最后更新:2026年3月2日