Java源码分析——不可变的String

什么是不可变对象?

在Java中,String类是不可变的。那么,到底什么是不可变的对象呢?可以这样认为:如果一个对象,在它创建完成之后,不能改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不可改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。

区分对象和对象的引用

对于Java初学者,对String是不可变对象总是存在疑惑的。

示例如下:

String赋值示例

首先创建一个String对象str,然后将str赋值为 "abcdef",再赋值为 "123456"

从打印的结果来看,str的值确实是改变了。那为什么还说String对象是不可变的呢?其实这里存在一个误区:str只是一个String对象的引用,并不是对象本身。对象在内存中是一块内存区,成员变量越多,这块内存区占的空间越大。引用只是一个4字节的数据,里面存放了它所指向的对象的地址,通过这个地址可以访问对象。

也就是说,str只是一个引用,它指向了一个具体的对象,当 str="123456" 执行之后,又创建了一个新的对象 "123456",而str重新指向了这个新的对象,原来的对象 "abcdef" 还在内存中存在,并没有改变。

图示如下:

String赋值图示

Java和C++的一个不同点是,在Java中不能直接操作对象本身,所有的对象都由一个引用指向,必须通过这个引用才能访问对象本身,包括获取成员变量的值,改变对象的成员变量,调用对象的方法等。而在C++中存在引用、对象和指针三个东西,这三个东西都可以访问对象。其实,Java中的引用和C++中的指针在概念上是相似的,他们都是存放的对象在内存中的地址值,只是在Java中,引用丧失了部分的灵活性,比如Java中的引用就不能像C++中的指针一样进行加减运算。

为什么String对象是不可变的?

要理解String的不可变性,首先看一下String类中都有哪些成员变量。

String源码成员变量

由以上代码可以看出,在Java中String类就是对字符数组value的封装,此外还有一个hash变量,是对该String对象哈希值的缓存。

value是private修饰的,并且没有提供setValue公共方法来修改它的值,所以在String类的外部无法修改它。此外,value变量是final的,也就是说在String类内部,一旦这三个值初始化,就不能改变了。所以可以认为String对象是不可变的了。

那么在String中,明明存在一些方法,调用他们可以得到改变后的值。这些方法包括substring、replace、replaceAll、toLowerCase等。

代码示例如下:

replace

可以看到,str的值改变了,其实,这是同样的误区。再次说明,str只是一个引用,不是真正的字符串对象,在调用 str.replace('a', 'A') 时,方法内部创建了一个新的String对象,并把这个新的对象的引用重新赋给str。关于这一点可以参考源码,如下:

replace源码

读者可以自己查看其他方法,都是在方法内部重新创建新的String对象,并且返回这个新的对象,原来的对象是不会改变的。这也是为什么像replace、substring、toLowerCase等方法都存在返回值的原因。也是为什么像下面这样调用不会改变对象的值:

replace demo 2

String对象真的不可变吗?

从上文可知String的成员变量是private final的,也就是初始化之后不可改变。而在这几个成员中,value是比较特殊的,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗?比如将数组中的某个位置上的字符变为下划线“_”。

至少在我们自己写的普通代码中是不能够做到的,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。

那么用什么方式可以访问私有成员呢?没错,用反射,可以反射出String对象中的value属性,进而改变通过获得的value引用改变数组的结构。

代码示例如下:

String

在这个过程中,str始终引用的是同一个String对象,但是在反射前后,这个String对象发生了变化,也就是说,通过反射是可以修改所谓的“不可变”对象的。但是一般我们不这么做。这个反射的示例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能就不是不可变对象。

例如一个car对象,它组合了一个wheel对象,虽然这个wheel对象声明成了private final的,但是这个wheel对象内部的状态可以改变,那么就不能很好的保证car对象不可变。

感谢 昨夜星辰_zhangjg 的 Java中的String为什么是不可变的? -- String源码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kareza

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

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

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

打赏作者

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

抵扣说明:

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

余额充值