前言
字符串就是因为它太常用,用起来也简单,所以总是会被忽视,所以本文就是带你了解这个你“很懂”的String类
思维导图
String类的组成
String类在java的lang包下,通过源码(java8)可以知道String类存对象的本质使用的是char数组
不可变性
不可变性是Java中String类的一个很重要的特性,这里有两个问题:
- 那么为什么说它是不可变的,Java是如何实现的?
- 为什么Java要把String类设计成不可变的?
如何实现不可变
Java的String类由final关键字修饰,意味着String类不可继承
同时String类的本质是char数组,这个私有数组且是被final关键字修饰,意味着此数组无法被修改引用
并且String类中没有提供任何方法如setValue这类方法修改value的值
除了上面几点以外,还有一点就是,String类内部使用一些操作String的方法实质上都是会创建一个新的String对象的,然后修改指针让用户看起来是在修改原String
如String的subString
方法,最后还是调用了new String重新生成一个新的String对象
还比如concat
方法,也是生成一个新的String对象返回
所以Java的String是不可变的
为什么要设计成不可变
设计成如此主要有几个好处:
- 只有String设计成不可变的,才能实现
字符串常量池
,如果String可变,那么多个引用常量池中同一常量的变量在修改时就会互相影响! - String在Java中大量使用,不可变性可以保证数据不被篡改
- 不可变的String,hash code也是固定的,所以String类中有hash的成员变量,涉及到hash运算的时候就不用重新计算,提高了效率
- 不可变的String是线程安全的,因为任意线程都无法修改String对象,它是固定的不可变的
有没有办法强行修改
那必须的,因为Java有反射
的存在,反射太imba了!
字符串常量池
为什么要设计字符串常量池
String在Java中使用非常频繁,如果设计上它和其他对象一样,那么频繁的创建对象势必会造成严重的性能问题
所以Java设计实现了字符串常量池,相当于一个字符串的缓存池。在创建字符串对象前会先去字符串常量池查找是否有此字符串,如果有,就直接返回引用,否则就创建一个字符串常量丢到池中再返回引用
这样就大大提高了字符串的利用率,减少了频繁的对象创建
下面来看看Java中String的创建过程
字符串对象创建过程
Java中字符串创建的最基本的流程就是:先查常量池,有则直接返回引用,无则创建再返回引用
这里存在一个重要的设计模式享元模式
但这不是全部,实际的过程会不同情况会有些许的变化,这是一个基础,知道了这个流程我们再去看看一些实际的例子,加深一些印象
String a = “Hello World”
流程:
- 先在字符串常量池中查找,有没有"Hello World"字符串缓存
- 有则直接返回"Hello World"对象的引用
- 没有则在字符串常量池中创建"Hello World"对象,再将引用返回
String a = new String(“Hello World”)
new String这个过程就和上面有点不同,它调用了构造方法,会去堆空间创建一个String对象出来
创建了几个对象呢?
- 当常量池存在"Hello World",创建一个String对象,在堆中,value直接复制常量池缓存
- 当常量池不存在"Hello World",先在堆中创建String对象value是"Hello World",然后缓存"Hello World"到常量池
理解了上面两个最基础的再往下看
String ab = “a” + “b”
(一下前提都是常量池不存在任何常量缓存)
这个过程中会先在常量池创建"a"和"b"
由于是两个常量池中常量的连接,Java在处理此类情况时会采取"COW"机制