关于String 的创建与内存存储
字符串常量池(String Pool,也称 String Table),在 JDK1.7 之前存放在方法区内,在 JDK1.7 及之后版本,字符串常量池的位置更改到了堆内存中。
String的创建方式:
- 构造器:通过new的方式创建
- 字面量:通过这种方式创建:String a = “字面量方式创建的字符串”;
但是无论哪种方式创建,其实都是使用一个char数组进行存储。
虽然存储方式都是char数组,但是由于创建方式不同,在细节上也会有所不同。
构造器方式创建:
使用构造器创建字符串对象的过程和创建其他对象完全相同,就是直接在堆中创建一个对象,然后将该对象的引用返回,变量就会存储该变量的引用,即对象的内存地址。
但是需要注意,使用已有字符串对象创建时,新字符串对象的value熟悉和已有字符串对象的value属性是相同的,即执行同一个char数组。
字面量
通过字面量创建字符串时,需要考虑字符串常量池。
字符串常量池位于堆中(只针对1.7以后版本),是一个由C++实现的HashTable,键是由字符串字面量及长度计算得到的Hash值,值为字符串对象的直接引用。
借助字符串常量池,可以减少字面量相同的字符串对象的创建,节省内存空间
使用字面量创建字符串对象时,会首先检查字符串常量池中有没有相同的键,如果有则直接返回,如果没有,则再堆中新建一个字符串对象,将其相关的Hash值与直接引用存入字符串常量池中。
字面量特点
- 非对象
严格的数,字面量在代码运行到它所在语句前,它还不是字符串对象
- 懒加载
当第一次使用到字面量的时候,才会创建对应的字符串对象。 - 不重复
同一个类中的值相同的字面量,其实只有一份,因为【类文件常量池】包括【运行时常量池】都是以类为单位的,所以不同类中相同字面量在内存中会存有两份,但是由于字符串常量池的存在,只要字面量相同,字符串对象也相同。
构造器+字面量
当同事使用构造器+字面量创建字符串时,会同事在堆中创建两个字符串对象,其中一个是根据字面量创建的,而另一个是通过构造器创建的,两个对象是不同的。
String s4 = "def";
String s5 = new String("def");
System.out.println(s4 == s5); // false
拼接
字面量拼接
使用字面量拼接时,编译时会进行优化,实际上是使用字面量进行构造。
String s6 = "abcedf";
String s7 = "abc" + "edf"; // 实质上就是 String s7 = "abcedf";
System.out.println(s6 == s7); // true
引用拼接
使用引用拼接时,实际上是新建了一个StringBuilder对象,调用append方法进行拼接,最后嗲偶哦那个toString方法创建一个新的String对象
String s8 = "123456";
String s9 = "123";
String s10 = s9 + "456";
System.out.println(s8 == s10); // false
但是,如果引用的变量使用了final修饰,则又相当于字面量拼接。
String s8 = "123456";
final String s9 = "123";
String s10 = s9 + "456";
System.out.println(s8 == s10); // true
intern()
使用intern()方法可以查询字符串常量池中是否存在字面量相同的字符串。
- 如果存在,则返回此字符串的对象引用;
- 如果不存在,则将此对象的引用放入字符串常量池中,并返回此引用(始终只有一个对象)。
String x = new String(new char[]{'a', 'b', 'c'});
String y = "abc"; // 通过字面量构造,"abc"对应对象的引用加入 String Table
String z = x.intern(); // 已有,返回 String Table 中 "abc" 对应对象的引用
System.out.println(z == y); // true
System.out.println(z == x); // false
参考博客:https://blog.youkuaiyun.com/x326279579/article/details/132208682