百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

漫画:为什么Java里面的String对象是不可变的?

ztj100 2024-12-05 18:08 14 浏览 0 评论


String的不变性

String在Java中特别常用,相信很多人都看过他的源码,在JDK中,关于String的类声明是这样的:

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}

可以看到,String类是final类型的,那么也就是说,String是一个不可变对象。

不可变对象是在完全创建后其内部状态保持不变的对象。这意味着,一旦对象被赋值给变量,我们既不能更新引用,也不能通过任何方式改变内部状态。

可是有人会有疑惑,String为什么不可变,我的代码中经常改变String的值啊,如下:

String s = "abcd";
s = s.concat("ef");

这样,操作,不就将原本的"abcd"的字符串改变成"abcdef"了么?

但是,虽然字符串内容看上去从"abcd"变成了"abcdef",但是实际上,我们得到的已经是一个新的字符串了。

如上图,在堆中重新创建了一个"abcdef"字符串,和"abcd"并不是同一个对象。

所以,一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。而且,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。

如果我们想要一个可修改的字符串,可以选择StringBuffer 或者 StringBuilder这两个代替String。

为什么String要设计成不可变

在知道了"String是不可变"的之后,大家是不是一定都很疑惑:为什么要把String设计成不可变的呢?有什么好处呢?

这个问题,困扰过很多人,甚至有人直接问过Java的创始人James Gosling。

在一次采访中James Gosling被问到什么时候应该使用不可变变量,他给出的回答是:

I would use an immutable whenever I can.

那么,他给出这个答案背后的原因是什么呢?是基于哪些思考的呢?

其实,主要是从缓存、安全性、线程安全和性能等角度出发的。

缓存

字符串是使用最广泛的数据结构。大量的字符串的创建是非常耗费资源的,所以,Java提供了对字符串的缓存功能,可以大大地节省堆空间。

JVM中专门开辟了一部分空间来存储Java字符串,那就是字符串池。

通过字符串池,两个内容相同的字符串变量,可以从池中指向同一个字符串对象,从而节省了关键的内存资源。

String s = "abcd";
String s2 = s;

对于这个例子,s和s2都表示"abcd",所以他们会指向字符串池中的同一个字符串对象:

但是,之所以可以这么做,主要是因为字符串的不变性。试想一下,如果字符串是可变的,我们一旦修改了s的内容,那必然导致s2的内容也被动地改变了,这显然不是我们想看到的。

安全性

字符串在Java应用程序中广泛用于存储敏感信息,如用户名、密码、连接url、网络连接等。JVM类加载器在加载类的时也广泛地使用它。

因此,保护String类对于提升整个应用程序的安全性至关重要。

当我们在程序中传递一个字符串的时候,如果这个字符串的内容是不可变的,那么我们就可以相信这个字符串中的内容。

但是,如果是可变的,那么这个字符串内容就可能随时都被修改。那么这个字符串内容就完全可信了。这样整个系统就没有安全性可言了。

线程安全

不可变会自动使字符串成为线程安全的,因为当从多个线程访问它们时,它们不会被更改。

因此,一般来说,不可变对象可以在同时运行的多个线程之间共享。它们也是线程安全的,因为如果线程更改了值,那么将在字符串池中创建一个新的字符串,而不是修改相同的值。因此,字符串对于多线程来说是安全的。

hashcode缓存

由于字符串对象被广泛地用作数据结构,它们也被广泛地用于哈希实现,如HashMap、HashTable、HashSet等。在对这些散列实现进行操作时,经常调用hashCode()方法。

不可变性保证了字符串的值不会改变。因此,hashCode()方法在String类中被重写,以方便缓存,这样在第一次hashCode()调用期间计算和缓存散列,并从那时起返回相同的值。

在String类中,有以下代码:

private int hash;//this is used to cache hash code.

性能

前面提到了的字符串池、hashcode缓存等,都是提升性能的提现。

因为字符串不可变,所以可以用字符串池缓存,可以大大节省堆内存。而且还可以提前对hashcode进行缓存,更加高效

由于字符串是应用最广泛的数据结构,提高字符串的性能对提高整个应用程序的总体性能有相当大的影响。

总结

通过本文,我们可以得出这样的结论:字符串是不可变的,因此它们的引用可以被视为普通变量,可以在方法之间和线程之间传递它们,而不必担心它所指向的实际字符串对象是否会改变。

我们还了解了促使Java语言设计人员将该类设置为不可变类的其他原因。主要考虑的是缓存、安全性、线程安全和性能等方面

来源:https://mp.weixin.qq.com/s/dQUrWBcS2TjS8GpiKwor6g

相关推荐

Vue3非兼容变更——函数式组件(vue 兼容)

在Vue2.X中,函数式组件有两个主要应用场景:作为性能优化,因为它们的初始化速度比有状态组件快得多;返回多个根节点。然而在Vue3.X中,有状态组件的性能已经提高到可以忽略不计的程度。此外,有状态组...

利用vue.js进行组件化开发,一学就会(一)

组件原理/组成组件(Component)扩展HTML元素,封装可重用的代码,核心目标是为了可重用性高,减少重复性的开发。组件预先定义好行为的ViewModel类。代码按照template\styl...

Vue3 新趋势:10 个最强 X 操作!(vue.3)

Vue3为前端开发带来了诸多革新,它不仅提升了性能,还提供了...

总结 Vue3 组件管理 12 种高级写法,灵活使用才能提高效率

SFC单文件组件顾名思义,就是一个.vue文件只写一个组件...

前端流行框架Vue3教程:17. _组件数据传递

_组件数据传递我们之前讲解过了组件之间的数据传递,...

前端流行框架Vue3教程:14. 组件传递Props效验

组件传递Props效验Vue组件可以更细致地声明对传入的props的校验要求...

前端流行框架Vue3教程:25. 组件保持存活

25.组件保持存活当使用...

5 个被低估的 Vue3 实战技巧,让你的项目性能提升 300%?

前端圈最近都在卷性能优化和工程化,你还在用老一套的Vue3开发方法?作为摸爬滚打多年的老前端,今天就把私藏的几个Vue3实战技巧分享出来,帮你在开发效率、代码质量和项目性能上实现弯道超车!一、...

绝望!Vue3 组件频繁崩溃?7 个硬核技巧让性能暴涨 400%!

前端的兄弟姐妹们五一假期快乐,谁还没在Vue3项目上栽过跟头?满心欢喜写好的组件,一到实际场景就频频崩溃,页面加载慢得像蜗牛,操作卡顿到让人想砸电脑。用户疯狂吐槽,领导脸色难看,自己改代码改到怀疑...

前端流行框架Vue3教程:15. 组件事件

组件事件在组件的模板表达式中,可以直接使用...

Vue3,看这篇就够了(vue3 从入门到实战)

一、前言最近很多技术网站,讨论的最多的无非就是Vue3了,大多数都是CompositionAPI和基于Proxy的原理分析。但是今天想着跟大家聊聊,Vue3对于一个低代码平台的前端更深层次意味着什么...

前端流行框架Vue3教程:24.动态组件

24.动态组件有些场景会需要在两个组件间来回切换,比如Tab界面...

前端流行框架Vue3教程:12. 组件的注册方式

组件的注册方式一个Vue组件在使用前需要先被“注册”,这样Vue才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册...

焦虑!Vue3 组件频繁假死?6 个奇招让页面流畅度狂飙 500%!

前端圈的朋友们,谁还没在Vue3项目上踩过性能的坑?满心期待开发出的组件,一到高并发场景就频繁假死,用户反馈页面点不动,产品经理追着问进度,自己调试到心态炸裂!别以为这是个例,不少人在电商大促、数...

前端流行框架Vue3教程:26. 异步组件

根据上节课的代码,我们在切换到B组件的时候,发现并没有网络请求:异步组件:...

取消回复欢迎 发表评论: