转载

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

Java里面的String类有些特殊,让很多程序员会感到一丝困惑。

看起来像基本类型,但又不是基本类型。

初始化一个String类型的变量有两种方式。

方式一, String str1 = "java" ;

方式二, String str2 = new String("java") ;

方式一跟基本类型的创建方式一样,比如 int i = 3 ;这是大家经常把它当作基本类型的原因,还有另一个原因就是String 变量之间的 + 操作。稍后会提到。

第一种方式,常量字符串初始化对象,它的值存放在字符串常量池中。

第二种方式,构造方法创建字符串,它的值存在堆中。

正是因为String对象存在两种不同存放对象的方式,才会让string对象显的如此特别。

例子1

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

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

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

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

String str1 = "java"
changeStr(str1);
//print java
System.out.println(str1);
public static void changeStr(String str){
    str = "python";
}

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

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

例子6

接第一个例子

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

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

总结:String 对象之所以变现的如此特殊,让很多程序员措手不及,最主要的是因为JVM有两个地方存放字符串对象,一个是堆内存,一个是字符串常量池,还有一个特殊之处就是String 是不可变对象。字符串常量池给我们带来这么多困惑,一定也有它存在的意义,它带来的好处就是

节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。

节省运行时间:比较字符串时, ==equals() 快。对于两个引用变量,只用 == 判断引用是否相等,也就可以判断实际值是否相等。

原文  https://hellofrank.github.io/2019/10/13/Java里面的String对象到底神奇在什么地方/
正文到此结束
Loading...