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",但是实际上,我们得到的已经是一个新的字符串了。
漫画:为什么Java里面的String对象是不可变的?
如上图,在堆中重新创建了一个"abcdef"字符串,和"abcd"并不是同一个对象。
所以,一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。而且,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
如果我们想要一个可修改的字符串,可以选择StringBuffer 或者 StringBuilder这两个代替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",所以他们会指向字符串池中的同一个字符串对象:
漫画:为什么Java里面的String对象是不可变的?
但是,之所以可以这么做,主要是因为字符串的不变性。试想一下,如果字符串是可变的,我们一旦修改了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语言设计人员将该类设置为不可变类的其他原因。主要考虑的是缓存、安全性、线程安全和性能等方面
——如果你get到了,那就点个赞转发支持一波吧!
——整理不易,白嫖私信口令【666】,100%回复分享!
文章浏览阅读6.7k次。新公司采用golang作为主要的开发语言,而且开发环境是mac,对我来说是一个不小的挑战,生平第一次使用mac系统,另外一个在mac下没有一个类似VS一样的优秀的IDE,现在才发现微软的伟大。 对一个IDE我有两个最基本的需求,第一需要能够语法识别高亮自动补全跳转,第二就是能够debug。只有vscode能够完美的支持这两个功能。 公司的started手册推荐的编辑器是intelij_mac vscode 创建go项目
文章浏览阅读778次。都是脚本语言, 不用编译, 直接写了就执行的perl 是用来处理文本的 特别适合大量文本的解析shell 则适合系统管理, 比如自动完成一系列linux命令拿 perl 来说, 在 linux 下面创建一个文本文件 xx.pl, #!/usr/bin/perl print "hello!\n";保存chmod 755 xx.pl./xx.pl_shell语言 perl脚本
文章浏览阅读2.2k次,点赞3次,收藏7次。Spring Boot的自动装配中的@ConditionalOnBean条件装配注解在Spring启动过程中,是如何保证处理顺序靠后的@ConditionalOnBean条件装配的顺序就是Spring的DeferredImportSelector接口延迟处理机制来保证的。_conditionalonbean顺序问题
文章浏览阅读1.9w次,点赞6次,收藏8次。全局聚类系数:全局集聚系数是基于结点三元组的。 全局集聚系数是封闭的三元组数目/所有三元组数目,即Clustering coefficient(global) = number of closed triplet / number of triplet(closed+open) 局部聚类系数:局部计算是面向节点的,对于节点vi,找出其直接邻居节点集合Ni,计算Ni构成的网络中的..._全局聚类系数怎么算
文章浏览阅读2.8k次,点赞2次,收藏15次。学习视频链接:Python数据分析与展示_北京理工大学_哔哩哔哩_bilibili P21-P35Matplotlib库入门一、Matplotlib库的导入Matplotlib库是python优秀的可视化第三方库Matplotlib库由各种可视化类构成,内部结构复杂,受Matlab启发,matplotlib.pyplot是绘制各类可视化图形的命令子库,相当与快捷方式。#plt是引入模块的别名,这是一种约定俗成的引入名称import matplotlib.pyplot as plt.._导入matplotlib库
文章浏览阅读3.1k次,点赞2次,收藏15次。多线程库vthread安装pip install vthread线程池如果不加装饰器就是普通的单线程,只用加一行就能在不破坏代码原来的结构直接实现线程池操作,能支持参数传递,支持分组。不加装饰器import timestart = time.time()def foolfunc(num): print(f"test foolnum:{num}")for i in rang..._vthread
文章浏览阅读459次。——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——- 一,语言基础组成 Java语言基础由关键字、标识符、注释、常量和变量、运算符、语句、函数和数组等组成。 本篇重点在变量中。 关键字:被Java语言赋予了特殊含义的单词,所有关键字的单词全小写拼写。 abstract :表明类或者成员方法具有抽象属性 assert :用来进行程序调试 bool_错误: 浮点文字的格式错误 public static void main(string args[]) throws 10exc
文章浏览阅读309次。当我们需要利用网站快速展示仿真数据时,可以用sematic UI和django快速开发。相关网址如下:django 3.2文档:semantic UIhighchartsB站视频:步骤:1… …> django-admin startproject mysite新建网站…> py manage.py startapp polls 创建应用polls/views.py中创建视图from django.http import HttpResponsedef inde_django和semantic
文章浏览阅读2.1w次。首先我们要知道,在计算机中,数据是用其二进制补码形式来存储的,过程如下:首先130会被当成int类型存储,int是4个字节32位,也就是0000 0000 0000 0000 0000 0000 1000 0010,转成byte类型会丢弃高三字节部分,也就是1000 0010,因为正数的源码,补码,反码相同,所以130的补码还是1000 0010,将其转换成源码:补码(1000 0010)->反码(1000 0001)->原码(1111 1110)为-126,所以最后的转换结果就是-126。_java中int 126+ 2
文章浏览阅读1.1w次。这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入欢迎使用Ma..._.netcore http 请求
文章浏览阅读7.2k次。Linq查询,lambda表达式LINQ(Language Integrated Query,语言集成查询)提供了一种跨数据源和数据格式的统一模型,实现查询。主要包括三个部分:LINQ to Objects、LINQ to XML、LINQ enabled ADO.NET,而在LINQ enabled ADO.NET中又包括LINQ to DataSet、LINQ to SQL 和 LINQ to Entities。“Lambda 表达式”是一种简化的匿名函数,在C#3.0中引入的,可用于创建委托.._using system; namespace aaa { class program { static void main() { int n
文章浏览阅读1.4k次。自制思维导图文章目录自制思维导图可复制的领导力推荐只要坚信方法论的有效性,构建起自己的科学思考逻辑,我们完全可以通过自己的刻意练习,获得很多看起来传奇的能力。持续不断地用同样的方法做同一件事,却期望得到不同的结果,这叫荒谬。管理就是最大程度的激发他人的善意我们只相信一点,学习可以改变自己。不会就学,学了就用,错了就改,就看谁快。第一章:80%的管理者能达到80分过去的企业管理员工,靠的是严格约束;现在企业管理员工,靠的是相互吸引。一个有野心的管理者,需要将每名员工变成团队的战略合作者。例子中西差异领导力_如何有效的复制领导力?