一、基本特性
1. 不可变性
String 类:
- 类被final修饰,不能被继承
- 类中所有的属性被final修饰,不能去修改
1. 当对字符串重新赋值时,需要重写制定内存区域赋值,不能使用原有的value进行赋值
2. 当对现有的字符串进行连接操作时,需要重新制定内存区域赋值,不能使用原有的value进行赋值
3. 当调用String 的replace方法修改制定字符或字符串时,需要重新制定内存区域赋值,不能使用原有的value进行赋值
- 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
- 字符串常量池中,不会存放两个相同的元素
package com.erick.string.d01;
import org.junit.Test;
public class Demo01 {
@Test
public void method01() {
String first = "erick";
String second = "erick";
System.out.println(first == second); // 指向同一个引用地址
second = "hehe";
System.out.println(first == second); // 引用地址已经变化
}
@Test
public void method02() {
String first = "Erick";
String second = "Erick";
second += "nihao";
System.out.println(first==second);// 已经重新分配了一个新的数组
}
@Test
public void method03(){
String first = "Erick";
String second = first.replace("E", "a");
// 上面其实是生成了一个新的byte[]来存储字符串
System.out.println(first==second);
}
}
2. String底层HashTable
String 的 String Pool 是一个固定大小的HashTable, 默认值大小长度是1009
- HashTable底层: 数组加链表的结构
- 这里说的大小长度指的是 数组长度,而不是总node的数量, 总node的数量可以无限存储
- 不过数组每个索引位置都存储元素的时候,查询起来就会慢 , 因为需要链表的遍历
- 如果放进去的String很多,就会造成Hash冲突严重,从而导致链表很长
链表如果很长,查询时候比较慢
-XX:StringTableSize=20000 可以设置StringTable的长度
- jdk6中StringTable是固定的,如果常量池中的字符串过多就会导致效率下降很快,最小值随便设置
- jdk7中,StringTable的长度默认是60013,
- jdk8时,1009是可以设置的最小值
- jdk11 : 默认长度为 65536
3. String 的内存分配
- Java包含八种基本数据类型和一种比较特殊的类型String
- 为了使得这些数据类型在运行过程中速度更快,更节省内存,都提供了一种常量池的概念
- 常量池就类似一个Java系统级别提供的缓存,八种基本数据类型的常量都是系统协调的
- String类型的常量池比较特殊,它的主要使用方法有两种
1. 直接使用“”声明的String对象会直接存储在常量池中
2. 不是“”声明的String对象,可以使用String提供的 intern()方法
4. String操作
4.1 字符串唯一性
- 一个字符串已经出现了,则后续如果字符串的字面量和上面相同,则直接去常量池中取
- 常量池中,不会保存两个完全相同的两个字符串
4.2 toString()
二、字符串拼接
1. 前端编译期间优化
@Test
public void test01() {
String first = "a" + "b" + "c";
String second = "abc";
System.out.println(first == second); // true
System.out.println(first.equals(second)); // true
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.erick.string.d01;
import org.junit.Test;
public class Demo02 {
public Demo02() {
}
@Test
public void test01() {
String first = "abc";
String second = "abc";
System.out.println(first == second);
System.out.println(first.equals(second));
}
}
2. 拼接前后出现了变量
- 如果拼接符号的前后出现了变量,则相当于在堆空间中new String()
- 既然new了对象,那就是不同的对象,因此也就是不同的地址值
- 对象的具体的内容是拼接的结果
3. string.intern()
- 判断字符串常量池中是否存在“javaeehadfd”的值
- 如果存在,则返回该常量池中该值的地址
- 如果不存在,则在常量池中加载一份,并返回该对象的地址值