Java基础——String
更新: 5/9/2025 字数: 0 字 时长: 0 分钟
String是Java基础类型中较为特殊的一个类,我们这里着重探讨一下这个类。
String
String底层使用一个char类型的数组来存储字符串,且该数组使用了private和final修饰
为什么说String是不可变的
- final与private修饰该字符数组且未暴露修改方法,杜绝了从外部修改该对象的可能性
- 由于final的使用导致子类无法继承该对象,进而导致子类不可能修改该数组
因而String是不可变的
线程安全性
由于不可变,进而可以理解为常量,因此所以线程安全
StringBuffer与StringBuilder
StringBuffer与StringBuilder均继承自AbstractStringBuilder(构造器模式),因此我们先来介绍一下AbstractStringBuilder类。
AbstractStringBuilder类中使用一个char类型的数组来存储字符串,同时为我们提供了大量字符串修改操作(insert,append....)
线程安全性
- StringBuffer使用了锁机制,因此线程安全。
- StringBuilder由于未使用锁机制导致线程不安全。
性能
StringBuilder的效率高于StringBuffer。
比较String,StringBuilder,StringBuffer
- 常量使用String
- 修改+多线程使用StringBuffer
- 修改+单线程使用StringBuilder
Java9的改动
Java9将上面三个类的底层char数组改成了byte数组
修改的底层
String的+与+=是Java唯二两个重载的运算符,我们通过观察字节码可以发现String的+和+=是创建了一个StringBuilder类来完成这一操作。
但是在for循环中,字节码无法优化成自创建一个StringBuilder类(即for每进行一次循环就要新创建一个StringBuilder),因此我们仍然建议存在大量修改的情况下使用StringBuilder或StringBuffer
字符串常量池
JVM为了提升性能减少内存消耗而专门为字符串开辟了一块区域,我们称呼为字符串常量池。
- 直接使用双引号声明出来的String对象会直接存储在常量池中。
- 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
String s1 = "Java";
String s2 = "Java";
s1赋值时,JVM会先看常量池中是否有这个对象,如果没有则创建该对象在常量池中,并返回常量池中的这个对象。
s2赋值时,JVM发现常量池中已经有这个对象,因此直接常量池中这个对象的引用赋值给s2。
因此s1==s2
构造方法和等于赋值的区别
使用构造方法创建字符串变量时,一般不会和常量池有关系。
但特别的,如果使用了以下形式创建字符串
String a=new String("123");
由于使用了双引号变量,因此会使用常量池。
一:
- 若双引号变量不存在,则会创建该变量到常量池中并返回他的引用
- 若双引号变量存在,则直接返回该变量的引用
二:
使用从常量池中得到的引用去初始化一个新的对象并放到堆中(浅拷贝)
intern方法
intern方法的意义是通过调用该方法的String变量的值去获取常量池中该与该变量值相同的对象并将其引用返回。
这又分为以下两种情况
- 若常量池中没有该与该变量值相同的对象,则将该变量丢入常量池中,并返回该变量的引用(与直接使用等于逻辑一致)
- 若常量池中存在与该变量值相同的对象,则直接返回常量池中对象的引用
注意:使用intern的过程并不会改变调用intern方法的对象
intern的意义
说了这么多,我们究竟会在什么场景使用intern呢?
我们这里带来一段fastjson的源码
public Entry(char[] ch, int offset, int length, int hash, Entry next){
characters = new char[length];
System.arraycopy(ch, offset, characters, 0, length);
symbol = new String(characters).intern(); //核心
this.next = next;
this.hashCode = hash;
this.bytes = null;
}
通过使用intern方法,将字符串缓存到了字符串常量池中,同时给到变量的是常量池中的对象而不是构造方法创建的对象,
这样一来后续凡是使用到逻辑相同的变量均是常量池中的同一变量,因此加快了速度。