String相关特性



1 字符串实例化两种方式的区别

String 的实例化方式有两种,一种是直接赋值,另一种是通过 new 关键字实例化字符串。

1.1 直接赋值

String str1 = "hello";

使用直接赋值方式,只要以后声明的字符串内容相同,则不会再开辟新的内存空间。这是Java中共享设计的思想,Java 通过创建一个字符串池,字符串池中保存了多个字符串对象。若新实例化的字符串对象已经在对象池中,则不再重新定义,直接从对象池中取出使用。

具体到例子, str1 通过直接赋值,因为字符串池不存在“hello”,所以创建一个新的实例置入字符串池,并在栈中开辟一个内存空间直接指向“hello”。

如果我们声明新的字符串 str2=“hello”, str2 将指向“hello”存放的内存空间,不会再开辟一个新的内存空间。这是因为,JVM会先到字符串池中查找,如果有的话返回字符串池中这个实例的引用,否则才创建一个新的实例并置入字符串对象池。因此,通过直接赋值可以只占用一个内存空间。

1.2 new关键字

String newstr1 = new String("hello");

使用 new 关键字实例化字符串,不管如何都将在堆中开辟一个新的空间。 new 创建字符串实例时会先在对象池中查找是否有相同值的字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址。如果没有,则在堆中创建一份,然后返回堆中的地址。

具体到例子, newstr1 通过 new 关键字实例化,无论对象池是否存在相同值的字符串,都会在堆中开辟一个新的内存空间,赋值后将在栈中开辟一个新的空间存放 newstr1 ,也就是说使用 new 关键字实例化字符串总共开辟了两个内存空间。因为多出了从字符串池拷贝到堆内存的操作。

1.3 字符串池

字符串池的优点是避免了相同字符串的创建,节省内存,省去创建相同字符串的时间;另一方面,字符串池的缺点是牺牲了 JVM 在常量池中遍历对象所需要的时间,不过时间成本相比之下较低。

1.4 小结

直接赋值只需一个内存空间,相同的字符串可以重复指向同一个内存空间;

new 创建字符串实例无论如何会在堆中开辟一个新的内存空间,因此总共占用两个内存空间。

综上,使用直接赋值的方法较好,可以避免占用多余的内存空间。


2 字符串比较“==”与 equals 的区别

字符串比较是较为常用的功能, Java 中主要提供了两种字符串比较方式,“==”和 equals ,如下:

String str1 = "hello";
String str2 = new String("hello");
System.out.println(str1==str2);             //false
System.out.println(str1.equals(str2));      //true

奇怪,明明 str1 和 str2 是一样的,为什么“==”会是 false 呢?

这是因为,在 String 中,“==”比较的是内存单元地址, equals 比较的是字符串的值。通俗的说,“==”是判断两个对象是不是同一个东西,而 equals 是判断两个对象的值是否相同。

由前一小节可知, new 关键字实例化方式会开辟两个内存空间, str1 和 str2 虽然都是“hello”,但它们存放的内存空间地址是不一样的。因此,“==”会认为它们不等,因为它们的地址不同(或者说不是同一个对象); equals 会认为它们相等,因为他们的值都是“hello”。

注: Java 中所有的类都继承自 Object 这个基类, equals 是 Object 中定义的一个方法,这里的 equals 比较的是对象的内存地址,你可以认为这里的 equals 等同于“==”。值得注意的是, String 类中的 equals 重写了 Object 中的 equals 方法,它有自己的实现,而不再是比较内存地址。因此,才有了“==”和 equals 在字符串比较中的区别。


3 String内容不可变

假设:

String str = "hello";
str = str + "world!";

分析:原先 str 指向的是存放“hello”的内存空间,经过重新赋值,并不会将内存空间的“hello”改成“helloworld!”,而是开辟了新的内存空间存放“helloworld!”,将 str 重新指向存放“helloworld!”的内存空间, str 不再指向存放“hello”的内存空间。


致谢:

  • 勋仔

参考资料:


 
comments powered by Disqus