乐趣区

字节码底层分析String

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 比较了堆中的地址

退出移动版