上一节主要描述的是String和char的基础简绍以及相关知识。这一节主要介绍String剩下的特点以及StringBuilder和StringBuffer的相关知识与面试题。
Sring类
上一节介绍了两种创建String类的方法,即如下:
//方法一
String str1 = "Hello";
//方法二
String str2 = new String ("Hello");
还有其他构造方式,有需要可以查看官方文档Java™ Platform, Standard Edition 8
API Specification
字符串常量池
直接赋值
我们先看一段代码
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
这段代码的相关内存空间图:
为什么没有开辟新的堆内存空间呢?String类的设计是用来共享设计模式
在jvm底层实际上会自动维护一个对象池(字符串常量池)
- 如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中.
- 如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用
- 如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。
构造方法
采用构造方法时,即String str1 = new String(“Hello”);这样的方式创建相当于在堆上另外开辟空间来存储“Hello”的内容。
这样的做法有两个缺点:
- 如果使用String构造方法就会开辟两块堆内存空间,并且其中一块堆内存将成为垃圾空间(字符串常量 "hello"也是一个匿名对象, 用了一次之后就不再使用了, 就成为垃圾空间, 会被 JVM 自动回收掉).
- 字符串共享问题. 同一个字符串可能会被存储多次, 比较浪费空间
我们可以使用 String 的 intern 方法来手动把 String 对象加入到字符串常量池中
// 该字符串常量并没有保存在对象池之中
String str1 = new String("hello") ;
String str2 = "hello" ;
System.out.println(str1 == str2);
// 执行结果
false
String str1 = new String("hello").intern() ;
String str2 = "hello" ;
System.out.println(str1 == str2);
// 执行结果
true
字符串是不可变的
字符串是一种不可变对象,它的内容不可以改变。
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str);
// 执行结果
hello world!!!
上述代码,内存表现,看起来改变了,实际未改变。
综上所述,String因为是不可变对象,所以在定义好一个String对象之后,String的值就不能改变,如果显示改变,改变的只有指向的链接而已。对于String对象的这一特点,就有了StringBuffer和StringBuilder这两个类,他们可以针对字符串进行修改。
StringBuilder 和 StringBuffer
从上面我们知道这两个类的相对于String最大特点是可以进行对字符串的修改。因为这两个类的功能大部分是相同的,所以我们在相同点上主要以StringBuffer为主介绍。
名称 | 作用 |
---|---|
reverse() | 字符串反转 |
delete(int start,int end) | 删除指定范围的数据 |
insert(int offset ,各种数据类型 b) | 插入数据 |
append(各种数据类型 b ) | 字符串连接 |
StringBuffer 示例使用:
public class Main {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("我的").append("博客");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp) {
temp.append("\n").append("https://blog.csdn.net/qq_44923467");
}
}
StringBuffer与StringBuilder的不同
先看看StringBuffer和StringBuilder的继承关系。从下图我们可以看出两个都继承了AbstractStringBuilder这个抽象类,实现了CharSequence接口。CharSequence接口描述的就是一系列的字符集,所以字符串是字符集的子类。
- StringBuffer的继承关系
- StringBuilder的继承关系
StringBuffer与StringBuilder不同的是StringBuffer中里面的操作方法都用synchornized来进行修饰,所以我们说StringBuffer是安全的。也正因如此,synchronized获取锁释放锁也还是需要时间的,所以StringBuffer适用于并发量并不是很高的场景下。
StringBuilder没有加任何锁,但是效率高,适用于单线程场景,但同时也适用于高并发场景中,提高高并发场景下程序的响应性能。
面试题
- 请解释String类中两种对象实例化的区别
- 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
- 构造方法:会开辟两块堆内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,可以使用intern()方法手工入池。
- 请解释String、StringBuffer、StringBuilder的区别:
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
- StringBuffer与StringBuilder大部分功能是相似的
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder采用异步处理,属于线程不安全操作。