Java不可变类作为参数传递遇到的坑

不可变类在Java中如String和BigInteger等,因其线程安全和设计优势而广泛应用。不可变对象在生命周期内保持不变,需具备final成员、无修改方法、私有域和可能的final类特性。然而,创建大量对象可能导致性能问题,建议提供可变配套类。BigDecimal作为不可变类,其方法不会直接修改原有对象,需通过赋值接收计算结果。在使用时需注意此行为以避免预期外的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、不可变类

不可变类在对象的整个生命周期(lifetime)内保持不变。Java平台类库中包含许多不可变的类,其中有String、基本类型的包装类、BigInteger、BigDecimal。

存在不可变的类有许多理由:不可变的类比可变的类更加容易设计,实现和使用。它们不容易出错,而且更加安全。

不可变类有以下特点:

  • 类中所有的成员变量被final修饰
  • 类中没有修改成员变量的方法,例如setXXX,可以提供一个带参的构造函数来初始化这些成员变量
  • 确保类中的方法不会被重写.可以将类或者类中的方法定义为final的来实现
  • 使所有的域都成为私有的private 。

不可变对象本质上是线程安全的,它们不要求同步。当多个线程并发访问这样的对象时,它们不会遭到破坏,所以,不可变对象可以被自由的共享。

不可变类的真正唯一的缺点是,对于每个不同的值都需要一个单独的对象。创建这种对象的代价可能很高,特别是对于大型对象的情形。所以,最好的办法是提供一个公有的可变配套类。

例如使用JDK9中的List.of创建出的list是不可变的,不能使用list.add方法,否则会报错。

二、使用BigDecimal作为参数传递

举个简单例子当时我的使用场景,将BigDecimal作为参数传递给方法,结果方法执行完之后值没变。

    public static void main(String[] args) {
        BigDecimal bigDecimal = new BigDecimal("200");
        add1(bigDecimal);
        System.out.println(bigDecimal);
    }

    public static void add1(BigDecimal bigDecimal) {
        bigDecimal.add(new BigDecimal("200"));
    }

点开add方法发现源码return只是返回了计算结果,而没有把计算结果赋值给调用者!
这就好比 t.add(y) ,只是执行了 t+y 而没有把计算结果返回给 t。

因为BigDecimal是不可变类,所以我们在使用BigDecimal 类的方法的时候,要有一个对象接受返回值。

### Java不可变类的概念 不可变对象是指一旦创建之后其状态就不能被修改的对象。这种特性使得不可变对象具有天然的线程安全性,因为它们的状态不会改变,因此不需要考虑同步问题[^2]。 ### 创建不可变类的方式 为了创建一个不可变类,可以遵循以下几个原则: - **私有化构造函数**:确保外部无法随意实例化此类。 - **只读字段**:所有的成员变量都应该是`final`类型,并且初始化时赋值。 - **提供公共静态工厂方法**:用于返回新构建好的不可变对象实例。 - **深拷贝集合属性**:如果存在可变组件作为成员,则需复制这些组件而不是共享引用。 ```java public final class ImmutablePerson { private final String name; private final int age; // 私有的构造器防止外界直接调用new关键字创建对象 private ImmutablePerson(String name, int age) { this.name = Objects.requireNonNull(name); this.age = age; } // 提供公有的静态工厂方法来代替构造器 public static ImmutablePerson of(String name, int age){ return new ImmutablePerson(name,age); } // Getter 方法 public String getName(){ return name; } public int getAge(){ return age; } } ``` ### 不可变类的特点 1. **线程安全**:由于内部状态固定不变,多个线程可以同时访问同一个不可变对象而无需担心竞态条件或数据一致性问题。 2. **简化调试过程**:当遇到错误时更容易追踪原因,因为知道没有任何地方会更改对象的内容。 3. **易于测试**:对于给定输入总是会产生相同的结果,从而便于编写单元测试案例。 4. **促进函数式编程风格**:鼓励开发者采用纯函数的思想去设计应用程序逻辑[^1]。 ### 应用场景 不可变类非常适合应用于那些需要频繁传递但又不允许被篡改的数据结构中,比如配置参数、消息体或者任何其他形式的关键业务实体。另外,在分布式系统里传输数据包的时候也经常使用不可变对象以保障数据的一致性和完整性[^4]。 ### 最佳实践 - 尽量使尽可能多的类成为不可变类,除非确实有必要允许变更行为才将其设为可变。 - 如果某个类包含指向其它对象(如数组或其他复杂类型)的引用,则应确保这些引用所指代的东西同样也是不可变得。 - 当不得不暴露某些敏感信息时可以通过防御性副本(defensive copy)机制来进行保护,即每次对外部请求提供数据前先克隆一份新的副本再交付出去[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值