1.String两种实例化区别
首先,我们来看最常见的,也是比较推荐的实例化方法:
String name="ZhangSan";//实例化String对象。这是最直接,最常见的实例化方法,直接赋值的方式
当然,另一种方法也是我们实例化普通对象时最常见的,使用String类构造器实例化:
String name=new String("ZhangSan");
二者区别:我们首先需要知道,每一个字符串,其实就是一个String类的匿名对象,匿名对象也就是已经开辟了堆内存的空间而且可以直接使用的对象。(比如:System.out.println("name".equals(name));,结果为true,这里的字符串"name"本身就是一个匿名对象,所以 可以调用equals方法)
1.1 对于String name="ZhangSan",实际上就是把一个在堆内存中开辟好了的空间的使用权交给了name这个对象,也就是 相当于将"ZhangSan"这个对象的地址,给了name,相当于c语言中的指针引用。
这种方式有个好处就是,当一个字符串已经被一个名称所引用,则以后再有相同的字符串声明的时候,不会再重新开辟空间,如下:
//声明三个字符串变量,每个变量内容都是一样的
String name1="ZhangSan";
String name2="ZhangSan";
String name3="ZhangSan";
System.out.println("比较结果:"+(name1==name2)+(name1==name3)+(name2==name3) ); 结果是true、true、true。补充一点,字符串比较如果使用“==”,则实际上比较的是其内存地址数值,也就是其地址。说明,虽然表面上声明了三次,但是name1、name2、name3都指向了字符串"ZhangSan",如图:
这个现象涉及到JVM底层实现,String类所采用的设计模式为共享设计模式。在JVM底层实际上存在一个对象池(不一定只保存String对象),当代码中使用了直接赋值的方式定义了一个String类对象时,会将此字符串对象所使用的匿名对象入池保存,而后如果还有其他String类对象也采用直接赋值的方式,并且设置同样内容的时候,那么不会新开辟堆内存空间,而是使用已有的对象进行引用分配。
1.2 对于String name=new String("ZhangSan"); 前边我们说过一个字符串就是一个匿名对象,如果使用new关键字的话,不管如何都会再开辟一个新的空间,但此时空间内容还是"ZhangSan"。所以使用这个方法,实际上是开辟了两个内存空间,但真正使用的只是一个使用关键字new开辟的空间,另一个是垃圾空间,如图:
二者虽然有所区别,但是在实际开发中,大多数情况一定是采用法一,也就是直接赋值的方式。
2.需要注意的是:字符串的内容不可改变。
看如下代码:
String str="hello";//声明字符串
str=str+"world!";//修改字符串
System.out.println(str);
运行结果是:hello world!看似这里的字符串发生了改变,其实,内容并不是真的修改了,一个String对象内容的改变实际上是通过内存地址的“断开-连接”变化完成的,而字符串本身内容并没有变化。如图:
所以,在日后开发中,如果修改字符串内容可以通过StringBuffer类完成,不要通过循环来改变其内容,每次改变都意味着一次断开连接,会降低代码性能。