String
特性
- 不可变性:不可变性也就是 如果创建了一个String对象,进行改变那么就是再创建了一个对象,而不是在原有的改变。
- jdk8之前内部存储定义的是char类型数组
- jdk9以后是定义的byte类型数组存储,可以更好的节省了空间。
- 字符串常量池在jdk1.7(含7)以后都是存储在堆空间中的。
字符串常量池
- 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 是在堆中创建了对象,并且在字符串常量池中创建了字面量,
* 第二行:(在创建字面量时 先查看了堆中是否有对象的引用,如果有那么就不会进行创建,之间在字符串 常量池中引用了堆中的地址【节省空间】)
* 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 比较了堆中的地址
}
面试点提问+源码
问如果在字符串拼接的时候一定是用的StringBuilder么?
答:不是 因为final 修饰的不是