字符串常量池

首先理解,如何在Java语言中如何创建字符串。在Java语言中有两种方式创建一个字符串对象

  1. 使用new运算符

    String str = new String("hello");
    StringBuilder str = new StringBuilder("Hello");
    StringBuffer str=  new StringBuffer("hello");
  2. 使用字符串常量或者常量表达式

    String str = "Hello"; //字符串常量
    String str = "Hel" + "lo"; //字符串常量表达式

这些字符串的创建方式之间的区别: 在Java中,equals方法被认为是对对象的值进行深层比较,而’==’运算符被认为是浅层比较equals方法比较的是两个对象的内容而不是引用==运算符则不同,如果是引用类型(例如,对象),如果两个对象的引用相同,也就是两个对象的引用指向相同的对象内存空间,则两个对象相等;如果是基本类型(例如,原语),如果过两个对象的值相等,则它们的之相等。equals方法将会返回true, 如果两个对象的内容相同。但是对于java.lang.Object类而言,默认的equals方法,如果一个类没有复写它,该方法在两个对象的引用指向同一个对象,也就是说如果两个对象的引用指向相同的内存空间,那么将返回true。
我们看以下例子:

public class DemoStringCreation {
  public static void main (String args[]) {
    String str1 = "Hello";  
    String str2 = "Hello"; 
    System.out.println("str1 and str2 are created by using string literal.");    
    System.out.println("    str1 == str2 is " + (str1 == str2));  
    System.out.println("    str1.equals(str2) is " + str1.equals(str2));  
    String str3 = new String("Hello");  
    String str4 = new String("Hello"); 
    System.out.println("str3 and str4 are created by using new operator.");    
    System.out.println("    str3 == str4 is " + (str3 == str4));  
    System.out.println("    str3.equals(str4) is " + str3.equals(str4));  
    String str5 = "Hel"+ "lo";  
    String str6 = "He" + "llo"; 
    System.out.println("str5 and str6 are created by using string constant expression.");    
    System.out.println("    str5 == str6 is " + (str5 == str6));  
    System.out.println("    str5.equals(str6) is " + str5.equals(str6));
    String s = "lo";
    String str7 = "Hel"+ s;  
    String str8 = "He" + "llo"; 
    System.out.println("str7 is computed at runtime.");     
    System.out.println("str8 is created by using string constant expression.");    
    System.out.println("    str7 == str8 is " + (str7 == str8));  
    System.out.println("    str7.equals(str8) is " + str7.equals(str8));
  }
}

输出结果:

str1 and str2 are created by using string literal.
    str1 == str2 is true
    str1.equals(str2) is true
str3 and str4 are created by using new operator.
    str3 == str4 is false
    str3.equals(str4) is true
str5 and str6 are created by using string constant expression.
    str5 == str6 is true
    str5.equals(str6) is true
str7 is computed at runtime.
str8 is created by using string constant expression.
    str7 == str8 is false
    str7.equals(str8) is true

使用相同的字符序列而不是使用new关键字创建的两个字符串会创建指向Java字符串常量池中的同一个字符串的指针。字符串常量池是Java节约资源的一种方式。

字符串常量池

字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。
例如:

public class Program
{
    public static void main(String[] args)
    {
       String str1 = "Hello";  
       String str2 = "Hello"; 
       System.out.print(str1 == str2);
    }
}

输出结果:

true

如果使用 String a = new String("Hello"); 。该字符串对象将会在字符串常量池外也就是Java 堆中进行创建,即使字符串常量池中存在相同的字符串。考虑到这些,我们需要避免使用new创建一个字符串,除非你明确的直到必须要这么做。例如下列代码

public class Program
{
    public static void main(String[] args)
    {
       String str1 = "Hello";  
       String str2 = new String("Hello");
       System.out.println(str1 == str2);
       System.out.println(str1.equals(str2));
    }
}

输出结果为:

false 
true

这里我们需要知道。JVM中拥有一个常量池,任何字符串至多维护一个对象。字符串常量总是指向字符串常量池中的一个对象。通过new操作创建的字符串对象不指向字符串常量池中的任何对象,而是在Java堆中创建相应的对象,但是可以通过使用intern()方法来指向或在常量池中创建一个同值的字符串对象。java.lang.String.intern()返回一个保留池字符串,也即是在全局字符串常量池中拥有一个入口。如果该字符串以前没有存在于字符串常量池中,那么它就会被添加到池中。
例如下列代码:

public class Program
{
    public static void main(String[] args)
    {
        // Create three strings in three different ways.
        String s1 = "Hello";
        String s2 = new StringBuffer("He").append("llo").toString();
        String s3 = s2.intern();
        // Determine which strings are equivalent using the ==
        // operator
        System.out.println("s1 == s2? " + (s1 == s2));
        System.out.println("s1 == s3? " + (s1 == s3));
    }
}

结果为:

s1 == s2? false
s1 == s3? true

为了优化空间,运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用。这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收。

Java语言规范第三版中的字符串常量

每一个字符串常量都是指向一个字符串类实例的引用。字符串对象有一个固定值。字符串常量,或者一般的说,常量表达式中的字符串都被使用方法 String.intern进行保留来共享唯一的实例。
例如:

public class Hello {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.println((hello == "Hello") + " ");
        System.out.println((Other.hello == hello) + " ");
        System.out.println((other.Other.hello == hello) + " ");
        System.out.println((hello == ("Hel"+"lo")) + " ");
        System.out.println((hello == ("Hel"+lo)) + " ");
        System.out.printlnln(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

输出结果:

true
true
true
true
false 
true

以上例子说明了
- 同一个包下同一个类中的字符串常量的引用指向同一个字符串对象;
- 同一个包下不同的类中的字符串常量的引用指向同一个字符串对象;
- 不同的包下不同的类中的字符串常量的引用仍然指向同一个字符串对象;
- 由常量表达式计算出的字符串在编译时进行计算,然后被当作常量;
- 在运行时通过连接计算出的字符串是新创建的,因此是不同的;
- 通过计算生成的字符串显示调用intern方法后产生的结果与原来存在的同样内容的字符串常量是一样的。


本文参考翻译于http://www.xyzws.com/Javafaq/what-is-string-literal-pool/3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值