#String
java字符串部分特点(关于String类型的详细定义和描述请读者阅读API手册):

  • 最终类。代表字符串的类,所有的字符串都市String的对对象。
  • 字符串是一个常量,定义好之后不可改变。
  • 因为字符串是一个常量,所以它是被共享的。
  • 字符串本质上是一个字符数组。
public class StringDemo {
	public static void main(String[] args) {
		// s1指向方法区
		String s1 = "ab";
		// s2指向堆内存,堆内存指向方法区
		String s2 = new String("ab");
		// 字符串在方法区只存放一份
		// 后续使用到值相等的字符串的时候使用的是同一个
		String s3 = "ab";
		System.out.println(s1 == s2);
		System.out.println(s1 == s3);
	}
}

上一段代码中,输出的结果为false,true。
这是为什么呢?希望通过下面两个例子,读者能够真正理解其原理。

#示例1

如图中所示:

java 存储emoji java 存储字符串_java


在这个我们定义了一个字符串str,它的数据为abc。

但是我们应该深入理解其存储过程,而不是简单的看到其字面含义。

首先,从图中,我们可以看到,整个存储过程涉及到栈、堆、方法区。其中,方法区存储的是静态类型、类、常量、字面量等信息;栈存储方法,变量,基本数据类型等;而堆则存储引用类型的数据(解释比较表面,具体可以另行参考)。

对于String Str = “abc”,首先在栈内存中开辟空间,存入变量str(非静态、非常量),然后在准备变量赋值的时候,系统发现值为字符串,其本质是字符数组,因此会在堆内存开辟空间进行存储,假设地址为0x03d1,而字符数组在底层是用常量(final)标记的,所以系统就会在方法区中的动态常量池中开辟一个空间,数组名字叫做value,假设地址为0x457f,然后在空间中存入前面字符数组的地址,其值为0x03d1(这个时候value指向字符数组);下一步就将value的值赋值给str,即str指向的是常量池,而不是堆内存的地址空间,且常量池中的value执行的是堆。

对于 str = “def”,同样的,会在堆中开辟新的空间,假设地址为0xb3f2,存的是“def”字符数组,因为字符数组是常量,所以在方法区运行常量池中开辟新空间,假设地址为0x998c,名字叫做value,记录的是字符数组的地址0xb3f2(即指向字符数组);下一步,将value的值赋值给str,即将原来的值覆盖;此时,str记录的是一个新的地址,间接指向了一个新的方法区地址,这个新地址几率的则是"def"在堆中的地址。综上,我们所说的字符串是常量不可变,指的是字符串本身(值)没有改变,而是字符串变量指向的地址发生了改变。

以上就是字符串赋值在内存存储的变化过程。有错误的地方,请指正,谢谢。

#示例2

java 存储emoji java 存储字符串_存储_02


对于String str2 = new String(“abc”);

我们知道,首先会在占中开辟一个空间,存储str2;然后在创建对象的过程,会在堆内存中,开辟一个空间,假设地址为0x4ef2,将地址赋值给str2,即str2指向了地址为0x4ef2的堆内存中的空间;在堆内存中写入数据的时候,发现其实字符串(字符串是常量,本质是字符数组),这个时候就会去方法区中运行时常量池中寻找是否有"abc"这个字符串的地址引用,当这个地址引用存在的时候,就会将value:0x03d1这个地址写入堆内存中,从而实现了str2间接指向堆内存中的"abc"的过程。

但是在这里,str记录的是方法区中的地址,而str2记录的是堆内存中的地址,因此,这里就解释了System.out.println(s1 == s2);输出为false的原理。

对于System.out.println(s1 == s3);输出为true;因为只要值相同,用的都是同一个字符串,也就意味着是s1和s3用的是同一个"ab"。即在堆中存入字符串之前,会去方法区中寻找是否有"abc"这个字符串的地址引用,当这个地址引用存在的时候,就会将value:0x03d1赋值给s3。

另外的,这里阐述一个核心概念,字符串是共享的,是因为其值在堆内存中只会保存一份,每当我们向堆中存入字符串的时候,都没检查运行时常量池中是否有这个字符串的地址引用,如果没有,就向堆中存入这个字符串;如果有,堆中存入的则是这个字符串在运行常量池的地址引用的地址,从而达到了一个数组只保存一份,实现共享的需求。
其次,这里的str类型为String,它对应的是一个赋值指向过程,代表的是数据类型,而str2的类型虽然也是String,但是它对应的是一个对象创建并指向的过程,即str2必须指向对象,String代表的是对象类型,这里有本质的区别。
以上阐述过程较为粗糙,请见谅!也谢谢读者的阅读。