前言
String这种东西天天都在用,但是还真的没有专门研究过,只是之前简单看过一些介绍。今天专门研究一下,加深一些印象,这些基础知识都是JAVA程序员(非码农)必须应该具备的硬实力。
实验代码
package com.java.example.demo.string;
/**
* 练习String知识点
*/
public class StringDemo {
public static void main(String[] args) {
String str1 = "zhaoxiaoluo";
String str2 = "zhaoxiaoluo";
String str3 = "zhao" + "xiaoluo";
String temp = "xiaoluo";
String str4 = "zhao" + temp;
String str5 = new String("zhaoxiaoluo");
String str6 = new String("zhaoxiaoluo");
String str7 = str6.intern();
// == 比较是否是同一个引用
System.out.println("str1 str2:" + (str1 == str2));
System.out.println("str1 str3:" + (str1 == str3));
System.out.println("str1 str4:" + (str1 == str4));
System.out.println("str1 str5:" + (str1 == str5));
System.out.println("str1 str7:" + (str1 == str7));
System.out.println("str5 str6:" + (str5 == str6));
System.out.println("str5 str7:" + (str5 == str7));
//使用equals比较值
System.out.println("下面是equals:");
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
System.out.println(str1.equals(str4));
System.out.println(str1.equals(str5));
System.out.println(str1.equals(str7));
System.out.println(str5.equals(str6));
System.out.println(str5.equals(str7));
}
}
执行结果
str1 str2:true
str1 str3:true
str1 str4:false
str1 str5:false
str1 str7:true
str5 str6:false
str5 str7:false
下面是equals:
true
true
true
true
true
true
true
理论知识补充
字符串的分配,和其他对象分配一样会耗费大量时间和空间代价。JVM为了提升性能和减少消耗,在实例化字符串常量时进行了一些优化。由于字符串的不可变性,为了减少在JVM中创建字符串的数量,Java维护了一个字符串常量池。每当有字符串常量被创建时,JVM会首先检查字符串常量池中是否已存在该字符串。若存在,返回该对象的引用。若不存在,实例化该字符串放在常量池中。
Java的堆内存是专门用来存放对象的,若使用new String这种形式,会生成一个新的String对象,强制存放在了堆内存中,就不会去和常量池作比较,所以引用地址是不一致的。
intern()方法是强制返回常量池中的对象引用,若常量池中已存在,则返回对应常量池中的引用,若不存在,实例化后放入常量池,返回该引用。
解析版代码
package com.java.example.demo.string;
/**
* 练习String知识点(解析版)
*/
public class StringDemo2 {
public static void main(String[] args) {
//1、去常量池中查询,没有“zhaoxiaoluo”,实例化并放入常量池
String str1 = "zhaoxiaoluo";
//2、去常量池中查询,有“zhaoxiaoluo”,直接返回该引用,所以此处和str1指向的是同一个常量池地址
String str2 = "zhaoxiaoluo";
//3、编译期会将 两个常量拼接在一起,然后去常量池中查询“zhaoxiaoluo”,同str2
String str3 = "zhao" + "xiaoluo";
//4、由于涉及了变量,运行时才会拼接在一起(非编译期),其内部实现是先new一个StringBuild,
//stringBuild.append("zhao").append(temp).toString();所以是返回了一个新的对象
String temp = "xiaoluo";
String str4 = "zhao" + temp;
//5、使用 new String创建了一个 String对象,所以强制放在了堆内存中(堆内存放对象实例),
// 没有和常量池比对,也没有放在常量池,所以和str1不是一个地址
String str5 = new String("zhaoxiaoluo");
//6、同str5,会存放在堆内存中,堆内存中每次都是新增对象(无论值是否相等)
String str6 = new String("zhaoxiaoluo");
//7、intern()方法的作用就是,返回一个字符串常量池中的引用,
// 若常量池中存在该字符串,返回引用,否则,在常量池中新增该字符串
String str7 = str6.intern();
// == 比较是否是同一个引用
System.out.println("str1 str2:" + (str1 == str2));
System.out.println("str1 str3:" + (str1 == str3));
System.out.println("str1 str4:" + (str1 == str4));
System.out.println("str1 str5:" + (str1 == str5));
System.out.println("str1 str7:" + (str1 == str7));
System.out.println("str5 str6:" + (str5 == str6));
System.out.println("str5 str7:" + (str5 == str7));
//如果用equals比较值的话,因为所有的值都是“zhaoxiaoluo”,所以都是相等的
System.out.println("下面是equals:");
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
System.out.println(str1.equals(str4));
System.out.println(str1.equals(str5));
System.out.println(str1.equals(str7));
System.out.println(str5.equals(str6));
System.out.println(str5.equals(str7));
}
}