作者:困了电视剧
专栏:《JavaSE语法与底层详解》
文章分布:这是一篇关于String类型及其底层构造的文章,如有疏漏,欢迎大佬指正!
String对象的创建
字符串的用法比较多,所以String类提供的构造方式也比较多,这里展示三种常用的构造方式:
public static void main(String[] args) {
String str1="aaa";
//==============================
String str2=new String("bbb");
//==============================
char[] array={'c','c','c'};
String str3=new String(array);
}
了解了如何构建一个字符串后,我们还需要了解String对象底层的构造逻辑,下面让我们打开看一下String类的源码:
可以看到,String是引用类型,内部并不存储字符串本身,字符串实际保存在char类型的数组中,这一点跟C语言构造字符串很像,可以进行参考。
字符串常量池
在工作中,String类是我们使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间,这就是字符串常量池。字符串常量池由String类私有的维护。
https://blog.csdn.net/m0_62815572/article/details/127887112在这篇文章中,我简要介绍了java对内存的分区。在jdk7之后字符串常量池被分进了堆区。
当我们创立一个String对象时,比如上述栗子中的str1,由于我之前并没有创建过内容为"aaa"的String对象,所以我的这个"aaa"就会被放入常量池中,当我下次在需要"aaa"这一字符串的时候,java就会从常量池中直接取出节约时间。
public static void main(String[] args) {
String str1="aaa";
String str2=new String("aaa");
}
就比如现在有这样一段代码,他们在内存中的创建方式具体为:
str1直接指向在常量池中的对象 ,str2由于是通过new String对象的方式创建的,所以会单独地在堆区中在开辟一块String的空间,然后这个对象的value数组再指向常量池的"aaa"。
String对象的修改
由String类的源码我们可以看到value数组是由final修饰的,并且当一个String对象被创建后,其字符串的值会被放在常量池中,value数组也会指向字符串的地址,那此时如果我想对字符串进行修改怎么办,让我们反编译一下,看看java是怎样做的:
现在有这样一段简单的代码,它的运行结果也在意料之中
这就是这段代码在运行过程中的背后逻辑了,我们可以发现一个神奇的点,java程序为了完成这一操作引用了StringBuilder类,根据注释我们可以理解他大体的逻辑就是:先创建一个StringBuilder对象,然后这个对象通过append方法实现字符串的拼接,最后运用toString方法,将拼接后的字符串返回成一个String对象,完成功能。那这个StringBuilder类又是什么呢?
StringBuilder类
我们打开StringBuilder的源码一探究竟
由这我们可以看出StringBuilder和String的一个很大的区别就是,StringBuilder的value数组并没有被final所修饰,也就是他不是一个常量,可以更改指向,再看一下append方法
可以看出在append方法中,append完成字符串的拼接后不会return一个新的对象而是return this,所以如果我们需要大量的拼接操作的话,用StringBuilder类会节约很多资源,提高效率。
举个栗子
String str = new String("ab"); // 会创建多少个对象
String str = new String("a") + new String("b"); // 会创建多少个对象
第一个是创建两个对象。
第二个会创建一个StringBuilder对象,这个对象完成拼接后由于接受的是String对象所以还会进行一次toString创建一个String对象,所以一共6个对象。
StringBuilder类与StringBuffer类的区别
这两个类的父类相同,在重写的方法中
StringBuffer多了一份线程上相关知识的运用,在本质上没有太大的区别。