Java String 为什么不可变

在日常写代码时,你肯定经常用到字符串:比如给用户显示姓名、拼接一段提示语,几乎处处都有它的身影。但不知道你有没有注意过一个细节——当你修改一个String对象时,它好像不是在原来的内容上改,而是生出了新的字符串。举个例子:你定义了一句字符串str = "我喜欢吃苹果",再写一句str = str.replace("苹果", "香蕉"),这时候你打印str,会变成“我喜欢吃香蕉”,但原来的“我喜欢吃苹果”还静静地躺在那里,没被碰过。

这背后就是Java中String的“不可变”特性。这个特性不是随便定的,而是经过了很多考虑,还藏着不少实际开发里的好处。

Java String 为什么不可变

什么是String的不可变

先把这个特性讲明白:简单说就是,你一旦创建了一个String对象,它包含的内容就再也不能被修改了。任何看起来是“修改字符串”的操作,比如替换、拼接、截取,其实都是在内存里新生成了一个String对象,原来的对象保持不变。

刚才的例子里,原来的"我喜欢吃苹果"是一个对象,replace操作后生成了新的"我喜欢吃香蕉"对象,然后把变量str指向了新对象,原来的对象因为没人再用,慢慢会被Java的垃圾回收清理掉。就像你在本子上写了“苹果”,要改成“香蕉”不是直接擦了改,而是重新写了一行,原来的字还在本子上。

为什么要设计成不可变

这个特性不是Java凭空想出来的,而是解决了开发里的几个大问题。

第一个好处是安全。假设你在程序里用字符串存了一些敏感信息,比如数据库密码、用户的身份证号。如果String是可变的,那随便一段代码就能改掉这些信息,可能会导致数据泄露或者程序出错。但不可变的话,内容从创建后就固定了,谁也改不了,就像把重要文件封进了密封袋,只能看不能改。

第二个好处是高效,尤其是节省内存。Java里有个“字符串常量池”的机制,就是把常用的字符串存在一块公共的内存里,多个相同内容的字符串可以共享同一个对象。比如你写了String a = "测试"; String b = "测试";,其实a和b指向的是同一个对象,不会占两份内存。如果String是可变的,那改其中一个就会影响另一个,常量池就没法用了,每次新建字符串都得单独占内存,浪费空间。

第三个是可以安全地用在多线程里。多线程开发时,最麻烦的就是多个线程同时修改同一个数据,容易出bug。String不可变的话,它的内容永远不会变,多个线程只能读,不能改,就不会有冲突,不用加额外的锁,代码写起来更简单。

有没有什么不便?

当然,不可变也不是完美的。如果要频繁修改字符串,比如循环里拼接几百次字符串,每次都会生成新对象,会占用很多内存,垃圾回收也会变忙,拖慢程序速度。这时候就别用String了,Java给我们准备了专门的可变字符串类:StringBuilder和StringBuffer。前者是单线程用的,效率高;后者是多线程用的,安全。比如需要循环拼接日志,用StringBuilder就比String快好几倍。

其实这个设计也体现了Java的思路:用不变性换安全和高效,再提供可变的工具解决性能问题,让开发者可以根据场景选最合适的方式。下次再用字符串的时候,不妨多留意一下这些细节,就能写出更靠谱的代码了。

上一篇 JavaScript 数组怎么定义