跳到内容

第 1 卷 / 第 7 章 / 第 1 课

7.1 从平行数组到对象 ​

前置知识:变量、数组和方法

预计时间:25 分钟

完成标志:能区分类、对象、字段和引用变量,并能画出对象别名关系

一个图书管理程序要保存书名、作者、ISBN、价格和借阅状态。以下书名为练习数据,ISBN 使用占位文本,尚未校验正式格式。若每个字段各用一个数组,代码会变成:

java
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. 类定义一种类型,对象保存一份实例状态 ​

java
class Book {
    String title;
    String author;
    String isbn;
    double price;
    boolean borrowed;
}

Book 是类。它声明每个 Book 对象有哪些字段。对象则是在程序运行时创建的具体实例,每个对象有自己的一组实例字段。

java
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 后按前几章的方式编译运行,输出:

text
Java 学习笔记
计算机系统笔记

一个源文件最多有一个 public 顶级类,文件名要与该类名一致。这里 Library 是 public,Book 使用包级可见性,因此可以同处 Library.java。

2. new Book() 的可观察结果 ​

执行 new Book() 时,Java 创建一个 Book 对象,先把实例字段设为默认值,再执行字段初始化器、初始化块和选中的构造方法。当前类没有显式构造方法,编译器会提供默认无参构造,构造方法一讲会拆解这条规则。

java
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. 赋值复制引用值,不复制对象 ​

java
Book first = new Book();
first.title = "Java 学习笔记";

Book alias = first;
alias.title = "新版 Java 学习笔记";

System.out.println(first.title);
// 新版 Java 学习笔记

此时只有一个 Book 对象,first 与 alias 都保存指向它的引用值。两个变量称为别名。画图时把“变量”和“对象”分开:

text
first ──┐
        ├──> Book 对象 { title = "新版 Java 学习笔记" }
alias ──┘

若再次执行 alias = new Book(),只会让 alias 改指向新对象,first 仍指向旧对象。

first == alias 比较两个引用值是否指向同一个对象,不比较字段内容。按内容比较需要类定义 equals 契约,留到后续对象方法章节。

4. null 表示当前没有对象引用 ​

java
Book selected = null;
// System.out.println(selected.title); // NullPointerException

访问 null 引用的实例字段或实例方法会失败。设计 API 时应明确 null 是否允许;若“不存在”是正常业务状态,后续还可学习空集合、Optional 或专门状态对象。

把一个变量设为 null 只断开这一条引用。对象只有在不再可达时才有资格被垃圾回收,何时回收没有保证,也不能用 null 当作手动释放命令。

5. 类把相关数据重新放回一个边界 ​

平行数组按“所有书名、所有作者”组织数据;Book[] 则按“每一本书”组织:

java
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 也只断开一条引用,不等于立即回收对象。

→ 下一步:字段、方法与 this

规范入口:JLS §15.9:类实例创建表达式。

Built with VitePress | Software Systems Atlas