String
特性
不可变性:不可变性也就是 如果创建了一个 String 对象,进行改变那么就是再创建了一个对象,而不是在原有的改变。
jdk8 之前内部存储定义的是 char 类型数组
jdk9 以后是定义的 byte 类型数组存储,可以更好的节省了空间。
字符串常量池在 jdk1.8(含 8)以后都是存储在堆空间中的。
字符串常量池
String pool 底层也就是 HashTable
所以字符串常量池是不可重复的
如 String s =“abc”s2 =“abc”. 在栈中其实引用的是同一个地址(编译优化)
jdk1.6 以前 HashTable 长度默认值 1009,jdk7 默认为 60013,jdk8 以后设置长度最低要求 1009
参数设置
-XX:StringTableSize=*
链表短的话会影响效率
实战
可编译看字节码,一下都是基于 jdk1.8 以后进行说明
声明:1. 字面量创建 String 那么会在字符串常量池中添加一个空间,但是堆空间不会有地址;
声明:2.new 方式创建 String 那么会在字符串常量池中添加一个空间,并且在堆中开辟一个空间,并且引用堆空间的地址
声明:3. 两个字面量拼接,那么会编译阶段就会进行编译优化如 String c = “a”+”b”; 编译期间会默认为 String c = “ab”, 不会再字符串常量池中分别开辟空间
声明:4. 在 String c = new String(“a”)+new String(“b”) 相加是不会在字符串常量池中开辟新的空间的,c 只是引用
public void test(){
String s = “javaHeap”;
String s1 = “java”;
String s2 = “heap”;
String s3 = s1 + s2;
/**
* 因为编译时 s1 s2 相当于在字符串常量池中创建对象,不知道其中的值,需要引用地址
* 当赋值时,那么底层会使用 StringBuilder 的 append 方法进行拼接,是引用了两个地址,进行赋值, 组成新的对象, 并且字符串常量池中是没有 ”a、javaHeap”
*/
boolean result = s == s3; //false
}
public void test2(){
String s = “javaHeap”;
final String s1 = “java”;
final String s2 = “heap”;
String s3 = s1 + s2;
/**
* final 在编译期就已经确定下来了值,在进行字符串拼接时会进行 编译优化 String s3 = “javaHeap”
*/
boolean result = s == s3; //true
}
public static void test3(){
String s = new String(“javaHeap”);
/**
* 在内存中创建了 5 个对象 细致的说 6 个
* 1.new 在堆中
* 2.“字符串”在字符串常量池中
* 3. + 变量拼接 创建了 StringBuilder
* 外加:StringBuilder 中的 toString 方法,底层也就是 new String(); 但是不会在常量池中创建
*/
String s2 = new String(“java”) + new String(“Heap”);
}
public static void test4(){
/**
* 字面量和 new 的区别
* 1. 字面量会在字符串常量池中 放入,new 对象也会在常量池中放入 ”javaHeap”
* 2. 字面量不会在堆中开辟空间,而 new 会在堆中开辟空间
*/
String b = new String(“javaHeap”);// 返回的是堆空间中的地址
String a = “javaHeap”;
System.out.println(a==b);//false
}
intern 方法
简介
Intern 方法:查看字符串常量池中是否有,如果有,那么返回字符串常量池中的引用,如果没有将会查看堆中是否有数据,有将会引用对象地址,没有那么创建字符串,放入常量池。
为什么使用 intern?
: 降低内存的大小,提高的运行的速度,会自动释放内存,会引用常量池中的数据
实战详解
public static void test(){
/**
* 内存结构详解:
* new String 是在堆中创建了对象,并且在字符串常量池中创建了字面量,MT5 www.gendan5.com/mt5.html
* 第二行:(在创建字面量时 先查看了堆中是否有对象的引用,如果有那么就不会进行创建,之间在字符串 常量池中引用了堆中的地址【节省空间】)
* String s = “javaHeap”;
* 字面量:只会在常量池中 创建常量。
*
* 所以在比较时 new 在常量池是对象引用地址,而字面量是值(或者说地址)所以 false
*/
String s2 = new String(“javaHeap”);
s2.intern();
String s = “javaHeap”;
System.out.println(s==s2);//false
}
public static void test2(){
/**
* 内存结构详解:
* 这里在进行第一行代码时 结构(创建了 6 个对象,最后在堆中生成了对象 s =”javaHeap” 变量池中没有生成)
* 第二行:在变量池中生成
* 第三行:直接引用变量池
*
*/
String s = new String(“java”) + new String(“Heap”);
s.intern();
String s2 = “javaHeap”;
System.out.println(s==s2);//true
}
//————————————————————————————————-
public static void main(String[] args) {
String str2 = new String(“xin”) + new String(“cen”);
//String str2 = new String(“a”);
String intern = str2.intern();
String str = “xincen”;
System.out.println(intern==str);//true 比较串常量池中的地址
System.out.println(str2==str);//false 比较了堆中的地址