String类的两种定义方式:
-
直接赋值
-
通过构造方法赋值
//直接赋值 public class test2 { public static void main(String args[]) { String str = "hello"; System.out.println(str); } } //通过构造方法 public class test2 { public static void main(String args[]) { String str = new String("hello"); System.out.println(str); } }
但是,两种方法却有着极大的区别
主要体现在内存上
一个简单的例子:
public class test1 { public static void main(String args[]) { int a = 10; int b = 10; String str_1 = "hello"; String str_2 = "hello"; String str_3 = new String("hello"); System.out.println(a==b); System.out.println(str_1==str_2); System.out.println(str_1==str_3); System.out.println(str_2==str_3); } } //输出为true true false false
按道理来说,应该是四个true,因为他们的值都是相等的啊.......但是.....
上图解释了两个通过直接赋相同值产生的字符串使用“”==“”为什么返回true。
为什么会全部指向一块堆内存而不是分别指向自己的堆内存呢???
共享设计模式:
在JVM底层有一个对象池,里面有包括String在内的许多对象,当代码之中使用了直接赋值的方式定义了一个String类对象时,会将此字符串使用的匿名对象加入对象池。之后若有采用直接赋值的方式定义字符串,并且赋了相同的值,那么不会开辟新的堆内存空间,而是使用已有的对象(堆内存)进行继续使用。
上图解释了为什么直接赋值和构造方法赋相同的值,然而返回值却为false。因为使用构造方法定义字符串,使用了new关键字,意味着产生一块新的堆内存。那么两种方法各自开辟了一块堆内存,“==”符号对于字符串来说,比较的是地址值。两块堆内存的地址不同,所以返回值为false。
若要比较字符串的内容,而不是地址,应该使用str_a.equals(str_b)
字符串常量就是String类的匿名对象,所谓的直接赋值实际上就是为匿名对象加了一个名字。但是String类的匿名对象是由系统自动生成的,不用用户自己创建
public class test1 { public static void main(String args[]) { String str = "hello"; System.out.println("hello".equals(str)); } } //返回值为true
tips:
防止空指向异常(使用了未实例化的对象):将字符串写在输入的前面
public class test1 { public static void main(String args[]) { String input = null; System.out.println(input.equals("hello")); } } //Exception in thread "main" java.lang.NullPointerException at test.test1.main(test1.java:6) 空指向异常 public class test1 { public static void main(String args[]) { String input = null; System.out.println("hello".equals(input)); } } //false
两种定义方法的优劣:
直接赋值节约空间
然而构造方法却会产生大量垃圾,如下图:
代码从右向左看,产生的第一块堆内存会成为垃圾
构造方法实例化的方法除了浪费内存外,其定义的对象不会保存在对象池之中,若要保存,需要手动入池(使用inner()方法)
public class test1 { public static void main(String args[]) { String str_1 = new String("hello").intern(); String str_2 = "hello"; System.out.println(str_1==str_2); } } //true
返回值为true,证明入池成功。