String对象的两种创建方式
?
?
关于String有很多问题,首先关于String的创建,有两种创建方式
?
?
String x = "abc";
String z = new String("abc");
?
?
那么这两种方式有什么不同呢
?
?
两者的不同
?
?
其实这两种方式创建的"abc"并不在一个地方,第一句的真正含义是在String池中创建一个对象"abc",然后在栈内创建一个引用x指向池中的对象"abc";第二句实在堆中创建一个对象"abc",然后在栈内创建一个引用z指向堆中的对象"abc"
?
?
首先我们都知道Java中有堆和栈两个重要的内存区域,栈里面放着基本数据类型和对象的引用,而堆里面放着实实在在的对象,由new操作创建的对象肯定是放在堆里的,但是由双引号创建的对象则是放在String池里的,这个String池是常量池的一部分,它不属于堆和栈,它属于方法区(其实Java内存区域除了堆,栈之外还有程序计数器,本地方法栈,方法区这几部分)
?
?
双引号创建方式
?
?
双引号创建方式创建的内容相同的字符串是同一个字符串
?
?
String x = "abc";
String y = "abc";
?
?
Java虚拟机在运行时维护一个String池,池中的String对象是单例的,首先String x = "abc"想创建字符串对象"abc"的时候首先会查看字符串池中是否已经存在,存在就直接返回池中的该String对象,这里不存在,那么就会创建一个新的String对象,并将该对象放进字符串池中,然后再栈中创建引用x,指向它
?
?
在String y = "abc"想创建字符串对象"abc"的时候它首先也会查看字符串池中是否已经存在,由于已经存在,那么就直接返回池中的该String对象,然后再栈中创建引用y,指向它,由于是已经存在的,所以它们俩指向的是同一个对象
?
?
字符串常量池的作用
?
?
字符串池是为了解决字符串重复的问题,生命周期长,它存在于 permgen 中。而且放入字符串常量池实在编译的时候完成的,源文件中出现的双引号内的字符串都被收纳到常量池中。
?
?
几个测试
?
?
有了之前的知识基础我们看看常见的一些测试
?
?
首先x==y肯定是对的
然后x=="abc"肯定也是对的,这里的"abc"也在常量池中
然后z=="abc"是不对的,因为"abc"在常量池中,z在堆中,==判断的是地址
然后x.equals(z)这个是对的,因为String重写了equal方法,会去判断字符串的内容是否相等
但是x==z就是不对的
?
?
String的intern方法
?
?
下面说一下intern()方法,该方法先在String池中查找是否存在一个对象,存在了就返回String池中对象的引用,如果不存在就在池中创建该对象。
那么这样说起来,本来x==z是不对的,但是x==z.intern()就是对的,大家可以测试一下
?
?
String的hashCode方法
?
?
下面再说一下hashCode()方法,这个方法是返回字符串内容的哈希码,那么这样说来,虽然x==z是错的,但x.hashCode==z.hashCode就是对的,因为hashCode是根据字符串内容算出来的,既然内容相同,哈希码必然相同
?
?
String的连接
?
?
String重载了+运算符用于实现字符串的连接,但是由于String是不可更改的,所以+其实是新创建了一个字符串对象
?
?
由于使用+连接字符串每次都生成新的对象,而且是在堆内存上进行,而堆内存速度比较慢(相对而言),那么大量连接字符串时使用+的效率就会比较低。Java提供了StringBuffer和StringBuilder来解决这个问题。
?
?
区别是前者是线程安全的而后者是非线程安全的,StringBuilder在JDK1.5之后才有。不保证安全的StringBuilder有比StringBuffer更高的效率。
?
?
另外有种说法是JDK1.5之后,Java虚拟机执行字符串的+操作时,内部实现也是StringBuilder,之前采用StringBuffer实现,但是每次相加后其实还是都在堆区再新创建了一个对象,只是省去了中间大量连续连接字符串所创建的消耗