7.1 从平行数组到对象
前置知识:变量、数组和方法
预计时间:25 分钟
完成标志:能区分类、对象、字段和引用变量,并能画出对象别名关系
一个图书管理程序要保存书名、作者、ISBN、价格和借阅状态。以下书名为练习数据,ISBN 使用占位文本,尚未校验正式格式。若每个字段各用一个数组,代码会变成:
String[] titles = new String[1000];
String[] authors = new String[1000];
String[] isbns = new String[1000];
double[] prices = new double[1000];
boolean[] borrowed = new boolean[1000];第 17 个位置必须在五个数组里始终代表同一本书。新增、删除或按书名排序时,只要漏改一个数组,数据就错位。
1. 类定义一种类型,对象保存一份实例状态
class Book {
String title;
String author;
String isbn;
double price;
boolean borrowed;
}Book 是类。它声明每个 Book 对象有哪些字段。对象则是在程序运行时创建的具体实例,每个对象有自己的一组实例字段。
public class Library {
public static void main(String[] args) {
Book first = new Book();
first.title = "Java 学习笔记";
first.author = "Lin";
first.isbn = "ISBN-DEMO-001";
first.price = 99.0;
Book second = new Book();
second.title = "计算机系统笔记";
System.out.println(first.title);
System.out.println(second.title);
}
}把两个类保存为 Library.java 后按前几章的方式编译运行,输出:
Java 学习笔记
计算机系统笔记一个源文件最多有一个 public 顶级类,文件名要与该类名一致。这里 Library 是 public,Book 使用包级可见性,因此可以同处 Library.java。
2. new Book() 的可观察结果
执行 new Book() 时,Java 创建一个 Book 对象,先把实例字段设为默认值,再执行字段初始化器、初始化块和选中的构造方法。当前类没有显式构造方法,编译器会提供默认无参构造,构造方法一讲会拆解这条规则。
Book book = new Book();
System.out.println(book.title); // null
System.out.println(book.price); // 0.0
System.out.println(book.borrowed); // false语言规范并不要求你把局部引用变量描述成“必在栈上”,也不应把引用值说成可以操作的原始内存地址。可靠的源代码模型是:
book是Book类型的变量;- 它可以保存某个
Book对象的引用值,也可以保存null; - 通过非 null 引用可访问对象的实例成员。
JVM 实现会使用栈帧、堆和优化技术,但逃逸分析等优化可能改变物理放置。对象语义不依赖你猜测具体地址。
3. 赋值复制引用值,不复制对象
Book first = new Book();
first.title = "Java 学习笔记";
Book alias = first;
alias.title = "新版 Java 学习笔记";
System.out.println(first.title);
// 新版 Java 学习笔记此时只有一个 Book 对象,first 与 alias 都保存指向它的引用值。两个变量称为别名。画图时把“变量”和“对象”分开:
first ──┐
├──> Book 对象 { title = "新版 Java 学习笔记" }
alias ──┘若再次执行 alias = new Book(),只会让 alias 改指向新对象,first 仍指向旧对象。
first == alias 比较两个引用值是否指向同一个对象,不比较字段内容。按内容比较需要类定义 equals 契约,留到后续对象方法章节。
4. null 表示当前没有对象引用
Book selected = null;
// System.out.println(selected.title); // NullPointerException访问 null 引用的实例字段或实例方法会失败。设计 API 时应明确 null 是否允许;若“不存在”是正常业务状态,后续还可学习空集合、Optional 或专门状态对象。
把一个变量设为 null 只断开这一条引用。对象只有在不再可达时才有资格被垃圾回收,何时回收没有保证,也不能用 null 当作手动释放命令。
5. 类把相关数据重新放回一个边界
平行数组按“所有书名、所有作者”组织数据;Book[] 则按“每一本书”组织:
Book[] shelf = new Book[2];
shelf[0] = first;
shelf[1] = new Book();注意,new Book[2] 只创建两个引用组件,默认都是 null,不会自动创建两本书。
类不只负责把字段装进同一个对象,还能把借阅、归还和校验规则放在字段旁边。下一篇开始给 Book 加行为。
专业速查
| 术语 | 精确定义 |
|---|---|
| 类 | 声明类型成员和对象行为的程序结构 |
| 对象 | 类实例或数组;本页主要讨论 Book 类实例 |
| 实例字段 | 每个对象各自拥有的状态 |
| 引用变量 | 保存对象引用值或 null 的变量 |
| 别名 | 多个引用值指向同一对象 |
| 身份比较 | a == b 检查是否同一对象 |
离开这一页前
把五条平行数组改画成两个 Book 对象和一个 Book[],标出数组组件初始为 null。然后分别画出 Book book、new Book()、alias = book 与 alias = new Book();变量、引用值和对象不要混成一个方框。把变量设为 null 也只断开一条引用,不等于立即回收对象。
规范入口:JLS §15.9:类实例创建表达式。