本章讲解 Java 面向对象核心:类与对象、封装、继承、多态、抽象类与接口。Spring 里 @Service、@RestController 本质都是类的实例与接口实现。
1. 类与对象
概念
- 类(Class):对象的蓝图,定义字段(状态)与方法(行为)。
- 对象(Object / Instance):
new出来的具体实体。 - 字段(Field)、方法(Method)、构造器(Constructor)。
原理
类加载后,Metaspace 存类元数据;new ClassName() 在堆上分配对象,构造器完成初始化。
完整示例
package com.example.demo;
public class User {
private Long id;
private String username;
public User(Long id, String username) {
this.id = id;
this.username = username;
}
public String displayName() {
return username + " (#" + id + ")";
}
public Long getId() { return id; }
public String getUsername() { return username; }
public static void main(String[] args) {
User u = new User(1L, "alice");
System.out.println(u.displayName());
}
}
练习
定义 Order:字段 id、amount、status;方法 isPaid() 返回 status 是否为 "PAID"。
完整例子
package com.example.demo;
public class Order {
private Long id;
private double amount;
private String status;
public Order(Long id, double amount, String status) {
this.id = id;
this.amount = amount;
this.status = status;
}
public boolean isPaid() {
return "PAID".equals(status);
}
public static void main(String[] args) {
Order o = new Order(1L, 99.0, "PAID");
System.out.println(o.isPaid()); // true
}
}
常见坑
- 忘记写构造器时,编译器生成无参 public 构造器;一旦自定义构造器,默认无参不再自动提供。
- 字段未初始化:基本类型默认 0/false,引用类型默认
null。
2. 封装(Encapsulation)
概念
隐藏内部状态,通过 getter/setter 或行为方法 controlled 访问。private 字段 + 公开方法。
原理
限制外部直接改字段,便于校验、审计、兼容后续重构(Spring Entity 常见 private 字段 + getter/setter 或 Lombok @Data)。
完整示例
package com.example.demo;
public class BankAccount {
private final String accountNo;
private double balance;
public BankAccount(String accountNo, double initial) {
if (initial < 0) throw new IllegalArgumentException("initial < 0");
this.accountNo = accountNo;
this.balance = initial;
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("amount <= 0");
balance += amount;
}
public boolean withdraw(double amount) {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
return true;
}
public double getBalance() { return balance; }
}
练习
给 Order 加 pay():仅 status 为 CREATED 时可变为 PAID,非法状态抛 IllegalStateException。
完整例子
package com.example.demo;
public class Order {
private Long id;
private double amount;
private String status;
public Order(Long id, double amount, String status) {
this.id = id;
this.amount = amount;
this.status = status;
}
public void pay() {
if (!"CREATED".equals(status)) {
throw new IllegalStateException("cannot pay when status=" + status);
}
status = "PAID";
}
public String getStatus() { return status; }
public static void main(String[] args) {
Order o = new Order(1L, 99.0, "CREATED");
o.pay();
System.out.println(o.getStatus()); // PAID
// o.pay(); // 再调会抛 IllegalStateException
}
}
常见坑
- public 字段导致任何人可改,破坏不变量。
- setter 里不做校验,负数金额等脏数据进入系统。
3. 继承(Inheritance)
概念
子类 extends 父类,获得非 private 成员,可**重写(Override)**方法。Java 单继承(一个类只能 extends 一个类)。
原理
子类对象内存布局包含父类部分;动态分派在运行时按实际对象类型调用方法(多态基础)。
完整示例
package com.example.demo;
public class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public String speak() { return "..."; }
}
public class Dog extends Animal {
public Dog(String name) { super(name); } // 必须先调 super(...)
@Override
public String speak() { return name + ": 汪汪"; }
}
练习
Employee 父类字段 name、baseSalary;Manager 子类加 bonus,重写 totalPay()。
完整例子
package com.example.demo;
public class Employee {
protected String name;
protected double baseSalary;
public Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
}
public double totalPay() {
return baseSalary;
}
}
class Manager extends Employee {
private double bonus;
public Manager(String name, double baseSalary, double bonus) {
super(name, baseSalary);
this.bonus = bonus;
}
@Override
public double totalPay() {
return baseSalary + bonus;
}
public static void main(String[] args) {
Employee e = new Manager("bob", 10000, 3000);
System.out.println(e.totalPay()); // 13000(多态)
}
}
常见坑
- 子类构造器第一行必须是
this(...)或super(...)。 - 误用继承表达「has-a」关系 — 应优先组合(见后文)。
4. 多态(Polymorphism)
概念
编译类型(引用声明的类型)与运行类型(实际对象类型)可不同。同一父类/接口引用,调用被重写的方法时,执行子类实现。
原理(直觉)
Payment p = new AlipayPayment();
p.pay(100); // 运行时找 AlipayPayment.pay,而非 Payment.pay
JVM 虚方法表(vtable) 在运行时解析具体实现。设计意义:依赖抽象(接口/父类),便于扩展与测试(Spring 注入接口)。
完整示例
package com.example.demo;
interface Payment {
String channel();
void pay(double amount);
}
class AlipayPayment implements Payment {
public String channel() { return "ALIPAY"; }
public void pay(double amount) { System.out.println("支付宝: " + amount); }
}
class WechatPayment implements Payment {
public String channel() { return "WECHAT"; }
public void pay(double amount) { System.out.println("微信: " + amount); }
}
public class PolymorphismDemo {
static void checkout(Payment payment, double amount) {
System.out.println("渠道=" + payment.channel());
payment.pay(amount);
}
public static void main(String[] args) {
Payment p1 = new AlipayPayment();
Payment p2 = new WechatPayment();
checkout(p1, 99.0);
checkout(p2, 99.0);
}
}
练习
用户角色:定义 Role 接口 permissions();Admin、Guest 实现;User 持有 Role,方法 canDelete() 根据角色返回 boolean。详见 exercises.md。
要点:接口 Role 提供 Set<String> permissions() 或 boolean can(String action);AdminRole / GuestRole 权限不同;User 委托给 role,不要写死 if (isAdmin);main 里用多态 Role role = new AdminRole()。
完整例子
package com.example.demo.exercise;
import java.util.Set;
interface Role {
Set<String> permissions();
default boolean can(String action) {
return permissions().contains(action);
}
}
class AdminRole implements Role {
public Set<String> permissions() {
return Set.of("READ", "WRITE", "DELETE");
}
}
class GuestRole implements Role {
public Set<String> permissions() {
return Set.of("READ");
}
}
public class User {
private final String name;
private final Role role;
public User(String name, Role role) {
this.name = name;
this.role = role;
}
public boolean canDelete() { return role.can("DELETE"); }
public boolean canRead() { return role.can("READ"); }
public static void main(String[] args) {
User admin = new User("alice", new AdminRole());
User guest = new User("bob", new GuestRole());
System.out.println(admin.canDelete()); // true
System.out.println(guest.canDelete()); // false
}
}
常见坑
- 多态只针对实例方法;
static、final、private方法不参与重写意义上的多态。 - 强转前用
instanceof:((Dog) animal).fetch()错类型会ClassCastException。
5. 抽象类(Abstract Class)
概念
abstract class 可含抽象方法(无方法体)与具体方法;不能 new,须子类实现抽象方法。
原理
介于「纯接口」与「普通类」之间:共享代码 + 强制子类实现差异部分。模板方法模式常用抽象类。
完整示例
package com.example.demo;
abstract class Notification {
protected String to;
Notification(String to) { this.to = to; }
public final void send(String content) { // 模板方法
String body = render(content);
doSend(body);
}
protected abstract String render(String content);
protected abstract void doSend(String body);
}
class EmailNotification extends Notification {
EmailNotification(String to) { super(to); }
protected String render(String content) { return "[Email to " + to + "] " + content; }
protected void doSend(String body) { System.out.println(body); }
}
练习
抽象类 BaseExporter 定义 export(List<String> lines) 模板:校验 → format 抽象 → 写文件抽象。
完整例子
package com.example.demo;
import java.util.List;
abstract class BaseExporter {
public final void export(List<String> lines) {
if (lines == null || lines.isEmpty()) {
throw new IllegalArgumentException("lines empty");
}
String body = format(lines);
write(body);
}
protected abstract String format(List<String> lines);
protected abstract void write(String body);
}
class ConsoleCsvExporter extends BaseExporter {
protected String format(List<String> lines) {
return String.join(",", lines);
}
protected void write(String body) {
System.out.println(body);
}
public static void main(String[] args) {
new ConsoleCsvExporter().export(List.of("a", "b", "c"));
}
}
常见坑
- 抽象类可有构造器,供子类
super()调用。 - 与接口同时出现时,类
extends只能一个,但可implements多个接口。
6. 接口(Interface)
概念
Java 8+ 接口可有 default、static 方法;接口是能力契约,类 implements 可多实现。
原理
面向接口编程解耦实现。Spring 中大量 @Autowired SomeService,注入的是接口或具体类的代理。
完整示例
package com.example.demo;
interface Repository<T> {
T findById(Long id);
void save(T entity);
}
class InMemoryUserRepository implements Repository<User> {
// 简化:Map 存储
public User findById(Long id) { return null; }
public void save(User entity) { }
}
// default 方法示例
interface Auditable {
default void audit(String action) {
System.out.println("[AUDIT] " + action);
}
}
练习
支付接口:Payment 含 pay(Money)、refund(Money);实现 MockPayment 打日志即可。见 exercises.md。
要点:接口 Payment 含 channel()、pay(double)、refund(double)(金额可用 double,日后可换 BigDecimal);编写 checkout(Payment p, double amount) 体现多态;新增渠道只加实现类。
完整例子
package com.example.demo.exercise;
interface Payment {
String channel();
void pay(double amount);
void refund(double amount);
}
class MockPayment implements Payment {
public String channel() { return "MOCK"; }
public void pay(double amount) {
System.out.println("[MOCK] pay " + amount);
}
public void refund(double amount) {
System.out.println("[MOCK] refund " + amount);
}
}
public class PaymentDemo {
static void checkout(Payment p, double amount) {
System.out.println("channel=" + p.channel());
p.pay(amount);
}
public static void main(String[] args) {
checkout(new MockPayment(), 99.0);
}
}
常见坑
- 接口字段默认
public static final。 default方法菱形继承冲突时需子类 override 解决。
7. this 与 super
概念
this:当前实例;区分字段与参数同名;构造器链this(...)。super:父类部分;调父类构造super(...);调被覆盖的父类方法super.method()。
完整示例
package com.example.demo;
public class Point {
private int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(int xy) {
this(xy, xy); // 委托其他构造器
}
}
class LabelPoint extends Point {
private String label;
LabelPoint(int x, int y, String label) {
super(x, y);
this.label = label;
}
}
常见坑
this()/super()必须放在构造器首行,二者不能同构造器并存。- 在 static 方法里不能用
this(无当前实例)。
8. 访问修饰符(Access Modifiers)
概念
| 修饰符 | 同类 | 同包 | 子类 | 任意 |
|---|---|---|---|---|
private | ✓ | |||
| (包私有) | ✓ | ✓ | ||
protected | ✓ | ✓ | ✓ | |
public | ✓ | ✓ | ✓ | ✓ |
原理
封装边界 + 包内协作。Spring 组件通常 public 类;字段倾向 private。
完整示例
package com.example.demo;
public class AccessDemo {
private void internal() {}
void packagePrivate() {}
protected void forSubclass() {}
public void api() {}
}
练习
将 BankAccount.balance 保持 private,同包测试类只能通过 deposit/withdraw 改余额。
完整例子
package com.example.demo;
// BankAccount.java — balance 必须 private(见上文完整示例)
public class BankAccountAccessTest {
public static void main(String[] args) {
BankAccount acc = new BankAccount("A001", 100);
acc.deposit(50);
// acc.balance = 999; // 编译错误:private
System.out.println(acc.getBalance()); // 150
System.out.println(acc.withdraw(200)); // false,余额不变
System.out.println(acc.getBalance()); // 150
}
}
常见坑
protected对不同包子类可见,但对 random 外部类不可见。- 误以为「getter public = 封装完美」— 若返回可变对象引用(如
List),仍可能外泄(应返回副本或不可变视图,见 04 章)。
9. 组合优于继承
概念
「有一个」用组合(持有其他类引用),「是一个」才考虑继承。
完整示例
package com.example.demo;
class Engine {
void start() { System.out.println("engine on"); }
}
class Car {
private final Engine engine = new Engine(); // 组合
void start() { engine.start(); }
}
常见坑
- 深层继承链难维护;优先接口 + 组合(Spring 里也常见 Delegate 模式)。
本章小结
| 主题 | 一句话 |
|---|---|
| 封装 | private 状态 + 公开行为 |
| 继承 | extends,单继承,super |
| 多态 | 接口/父类引用,运行时子类方法 |
| 抽象类 | 共享实现 + 抽象步骤 |
| 接口 | 契约,多实现,default/static |