学习Java——字符串

文章讨论了Java中String类的不可变性,解释了为何String设计为不可变,包括缓存优化(字符串池),安全性保障,线程安全的实现以及性能提升。不可变性使得字符串在多线程环境下更安全,且能有效利用字符串池和hashCode缓存以提高效率。
摘要由CSDN通过智能技术生成

目录

字符串的不可变性

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

缓存

 安全性

 线程安全

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方法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值