一、String对象的两种赋值方式解析
new String(“abc”) & 直接赋值
1、new String(“abc”)
网上摘录:
系统会先创建一个匿名对象(暂且叫A),值为”abc”,存入堆内存,然后new关键字会在堆内存中又开辟一块新的空间(即创建一个新对象B),然后把值”abc”存进去,并且把B对象的地址返回给栈内存中的str,
此时A对象成为了一个垃圾对象,因为它没有被任何栈中的变量指向,会被GC自动回收。
暂时对这段话有疑问。
目前我的理解是:
首先会去常量池中找有没有一个值为”abc”的对象,如果没有,则新建一个,并且入池,如果有直接返回”abc”对象的引用。
至于”abc”对象与new String对象的关系正在探究中。猜测:new String对象指向”abc”对象
2、直接赋值
如String str = “Hello”; 首先会去常量池中找有没有一个值为”Hello”的对象,如果没有,则新建一个,并且入池,如果有直接返回”Hello”对象的引用。如果还有String对象直接赋值为“Hello”, 则不需要开辟新的堆空间,仍然指向这个池中的”Hello”。
tip:直接赋值,编译器会自动默认调用构造函数new String(char value[])。
扩展:手动入池
可以使用一个java中的手动入池指令,让所创建的对象入池,以后依然可以被重复使用
下面程序中,我们使用了intern()方法,手动入池,所以结果是true.
public class TestString{
public static void main(String args[]){
String str1 = new String("Hello").intern();
String str2 = "Hello";
System.out.println( str1==str2 );
}
}
下面程序中,str2没有使用直接赋值,所以结果又变为false
public class TestString{
public static void main(String args[]){
String str1 = new String("Hello").intern();
String str2 = new String("Hello");
System.out.println( str1==str2 );
}
}
二、String str =new String(“abc”)创建了几个对象
答案 2个。一个是new String(),一个是“abc”。
分析:
该行代码内存分配:
1)str 创建了一个引用,放在栈内存中。
2)new String 放在堆内存中;
3)abc 放在常量池中,常量池在jvm的方法区里面,也有人叫做”永久代”,不属于堆也不属于栈,但是在jdk1.7中已经把常量池移到堆内存中去了,所以这里的“abc”是在堆内存中。jdk1.8将移除永久代。
我们可以把上面这行代码分成四部分来看待:
1)String str,只是定义了一个名为str的String类型的变量,因此它并没有创建对象;
2)=,是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给它,显然也没有创建对象;
3)”abc”,其中一个对象;
4)new String(),另一个对象。
new String(“abc”)为什么又能被看成”abc”和new String()呢?
我们来看一下String的构造器:
public String(String original) { … }
我们使用new创建了一个对象,并将它的引用赋值给了str变量。被调用的构造方法接受的参数也是一个String对象,这个对象正是”abc”。所以String str =new String(“abc”)创建了2个对象,一个是new String(),一个是“abc”。
那“abc”对象是怎么创建的呢?
编译器会自动调用new String(char value[])创建一个对象,这个对象就是值为“abc”的对象。
由此我们又要讨论另一种 创建String对象的方式——引号包含文本(即上面提到的直接赋值)
这种方式是String特有的,并且它与new的方式存在很大区别。
例一:String str=”abc”
创建了一个String对象。
例二:String a=”abc”; String b=”abc”
答案还是一个。
例三:String a=”ab”+”cd”
答案是三个。
分析:String a=”abc”,这行代码被执行的时候,JAVA虚拟机首先在常量池中查找是否已经存在了值为”abc”的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到常量池中,再将它的引用返回。因此,前两个例子只创建了一个String对象。
扩展:
1、在JAVA虚拟机(JVM)中存在着一个常量池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。常量池由String类维护,我们可以调用intern()方法来访问字符串池。
2、只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new对象(包括null)的“+”连接表达式,所产生的新对象都不会被加入字符串池中。
因此我们提倡大家用引号包含文本的方式来创建String对象以提高效率。
3、
栈(stack):主要保存基本类型和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆;
堆(heap):用于存储对象;
堆区:主要存放Java程序运行时创建的所有引用类型【new()生成的对象都放在其中】;
栈区:主要存放Java程序运行时所需的局部变量、方法的参数、对象的引用以及中间运算结果等数据;
代码区:主要存放Java的代码;
数据区:主要存放静态变量及全局变量