Java里面的String对象到底神奇在什么地方

Java里面的String类有些特殊,让很多程序员会感到一丝困惑。
看起来像基本类型,但又不是基本类型。

初始化一个String类型的变量有两种方式。
方式一,String str1 = "java";
方式二,String str2 = new String("java");
方式一跟基本类型的创建方式一样,比如 int i = 3;这是大家经常把它当作基本类型的原因,还有另一个原因就是String 变量之间的+操作。稍后会提到。
第一种方式,常量字符串初始化对象,它的值存放在字符串常量池中。
第二种方式,构造方法创建字符串,它的值存在堆中。
正是因为String对象存在两种不同存放对象的方式,才会让string对象显的如此特别。

例子1

1
2
3
4
5
6
7
8
9
10
String str1 = "java";
String str2 = "java";
String str3 = new String("java");
String str4 = new String("java");
//print true
System.out.println(str1 == str2);
//print false
System.out.println(str3 == str4);
//print false
System.out.println(str1 == str3);

str1str2 指向同一个内存区域,因为“java”存在常量池中。当jvm发现常量池中存在”java”时,就会把str2直接指向’java’不会在新创建。所以返回true.
str3str4 是在内存堆中分别创建了两个不同的对象。所以返回false; str1str3也返回false,因为一个在字符串常量区,一个在堆内存。

例子2

1
2
3
4
5
6
7
8
9
10
String str = "javajava";
String str5 = str1 + str2;
String str6 = "java" + str1;
String str7 = "java" + "java";
//print false
System.out.println(str5 == str);
//print false
System.out.println(str6 == str);
//print true
System.out.println(str7 == str);

String 对象就是这么神奇,支持像基本类型一样的+操作,这是java给我们的语法糖。这样也经常让大家误认为它是基本类型。
当对两个String变量的引用进行操作时,无论他们时通过何种方式赋值,都会在堆内存中开辟出一个新的空间存储操作结果。所以str5 == str返回false.
当字符常量和引用类型操作时,一样会在堆内存中进行操作。所以str6 == str 返回false.
当两个字符常量进行操作时,就可以直接在字符串常量区操作。操作结果如何在常量池已经存在则直接将值赋给引用变量。所以str7 == str返回true.

例子3

1
2
3
4
5
6
public static final String FINAL_STR1  = "maven_";
public static final String FINAL_STR2 = "jdk";
String finalStr = FINAL_STR1 + FINAL_STR2;
String finalStr3 = "maven_jdk";
//print true
System.out.println(finalStr == finalStr3);

FINAL_STR1FINAL_STR2都是用static final修饰的,在编译期会编译进字符串常量池,因此finalStr也是在编译阶段就编译进了字符串常量池。所以返回true

例子4

1
2
3
4
5
6
7
8
9
10
public static final String FINAL_STR1;
public static final String FINAL_STR2;
static {
FINAL_STR1 = "hdfs_";
FINAL_STR2 = "hive";
}
String finalStr = FINAL_STR1 + FINAL_STR2;
String finalStr4 = "hdfs_hive";
//print false
System.out.println(finalStr == finalStr4);

FINAL_STR1FINAL_STR2 在声明的时候没有进行初始化,所以编译阶段不能确定值,所以没有编译进字符常量池。所以返回false

例子5

1
2
3
4
5
6
7
String str1 = "java"
changeStr(str1);
//print java
System.out.println(str1);
public static void changeStr(String str){
str = "python";
}

String 明明是引用传递,为什么表现出值传递的效果呢?因为String是不可变的。上面的函数调用过程可以理解为

1
2
3
String str1 = "java"; 
String str = str1; // str2----->str1, 传引用
str = "python"; // str2指向了一个新的字符串,str1并没有变。

例子6
接第一个例子

1
2
3
String str8 = str3.intern();
//print true
System.out.println(str1 == str8);

这个intern()方法看起来有点儿神奇。String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。它可以手动将字符对象的值转移到字符串常量池中,这样str1str8就指向同样的内存地址了。

总结:String 对象之所以变现的如此特殊,让很多程序员措手不及,最主要的是因为JVM有两个地方存放字符串对象,一个是堆内存,一个是字符串常量池,还有一个特殊之处就是String 是不可变对象。字符串常量池给我们带来这么多困惑,一定也有它存在的意义,它带来的好处就是
节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
节省运行时间:比较字符串时,==equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

-------------本文结束-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%