Java 类与对象
类作为模板、对象作为实例:字段与方法如何归属,new 创建了什么,引用如何指向堆上对象。
#concept
#growing
#dev
#java
[!info] related notes
Java 类与对象
这个概念为什么出现
Java 用类描述一组状态与行为,用对象承载运行时实例。几乎所有领域模型(订单、用户、连接)都落在“对象 + 引用”上;不懂创建与引用共享,就无法理解可变状态、别名与 NPE。
[!abstract] 一句话理解 类是类型与模板;
new创建实例对象;变量持有引用(或 null),通过引用读写实例字段并调用实例方法。
最小可运行示例
场景:订单服务创建一笔订单并让两处引用观察同一状态
本例在同一线程里更新并读取订单状态,展示共享引用;跨线程观察最新状态还需要同步,不能只靠共享引用。
public class Order {
String id;
String status;
void markPaid() {
// 业务动作:状态迁移到 PAID
this.status = "PAID";
}
public static void main(String[] args) {
Order o1 = new Order(); // 在堆上创建对象,o1 持有引用
o1.id = "ORD-1";
o1.status = "CREATED";
Order o2 = o1; // 引用拷贝:两个变量指向同一对象
o2.markPaid();
System.out.println(o1.status); // PAID,因为共享实例
System.out.println(o1 == o2); // true,同一引用目标
}
}
建议运行:
javac Order.java && java Order
期望输出:
PAID
true
结合场景再看三个关注点
new之后才有实例状态;类本身不是那一笔订单。- 赋值复制的是引用,不是对象深拷贝。
- 领域行为优先做成方法(
markPaid),而不是到处改字段。
核心概念与准确模型
1. 类的成员
- 字段(状态)
- 方法(行为)
- 嵌套类型也是成员;构造器和初始化块可在类体中声明,但按 JLS 定义不属于类成员。
2. 对象创建过程(概念模型)
- 分配对象内存
- 默认值填充字段
- 执行构造逻辑
- 构造正常完成后,new 表达式返回引用;构造抛异常时不会正常返回,但构造期间主动泄漏的引用仍可能被外部持有。
这是可观察语义模型;JIT 可能通过逃逸分析、标量替换等消除实际对象分配,不应把每次 new 都当作必然的物理堆分配。
3. 引用
- 变量类型决定编译期可见成员
- 运行时对象有具体类
null表示“没有对象”
4. 实例方法调用
引用.方法(参数)
实际执行哪个实现,在存在继承/覆盖时由运行时类型决定(多态篇)。
边界情况与反直觉行为
- 使用实例成员需要有效对象引用;它可来自参数、工厂或已有对象,不要求当前代码亲自 new。null 解引用会 NPE。
- 多个引用共享可变对象 → 别名副作用。
- 类可以没有显式字段,只作行为命名空间(常配合 static,需克制)。
常见误区
[!warning] 常见误区:把类当全局可变单例袋 全是 static 字段的“上帝类”会失去对象模型的可测试性与隔离性。
工程实践
- 保持对象不变量:非法状态不要被构造出来。
- 不可变设计还要控制可变引用内容与暴露方式;final 字段加无 setter 并不自动保证对象深层不可变。
- 避免暴露内部可变集合引用。
- 一个类一个主要职责,订单别同时管邮件发送与报表。
本节总结
- 类模板 / 对象实例 / 引用访问
- 共享引用是协作也是风险
- 为构造器、继承、相等性打地基
自测题
Order a = b;拷贝了什么?- 为什么领域方法比公开可变字段更稳?
参考答案
- 拷贝引用,不拷贝对象。
- 方法可维护不变量与状态机,字段暴露会让外部绕过规则。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JLS Classes | 规范 | 类声明 |
| OOP Concepts (Oracle Tutorial) | 官方教程 | 对象概念 |