首先明确各个存储位置的作用
◆寄存器:我们在程序中无法控制
◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中(new 出来的对象)
◆堆:存放用new产生的数据
◆静态域:存放在对象中用static定义的静态成员
◆常量池:存放常量
◆非RAM存储:硬盘等永久存储空间
一、通过直接赋值方式创建对象和通过构造方法创建字符串对象的区别
public class TestString {
public static void main(String[] args) {
String str1 = "hello";
String str2 = new String("hello");
String str3 = str2; //引用传递,str3直接指向st2的堆内存地址
String str4 = "hello";
/**
* ==:
* 基本数据类型:比较的是基本数据类型的值是否相同
* 引用数据类型:比较的是引用数据类型的地址值是否相同
* 所以在这里的话:String类对象==比较,比较的是地址,而不是内容
*/
System.out.println(str1==str2);//false
System.out.println(str1==str3);//false
System.out.println(str3==str2);//true
System.out.println(str1==str4);//true
}
}
区别:
1.在字符串中,如果采用直接赋值的方式(String str=“hello”)进行对象的实例化,则会将匿名对象“hello”放入常量池,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象。
2.通过构造方法String str= new String(“hello”):JVM首先在字符串池中查找有没有"hello"这个字符串匿名对象,如果有,则不在池中再去创建"hello"这个对象了,直接在堆中创建一个"hello"字符串对象,然后将堆中的这个"hello"对象的地址返回赋给引用str3,这样,str3就指向了堆中创建的这个"hello"字符串对象;如果没有,则首先在字符串池中创建一个"hello"字符串对象,然后再在堆中创建一个"hello"字符串对象,然后将堆中这个"hello"字符串对象的地址返回赋给str3引用。而且新产生的hello不能够自动入池,需要通过public String intern()方法进行手工入池。
在开发的过程中不会采用构造方法进行字符串的实例化。
二、intern()方法
在调用”hello”.intern()方法的时候会返回”hello”,但是这个方法会首先检查字符串池中是否有”hello”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然后返回这个字符串的引用。
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
System.out.println(str5.equals(str3)); //true
System.out.println(str5 == str3); //false
System.out.println(str5.intern() == str3); //true
System.out.println(str5.intern() == str4); //false
- 第一、str5.equals(str3)这个结果为true,不用太多的解释,因为字符串的值的内容相同。
- 第二、str5 == str3对比的是对象的地址是否相同,由于str5采用new String方式定义的,所以对象地址一定不相等。所以结果为false。
- 第三、当str5调用intern的时候,会检查字符串池中是否含有该字符串。由于之前定义的str3已经进入字符串池中,所以会得到的引用和str3指向同一对象。
- 第四,当str4 = str1 + str2后,str4的值也为”ab”,但是为什么这个结果会是false呢?先看下面代码:
String a = new String("ab");
String b = new String("ab");
String c = "ab";
String d = "a" + "b";
String e = "b";
String f = "a" + e;
System.out.println(b.intern() == a); //false
System.out.println(b.intern() == c); //true
System.out.println(b.intern() == d); //true
System.out.println(b.intern() == f); //false
System.out.println(b.intern() == a.intern());//true
如d所示,字符串相加的时候,静态字符串的相加的结果会添加到字符串池,所以返回的引用d会指向字符串池中的字符串"ab"。
如果其中含有变量(如f中的e)则返回的引用不会指向字符串池。
三、误区
String str="hello";
str="hi";
str是一个String对象的引用(引用数据类型),而不是对象的本身。变的是str的指向,而不是原来的内容。(这里可以类比C++,str是一个指向String类型的指针)