String底层解析

基本数据类型由于长度固定,且需要空间比较少,所以直接存储在栈中;而对象比较大,所以栈中只存储一个4btye的引用地址(逻辑地址)。

java中对String对象特殊对待,所以在heap区域分成了两块:

一块是String constant pool,用于存储java字符串常量对象(确切的说应该属于方法区)。
另一块用于存储普通对象及字符串对象。

PS:字符串常量池,应该只是运行时常量池的一块区域。此外整型(只有0-127)也在常量池中。

而string的创建有两种方法:

String a = "abc"; 
String b=new String("abc");

1、String str=“abc”;一个常量池abc对象(str指向)

当代码中使用这种方式创建字符串对象时,JVM 首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。

这种方式可以减少同一个值的字符串对象的重复创建,节约内存。(str 只是一个引用,指向字符串常量池的一个地址)

这个str引用直接指向的是"abc",所以在用eqauls方法比较的时候,比较的是"abc"的地址。

2、String str = new String(“abc”) 创建了两个对象,一个JAVA Heap (堆)中的字符串对象(str指向),一个字符串常量池中的"abc"

首先在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,“abc"将会在常量池中创建;

其次,在调用 new 时,JVM 命令将会调用 String的构造函数,同时value引用常量池中的"abc” 字符串,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象。

请注意这里是直接把original的数组value的引用直接给了new的String的数组value。

即使用 new,对象会创建在堆中,同时赋值的话,会在常量池中创建一个字符串对象,复制到堆中。具体的复制过程是先将常量池中的字符串压入栈中,在使用 String 的构造方法时,会拿到栈中的字符串作为构方法的参数。这个构造函数是一个 char 数组的赋值过程(value被赋值),而不是 new 出来的,所以是String的value引用了常量池中的字符串对象,存在引用关系。javap -c 命令看字节码指令

3.对象中的成员对象赋值

这种情况下会在堆中直接创建,字符串是不会在常量池中创建

public class Location { private String city; private String region; // ....省略set get 方法 public void method3() { Location location = new Location(); location.setCity("深圳"); location.setCity("南山"); } public static void main(String[] args) throws InterruptedException { // 对象外赋值:,city都指向了常量池里的"深圳"的地址 String city = "深圳"; Location location1 = new Location(); location1.setCity("深圳"); System.out.println(city == location1.getCity());//true // 对象内赋值:是在堆上创建字符串 Location location = new Location(); location.method3(); System.out.println(location.getCity() == city);//false } }

4、String str2= "ab"+ "cd"+ "ef";

编程过程中,字符串的拼接很常见。 String 对象是不可变的,如果我们使用 String 对象相加,拼接我们想要的字符串,是不是就会产生多个对象呢?

例如以下代码:String str2= "ab"+ "cd"+ "ef";

分析代码可知:首先会生成 ab 对象,再生成 abcd 对象,最后生成 abcdef 对象,从理论上来说,这段代码是低效的。

编译器自动优化了这行代码,编译后的代码,你会发现编译器自动优化了这行代码,如下

String str= "abcdef";

5、大循环使用

public void mode5() { String str = "abcdef"; for (int i = 0; i < 1000; i++) { str = str + i; } // 编译器同样对这段代码进行了优化。Java在进行字符串拼接时,偏向使用StringBuilder,这样可以提高程序的效率 // String str = "abcdef"; // for (int i = 0; i < 1000; i++){ // str = new StringBuilder(String.valueOf(str)).append(i).toString(); // } }

6、intern()

intern() 方法可以使得所有含相同内容的字符串都共享同一个内存对象。查找常量池里是否有此对象,如果没有创建,有的话返回此对象的引用。

例如:返回"test"在常量池中的地址引用直接赋值给a,b。相当于String a = "test",String b = "test"

String a = new String("test").intern(); String b = new String("test").intern(); System.out.println(a == b);//true

1、new Sting() 会在堆内存中创建一个 a 的 String 对象,"test"将会在常量池中创建

2、在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用赋值给a。

3、调用 new String() 会在堆内存中创建一个 b 的 String 对象。

4、在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用赋值给b。

所以 a 和 b 引用的是同一个对象。

参考:String的Intern方法详解 - 简单爱_wxg - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值