java的String类型对象的存储疑惑

流光韶逝 发布于 2014/09/11 21:32
阅读 1K+
收藏 0
 string str ="xx";
 String str = new String("xxx");
     一般情况下,第一句在常量池里创建一个"xxx"对象;第二句先检查在常量池中是否有“xxx”,如果没有创建,有则直接引用,然后再创建一个String
对象。看完一般解释后有以下疑问。
     1.    java的对象放在堆中,那第一句话执行后在堆中有对象吗,那为什么叫创建对象呢? 还是只要开辟了一次空间,就认为了创建了一个对象?
     2.   java的基本数据类型在放在栈中,常量池里放的是基本类型和String的常量,这二者有什么区别。查了网上的资料,感觉很凌乱。
        常量池存放的是在编译期间确定基本类型和String类型的常量,栈中存放的是基本类型数据。但感觉第一个str 不符合常量的命名规则,为什么也放在常量池中呢?
     3.  java对象存储在堆中,对象由基本类型组成,那引用类型对象的基本类型是存储在堆中,还是栈中?
加载中
0
Catelyn
Catelyn

1、第一句执行完其实是在class文件中的常量池里的一个数据结构叫CONSTANT_Utf8_info,这个数据结构有一个item就是字节编码的字符串,相当于写死在class文件中的,运行时由一个叫ldc的指令将此字符串在class对象中的地址作为参数来将此字符串压栈压栈(如果它是局部变量的话),第二则是new对象,不属于编译时期可以确定的值,所以它是在堆中,只不过它的参数是常量所以这里有一层优化会先检查class对象的常量池是否有这个字符串,只要new就会在堆创建对象,而前者的对象则是写死在常量池的,所以二者都是创建对象,就是位置不同

2、基本类型如果是short或者比short小的,是有指令如iconst_1,bipush 100等将一个常量作为立即数来压栈,如果是比short大的数字,则会保存在常量池里,float,double和string常量也是保存在常量池里的,然后由相应指令拿指向常量池的此常量地址作为参数来将这些常量压栈

3、存储在堆中,也可能在perm中(perm在以后可能会移除)

表达可能有问题,如有不对还请指正

流光韶逝
流光韶逝
回复 @惉惉自喜 : 谢谢。
Catelyn
Catelyn
@无计留春住 是的,具体来说是permanent,class文件常驻的内存区域
流光韶逝
流光韶逝
perm是指jvm的内存吗?
流光韶逝
流光韶逝
回答挺好,受教了,非常感谢。
0
Maxwell1987
Maxwell1987

逐项回答楼主的问题:

1. 第一行代码后堆里有一个String对象,至于是不是新的,要看字符串池里有没有这个字符串,至于有没有,后面详细说。在java里面,创建一个对象的概念你可以认为是在堆里分配一段空间,做初始化工作(分配id,各种元数据等),调用构造方法,这些都做完才叫创建一个对象,在java里对象一定有id的。

2. 基本数据类型放在栈里这个提法不是很准确,要分两种情况。第一个如果是作为类或者对象的属性,那就是类定义或者对象的一个组成部分,它们在不同的堆(含PermGen)上。第二个如果是在方法内部的局部变量,那么会跟其它的引用(指引用自身而不是引用指向的对象)一起放在栈上,这种情况下方法执行完返回这个栈上的基本数据类型的变量也就消失了。常量池是一个堆(含PermGen)里面的数据结构,本质上它是个map,key是基本数据类型,value是一个对象。

第一个str在执行时做了这样一个操作:查看字符串池里面有没有"xx"这一项,如果有直接给str赋值对应的对象,如果没有就创建一个对象,放到池里同时也赋值给str。从这个流程也就可以明白,String str = "xx";到底有没有创建对象是不一定的。

同时需要注意的是,java7之后的字符串池也是可以被垃圾回收的,所以未必再次执行String str = "xx";就不会创建对象了。

3. 在堆中,基本数据类型的包装类型,可以简单理解为一个只有一个对应基本数据类型属性的类。所有对象的引用(还是指引用自身)和基本数据类型,都是对象的一部分。只有引用指向的对象,才在对象所拥有的空间之外。

其中的细节不同版本的java表现有所不同,就不细说了,楼主需要了解进一步信息的话,我再拿点代码来讲。这个其实一般用不着了解,只有需要优化的时候才可能会用到。稍微提一点进一步的信息,就是String的intern()方法,这是对于new的对象,会触发一次往字符串池里面扔的操作。但是这个操作很微妙也没有什么显式调用的必要,不去关心它最好。如果面试中级软件工程师及以下的岗位被问到intern(),我敢保证面试官也十有八九不完全了解它。

另外补充的是new String("xxx")的方式至少在目前不会去字符串池里面找,也就是new一定能得到一个新对象(这对任意类型都成立)。而valueOf跟=是一样的(作为基本数据类型会拆箱)。
作为最佳实践,对于String和各种包装类型来说,优先使用=字面值的方式(String str = "xx"; Integer i = 1;),其次是valueOf,如果非常清楚自己需要一个新对象,才是new。

流光韶逝
流光韶逝
感谢maxwell,回答的更符合我当时的疑惑。
返回顶部
顶部