跳到正文
Elaine Blog
返回

类型系统、值传递与对象引用

Java 基础

Java 永远是值传递(Pass by Value):调用方法时,参数变量中的值会被复制。对象参数复制的不是整个对象, 而是“指向对象的引用值”。这句话是理解 Java 参数行为的钥匙。

Table of contents

Open Table of contents

运行三组对照实验

把下面内容保存为 值传递实验.java,然后运行 javac --release 21 值传递实验.java && java 值传递实验

public class 值传递实验 {
    record 用户(String name) {}

    static void 修改数字(int number) { number = 99; }
    static void 重新赋值(用户 user) { user = new 用户("新用户"); }
    static void 修改列表(java.util.List<String> names) { names.add("新增项"); }

    public static void main(String[] args) {
        int number = 1;
        修改数字(number);
        System.out.println(number);                 // 1

        用户 user = new 用户("原用户");
        重新赋值(user);
        System.out.println(user.name());            // 原用户

        var names = new java.util.ArrayList<>(java.util.List.of("原项"));
        修改列表(names);
        System.out.println(names);                  // [原项, 新增项]
    }
}

前三个结果并不矛盾:数字值被复制;对象引用值也被复制;两个列表引用起初指向同一个可变对象,所以通过任一引用都能 修改那个对象。方法内把参数重新指向新对象,不会改变调用者变量。

基本类型、引用类型与包装类型

intlongdoubleboolean 等是基本类型,变量直接保存数值。类、数组、接口和枚举是引用类型, 变量保存可用于定位对象的引用;引用也可以是 null

Integerint包装类型。自动装箱会在需要对象时把 int 转成 Integer,拆箱则反向转换:

Integer boxed = 42;       // 自动装箱
int value = boxed;        // 自动拆箱
Integer missing = null;
// int broken = missing;  // 运行时抛出 NullPointerException

不要用 == 比较包装对象的数值。某些小整数会命中缓存,让错误代码偶尔“看起来正确”。使用 Objects.equals(a, b),或在确认非空后拆箱比较。

类型系统解决什么问题

静态类型系统在编译期检查“这个值能否用于这里”。它能阻止把字符串直接交给整数参数,却不能自动保证字符串 不是空白、金额大于零或订单状态合法。这些属于业务不变量,需要构造器校验、值对象和测试共同保证。

record 金额(java.math.BigDecimal value) {
    金额 {
        if (value == null || value.signum() < 0) {
            throw new IllegalArgumentException("金额不能为负数");
        }
    }
}

实践规则

下一步

阅读面向对象不是堆类,把类型用于表达职责和业务规则。


分享这篇文章:

上一篇
第一个可测试的 Java 项目
下一篇
面向对象不是堆类:封装、组合与多态