字符串常量池、String类、StringBilder类和StringBuffer类

1.构造String

一般,我们常用下面的两种方式来构造String。
1.

String str1 = "hello";
String str2 = "hello";
String str1 = new String("hello");
String str2 = new String("hello")

但是,构造String的方法不止这两种,其他方法在这里就不再赘述了。

看起来,这两种方法都构造了两个相同的字符串hello,但是,在底层,这两种方法是不同的。

2.从内存上看两种方法的区别

对于方法1代码,在内存上有:
在这里插入图片描述
也就是,通过这种方法,只构造了一个hello,在构造String str2 = “hello”;的前,已经有一个hello在堆上了,这时便不再开辟新的堆空间去构造hello,而是在栈上再创建一个str2引用,指向原有的这个hello。

对于方法2,从内存上看:
在这里插入图片描述
通过这样的方法,在堆上保存了两份hello,创建了两个引用str1,str2分别来指向这两个字符串。

3.如何验证2中的说法

我们知道,在Java中,对于String,==是比较两个引用是否指向同一个对象,equals()方法是比较字符串内容是否相同。
那么,对于方法1:

str1 == str2;

结果是True。从上面的内存图中可以知道,str1和str2指向的是同一个对象。

对于方法2:

str1 == str2;

结果是False。因为str1和str2指向的是两个不同的hello。

4.字符串常量池

对于上面的例子,方法1暂且称为直接赋值法。

String str1 = "hello" ;
String str2 = "hello" ; 
String str3 = "hello" ; 
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true

对于这个代码,它的内存分布又是怎样的呢?
在这里插入图片描述
通过上面的介绍,能知道,此时堆中只有一份hello,栈中有三个引用指向这一个hello。
那么问题来了,为什么没有开辟新的空间呢?
在JVM底层实际上会自动维护一个对象池(字符串常量池)
如果采用了直接赋值法进行String类的对象实例化操作,那么该实例化对象将自动保存到这个对象池之中。
如果下次继续使用直接赋值声明String类对象,此时对象池之中如若有指定内容,将直接进行引用如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。

对于方法2:
在这里插入图片描述

如果使用String构造方法就会开辟两块堆内存空间,并且其中一块堆内存将成为垃圾空间(字符串常量 “hello” 是一个匿名对象, 用了一次之后就不再使用了, 就成为垃圾空间, 会被 JVM 自动回收掉),也就是标红的那部分是垃圾空间,会被回收掉,显然,这种方法的开销比直接赋值法更大。

我们可以使用手动入池的方法让字符串对象加入到字符串常量池中。

String str1 = new String("hello").intern() ; 
String str2 = "hello" ; 
System.out.println(str1 == str2);//True

此时,在内存上表现为:
在这里插入图片描述

5.字符串不可变

字符串是一种不可变对象. 它的内容不可改变。
先看这样的一段代码:

String str = "hello" ; //1
str = str + " world" ; //2
str += "!!!" ; //3
System.out.println(str); 
// 执行结果
hello world!!!

表面上看起来,字符串好像变了,其实并不是。看一下内存图:
在这里插入图片描述

这是第一行代码执行后的内存图。
在这里插入图片描述

这是第二行代码执行后的内存图。
在这里插入图片描述

这是第三行代码执行后的内存图。
+= 之后 str 打印的结果却是变了, 但是不是 String 对象本身发生改变, 而是 str 引用到了其他的对象.

6.StringBuilder和StringBuffer

String类是不可变类,StringBuilder和StringBuffer类是可变类。
StringBuffer和StringBuilder用法基本相同,不同的是,StringBuilder是线程不安全的,StringBuffer是线程安全的。这里以StringBuffer为例。
StringBuffer提供了append(),replaceAll(),replaceFirst(),insert()等方法可以改变字符串的内容。

还与String类不同的是,StringBuffer没有提供equals方法,在比较字符串内容时,需要使用toString()方法转换成String类型,再使用equals()方法。


版权声明:本文为ren__wei_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>