学习Java——字符串
创始人
2025-05-30 08:41:52
0

目录

字符串的不可变性

为什么字符串要设计成不可变 

缓存

 安全性

 线程安全

hashCode缓存

性能

总结

附加题


字符串的不可变性

        String在Java中特别常用,而且我们经常要在代码中对字符串进行赋值和改变他的值,但是,为什么我们说字符串是不可变的呢?

        首先,我们需要知道什么是不可变对象?

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

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

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

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

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

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

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

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

为什么字符串要设计成不可变 

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

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

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

I would use an immutable whenever I can.(只要可以,我就会使用一个不可变变量。)

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

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

Q:缓存、安全性、线程安全和性能?这有都是啥
A:你别急,听我一个一个给你讲就好了。

缓存

字符串是使用最广泛的数据结构。大量的字符串的创建是非常耗费资源的,所以,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语言设计人员将该类设置为不可变类的其他原因。主要考虑的是缓存、安全性、线程安全和性能等方面

附加题

1.replace、replaceAll和replaceFirst的区别

        replace、replaceAll和replaceFirst是Java中常用的替换字符的方法,它们的方法定义是:

        replace(CharSequence target, CharSequence replacement) ,用replacement替换所有的target,两个参数都是字符串。

        replaceAll(String regex, String replacement) ,用replacement替换所有的regex匹配项,regex很明显是个正则表达式,replacement是字符串。

        replaceFirst(String regex, String replacement) ,基本和replaceAll相同,区别是只替换第一个匹配项。

        可以看到,其中replaceAll以及replaceFirst是和正则表达式有关的,而replace和正则表达式无关。

        replaceAll和replaceFirst的区别主要是替换的内容不同,replaceAll是替换所有匹配的字符,而replaceFirst()仅替换第一次出现的字符

String string = "abc123adb23456aa";
System.out.println(string);//abc123adb23456aa//使用replace将a替换成H
System.out.println(string.replace("a","H"));//Hbc123Hdb23456HH
//使用replaceFirst将第一个a替换成H
System.out.println(string.replaceFirst("a","H"));//Hbc123adb23456aa
//使用replace将a替换成H
System.out.println(string.replaceAll("a","H"));//Hbc123Hdb23456HH//使用replaceFirst将第一个数字替换成H
System.out.println(string.replaceFirst("\\d","H"));//abcH23adb23456aa
//使用replaceAll将所有数字替换成H
System.out.println(string.replaceAll("\\d","H"));//abcHHHadbHHHHHaa

2.String.valueOf和Integer.toString的区别 

我们有三种方式将一个int类型的变量变成一个String类型,那么他们有什么区别?

1.int i = 5;
2.String i1 = "" + i;
3.String i2 = String.valueOf(i);
4.String i3 = Integer.toString(i);

第三行和第四行没有任何区别,因为String.valueOf(i)也是调用Integer.toString(i)来实现的。

第二行代码其实是String i1 = (new StringBuilder()).append(i).toString();,首先创建一个StringBuilder对象,然后再调用append方法,再调用toString方法。


相关内容

热门资讯

安全生产检查工作计划 安全生产检查工作计划(15篇)  人生天地之间,若白驹过隙,忽然而已,又将迎来新的工作,新的挑战,写...
儿童保健工作计划范文 儿童保健工作计划范文(精选3篇)  光阴迅速,一眨眼就过去了,我们又将续写新的诗篇,展开新的旅程,该...
小学二年级班主任德育工作计划 小学二年级班主任德育工作计划(通用11篇)  时间过得太快,让人猝不及防,我们的工作又将在忙碌中充实...
描写尴尬的好句 描写尴尬的好句  在日常学习、工作或生活中,大家都知道一些经典的句子吧,根据语气的不同句子可以分为陈...
AcWing 3382. 整数... 乍一眼看到还以为做个解空间树和dfs就做出来了...#include using namespace...
C语言实例:判断元音/辅音,三... C 语言实例 - 判断元音/辅音 以下是一个简单的 C 语言程序,可以帮助您判断输入的...
【Java版oj】day13参... 目录  一、参数解析 (1)原题再现 (2)...
经典版英文表白句子 经典版英文表白句子  1、I miss you!i want to see you now alth...
改变人生的经典句子 改变人生的经典句子  无论是在学校还是在社会中,大家或多或少都接触过一些经典的句子吧,句子是语言运用...
名师工作室成员工作计划 名师工作室成员工作计划  时光在流逝,从不停歇,又迎来了一个全新的起点,不妨坐下来好好写写工作计划吧...
唯美女生句子 唯美女生句子75句  你走进了我那个曾经下着雪的内心,令我不知所措。下面是小编精心整理的唯美女生句子...
Android 进程间通信机制... 一. 简介         要想启动一个应用,首先要保证这个应用所需要的进程已经启动过...
week13周报 一.动态规划走楼梯2难点:不能连续走三次两级台阶如何表示思路:可以用二维...
描写清晨的句子 描写清晨的句子50则  导语:早起的云雀在那半明半暗的云空高啭着歌喉,而在遥远的遥远的天际,则有着一...
关于梦想的唯美句子   梦想,是对未来的一种期望,指在现实想未来的事或是可以达到但必须努力才可以达到的境况。每个人都有梦...
描写冬天的景色的句子 描写冬天的景色的句子(精选50句)  无论是身处学校还是步入社会,大家都收藏过令自己印象深刻的句子吧...
读书的句子 关于读书的句子书籍是巨大的力量——列宁书籍是青年人不可分离的生命伴侣和导师——高尔基读一本好书,就是...
[JAVA]类和对象 目录 1.类 1.1类的初步认识  1.2类的定义格式 1.3类的实例化 1.4类和对象的说明 2...
spring中各种现象的解释 1、现象一:实现了BeanFactoryPostProcessor的类无法解析@...
感人的爱情语句 感人的爱情语句20句  1、如果你也爱一个人,请不要去和别人暧昧,因为那样会伤害到你们之间得来不易的...