A和B虽然被定义为常量,但是它们都没有马上被赋值。在运算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s不能在编译期被确定,而只能在运行时被创建了。
  String s1 = new String("xyz"); 创建了几个对象?
  考虑类加载阶段和实际执行时。
  (1)类加载对一个类只会进行一次。”xyz”在类加载时已经创建并驻留了(如果该类被加载之前已经有”xyz”字符串被驻留过则不需要重复创建用于驻留的”xyz”实例)。驻留的字符串是放在全局共享的字符串常量池中的。
  (2)在这段代码后续被运行的时候,”xyz”字面量对应的String实例已经固定了,不会再被重复创建。所以这段代码将常量池中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s1 持有。
  这条语句创建了2个对象。
  java.lang.String.intern()
  运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的是String类的intern()方法。
  String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。
  public static void main(String[] args) {   
  String s1 = new String("计算机");
  String s2 = s1.intern();
  String s3 = "计算机";
  System.out.println("s1 == s2? " + (s1 == s2));
  System.out.println("s3 == s2? " + (s3 == s2));
  }
  s1 == s2? false
  s3 == s2? true
  字符串比较更丰富的一个例子
  public class Test {
  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.println(hello == ("Hel"+lo).intern());
  }  
  }
  class Other { static String hello = "Hello"; }
  package other;
  public class Other { public static String hello = "Hello"; }
  true true true true false true
  在同包同类下,引用自同一String对象.
  在同包不同类下,引用自同一String对象.
  在不同包不同类下,依然引用自同一String对象.
  在编译成.class时能够识别为同一字符串的,自动优化成常量,引用自同一String对象.
  在运行时创建的字符串具有独立的内存地址,所以不引用自同一String对象.