一文详解Java中的可变对象(Mutable)与不可变对象(Immutable)

 更新时间:2023年11月05日 08:53:15   作者:it键盘侠  
如何在 Java 中创建不可变对象?我以前以为所有对象都是不可变的,因为如果你改变一个 String 实例的内容,它总是会创建一个新的 String 对象并指向该对象,在本文中,我不仅将分享在 Java 中Immutable的步骤,还将讨论可变对象与不可变对象及其优缺点

如何在 Java 中创建不可变对象?我以前以为所有对象都是不可变的,因为如果你改变一个 String 实例的内容,它总是会创建一个新的 String 对象并指向该对象。但后来我发现,String 是一个特殊的类,它被特别设计为Immutable,因为它经常被cache。显然,你不能缓存任何不恒定的东西,这就是为什么 String 在 Java 中是不可变的原因。但这鼓励我学习更多有关 Java 中Immutable和Mutable类的知识,以及如何在 Java 中创建自定义的Immutable。

在本文中,我不仅将分享在 Java 中Immutable的步骤,还将讨论可变对象与不可变对象及其优缺点。这也是一个常见的 String 面试问题 ,Java 开发人员也应该意识到这一点。  

Java 中的可变类和不可变类是什么?

在 Java 中,可变类和不可变类的概念指的是对象创建后其状态是否可以更改。可变类是指实例创建后可以修改的类,而不可变类一旦创建就不能改变其状态。

可变对象的状态可以通过修改其字段或属性的方法来改变。例如,StringBuilder 和 ArrayList 都是可变类。例如,你可以add、delete或modify StringBuilder 或 ArrayList 中的元素。

StringBuilder mutableString = new StringBuilder("Hello");

mutableString.append(", World!"); // Mutable operation

System.out.println(mutableString.toString()); // Outputs: Hello, World!

不可变类是指实例创建后不可修改的类。不可变对象的状态在创建过程中就已设定,创建后无法更改。例如,String 和 Integer 就是不可变类的例子。一旦创建了 String 对象,就不能更改其中包含的字符。

String immutableString = "Hello";


// Immutable operation - returns a new String,
// but doesn't modify the original

immutableString.concat(", World!"); 

System.out.println(immutableString); // Outputs: Hello

因此,你现在应该知道,不可变对象就是其内容不可以更改的对象。所有字段都是final字段的类,或者所有字段都是private字段且没有构造器的类就是几个例子。由于这些字段都是final字段或private字段,因此永远无法从外部更改。这使得它们不可变。

另一方面,可变类允许更改其内容。例如,带有非final字段或带有构造器的private字段的类。由于外部代码可以更改类的内容,因此该类是可变的。

不可变类(如 String)也可以被缓存,在 Java 中,String 被缓存在一个特殊的 String 池中,这主要是为了节省内存,并允许重复使用 String 字面量:

如何在 Java 中创建不可变类?

既然我们已经知道什么是 Java 中的可变类和不可变类,那么现在就来了解一下如何编写不可变类,以及编写不可变类与创建可变类有什么不同。唯一的区别在于如何编写。

下面是一个不可变类的示例:

public class CustomImmutableClass {

    public final String customString = ""; //field is final, so it cannot be changed

    private int customInt = 0; //field is private and has no setter, so it cannot be changed

    public int getCustomInt() {

        return customInt;  //CustomInt can be retrieved, but not set.

    }
}

这是可变类的示例:

public class CustomMutableClass {
    public String customString = ""; //field is NOT final, so it CAN be changed

    private int customInt = 0; //field is private and has a setter, so it CAN be changed

    public int getCustomInt() {

        return customInt;  //CustomInt can be retrieved
    }

    public void setCustomInt(int customInt) {

        this.customInt = customInt; //customInt can be set
    }

}

不可变类应被标记为 final 类,这样它们就不能被扩展,但仅仅使类成为 final 类并不能使其成为不可变类,尤其是当它可能泄漏状态(如返回一个非 final 的对象和状态的一部分)时。此外,拥有公共的 final 字段也是一种不好的形式。

字符串是不可变的,而大多数对象不是。无论何时使用突变器方法(setSomething 或 addSomething)

返回 void 的对象很可能是可变的。一个突出的例子就是 ArrayList。

要使对象不可变,请确保它们只有非数组的final字段(在 Java 中数组总是可变的),并且所有字段类型也只有final字段。如果不允许访问/更改字段,就可以使用非final字段,但这并不容易推理(但大多数情况下比较容易)。

Java 中不可变对象与可变对象的区别

以下是 Java 中可变类和不可变类之间的一些主要区别:

1. 修改

可变对象在创建后可以修改,但不可变对象在创建后不能修改。

2. 线程安全可变对象不是线程安全的,如果在多线程环境中使用,可能需要同步以避免数据损坏。另一方面,不可变对象通常是线程安全的,因为状态不能更改并且可以在多个线程之间安全共享。 

3. 状态更改可变对象允许更改状态,但不可变对象的状态在创建时是固定的。

4. 使用案例当你需要经常修改对象,或想表示状态会随时间变化的实体时,可变对象就派上用场了。而不可变对象则适用于需要确保对象状态保持不变或需要线程安全的情况。

5. 性能由于直接修改状态,可变对象在某些场景下可以具有更好的性能,但不可变对象可能涉及创建新对象,可能会影响性能,但在安全性和简单性方面具有优势。

总结

这就是Java 中的不可变类和可变类的全部内容。 本文不仅介绍了什么是可变类和不可变类,还介绍了它们之间的区别。在可变类和不可变类之间做出选择,取决于程序的具体要求和所需对象的特性。不可变类通常是并发或多线程环境中的首选,可以简化对对象状态的推理。

以上就是一文详解Java中的可变对象(Mutable)与不可变对象(Immutable)的详细内容,更多关于Java可变对象与不可变对象的资料请关注脚本之家其它相关文章!

相关文章

  • Spring的@Scope注解详细解析

    Spring的@Scope注解详细解析

    这篇文章主要介绍了Spring的@Scope注解详细解析,@Scope注解主要作用是调节Ioc容器中的作用域,springboot 程序启动时会对classpath路径下的包中的类进行扫描,将类解析成BeanDefinition,需要的朋友可以参考下
    2023-11-11
  • Java程序命令行参数用法总结

    Java程序命令行参数用法总结

    这篇文章主要介绍了Java程序命令行参数用法总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 关于Springboot日期时间格式化处理方式总结

    关于Springboot日期时间格式化处理方式总结

    这篇文章主要介绍了关于Springboot日期时间格式化处理方式总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • SpringBoot使用knife4j进行在线接口调试

    SpringBoot使用knife4j进行在线接口调试

    这篇文章主要介绍了SpringBoot使用knife4j进行在线接口调试,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Spring自定义注解实现接口版本管理

    Spring自定义注解实现接口版本管理

    这篇文章主要介绍了Spring自定义注解实现接口版本管理,RequestMappingHandlerMapping类是与 @RequestMapping相关的,它定义映射的规则,即满足怎样的条件则映射到那个接口上,需要的朋友可以参考下
    2023-11-11
  • SpringBoot快速配置数据源的方法

    SpringBoot快速配置数据源的方法

    这篇文章主要介绍了SpringBoot快速配置数据源的方法,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下
    2020-10-10
  • MybatisPlus使用排序查询时将null值放到最后

    MybatisPlus使用排序查询时将null值放到最后

    按照更新时间排序,但是更新时间可能为null,因此将null的数据放到最后,本文主要介绍了MybatisPlus使用排序查询时将null值放到最后,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • 详解Java设计模式之职责链模式

    详解Java设计模式之职责链模式

    责任链模式是一种行为设计模式,使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,文中通过代码示例给大家介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • java实现贪吃蛇极速版

    java实现贪吃蛇极速版

    这篇文章主要为大家分享了java贪吃蛇极速版,贪吃蛇经典手机游戏,既简单又耐玩,本文用java来实现下贪吃蛇小游戏,感兴趣的小伙伴可以参考下
    2015-12-12
  • logback使用MDCFilter日志过滤源码解读

    logback使用MDCFilter日志过滤源码解读

    这篇文章主要介绍了logback使用MDCFilter日志过滤源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论