本篇文章给大家带来的内容是关于Java中的不可变对象详细解析(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
不可变对象想必大部分朋友都不陌生,大家在平时写代码的过程中100%会使用到不可变对象,比如最常见的String对象、包装器对象等,那么到底为何Java语言要这么设计,真正意图和考虑点是什么?可能一些朋友没有细想过这些问题,今天我们就来聊聊跟不可变对象有关的话题。
一.什么是不可变对象
下面是《Effective Java》这本书对于不可变对象的定义:
不可变对象(Immutable Object):对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化。
从不可变对象的定义来看,其实比较简单,就是一个对象在创建后,不能对该对象进行任何更改。比如下面这段代码:
public class ImmutableObject { private int value; public ImmutableObject(int value) { this.value = value; } public int getValue() { return this.value; }}
由于ImmutableObject不提供任何setter方法,并且成员变量value是基本数据类型,getter方法返回的是value的拷贝,所以一旦ImmutableObject实例被创建后,该实例的状态无法再进行更改,因此该类具备不可变性。
再比如我们平时用的最多的String:
public class Test { public static void main(String[] args) { String str = "I love java"; String str1 = str; System.out.println("after replace str:" + str.replace("java", "Java")); System.out.println("after replace str1:" + str1); }}
输出结果:
从输出结果可以看出,在对str进行了字符串替换替换之后,str1指向本文来源gaodai$ma#com搞$代*码网2的字符串对象仍然没有发生变化。
二.深入理解不可变性
我们是否考虑过一个问题:假如Java中的String、包装器类设计成可变的ok么?如果String对象可变了,会带来哪些问题?
我们这一节主要来聊聊不可变对象存在的意义。
1)让并发编程变得更简单
说到并发编程,可能很多朋友都会觉得最苦恼的事情就是如何处理共享资源的互斥访问,可能稍不留神,就会导致代码上线后出现莫名其妙的问题,并且大部分并发问题都不是太容易进行定位和复现。所以即使是非常有经验的程序员,在进行并发编程时,也会非常的小心,内心如履薄冰。
大多数情况下,对于资源互斥访问的场景,都是采用加锁的方式来实现对资源的串行访问,来保证并发安全,如synchronize关键字,Lock锁等。但是这种方案最大的一个难点在于:在进行加锁和解锁时需要非常地慎重。如果加锁或者解锁时机稍有一点偏差,就可能会引发重大问题,然而这个问题Java编译器无法发现,在进行单元测试、集成测试时也发现不了,甚至程序上线后也能正常运行,但是可能突然在某一天,它就莫名其妙地出现了。
然而人类是机智的,既然采用串行方式来访问共享资源这么容易出现问题,那么有没有其他办法来解决呢?答案是肯定的。
事实上,引起线程安全问题的根本原因在于:多个线程需要同时访问同一个共享资源。
假如没有共享资源,那么多线程安全问题就自然解决了,Java中提供ThreadLocal机制就是采取的这种思想。