【问题标题】:Java String pool and type castingJava 字符串池和类型转换
【发布时间】:2012-03-26 00:27:41
【问题描述】:

我的问题是关于 Java 处理字符串文字的方式。从 Java 语言规范 (JLS) 中可以清楚地看出,字符串字面量是隐式实习的 - 换句话说,在堆的字符串常量池部分中创建的对象,与调用 @987654321 时创建的基于堆的对象相反@。

似乎与 JLS 所说的不一致的是,当使用带有强制转换的常量字符串类型的字符串连接创建新字符串时,根据 JLS,它应该被视为常量字符串,显然 JVM 是创建一个新的 String 对象,而不是隐式地实习它。我很欣赏有关此特定行为的任何解释,以及这是否是特定于平台的行为。我在 Mac OSX Snow Leopard 上运行。

public class Test
{
    public static void main(String args[])
    {
        /*
            Create a String object on the String constant pool
            using a String literal
        */
        String hello = "hello";
        final String lo = "lo"; // this will be created in the String pool as well
        /*
            Compare the hello variable to a String constant expression
            , that should cause the JVM to implicitly call String.intern()
        */
        System.out.println(hello == ("hel" + lo));// This should print true
        /*
            Here we need to create a String by casting an Object back
            into a String, this will be used later to create a constant
            expression to be compared with the hello variable
        */
        Object object = "lo";
        final String stringObject = (String) object;// as per the JLS, casted String types can be used to form constant expressions
        /*
            Compare with the hello variable
        */
        System.out.println(hello == "hel" + stringObject);// This should print true, but it doesn't :(

    }
}

【问题讨论】:

  • 不要对字符串使用 ==,使用 String1.equals(string2)
  • @dann.dev:我认为你错过了问题的重点。
  • 整件事是关于在哪里创建对象,而不是创建什么对象。
  • 您能否链接到 JLS 中指出转换为 String 的表达式将被放入常量池的部分?我的预感是第二个表达式根本不能被常量折叠,因为编译器没有进行数据流分析来确定stringObject 是常量"lo"
  • @dann.dev OP 不是在问如何比较字符串,而是在问常量字符串表达式在 Java 中是如何工作的。

标签: java string compiler-construction jvm


【解决方案1】:

在编译时常量表达式中不允许转换为 Object。唯一允许的转换是 String 和原语。 JLS(Java SE 7 版)第 15.28 节:

> - Casts to primitive types and casts to type String

(实际上还有第二个原因。object 不是final,因此不可能将其视为constant variable。“原始类型或String 类型的变量,即final 并用编译时常量表达式(第 15.28 节),称为常量变量。”——第 4.12.4 节。)

【讨论】:

  • 我将一个对象转换成一个字符串,所以应该形成一个常量表达式,不是吗?
  • 隐式转换为 Object 意味着它不再是常量。
  • @Hasanein,这不是问题 - 分配给非常量字符串的 Object 变量。
  • 将对象更改为最终对象仍会产生类似的结果。你能举一个使用转换为字符串来形成常量字符串表达式的例子吗?
  • @Hasanein 我看不出这是如何在编译时常量表达式中发生的。
【解决方案2】:

好像是因为你在这里引用了一个对象final String stringObject = (String) object;,这不再是一个“编译时”常量,而是一个“运行时”常量。 here 的第一个示例通过以下部分避开了它:

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));

字符串 str7 是在运行时计算的,因为它引用了另一个不是文字的字符串,所以按照这个逻辑,我假设尽管你把 stringObject 设为 final,它仍然引用一个对象,所以不能在编译时间。

从 Java 语言规范 here 中,它指出:

“当结果不是编译时常量表达式(第 15.28 节)时,字符串连接运算符 +(第 15.18.1 节)会隐式创建一个新的字符串对象。”

我找不到任何可以使用演员表的例子,除了这个可怕的例子:

System.out.println(hello == "hel" + ( String ) "lo");

这几乎没有任何逻辑用途,但由于上述情况,可能包含有关字符串转换的部分。

【讨论】:

  • 谢谢 dann,你说得对,我认为规范中提到的唯一原因是支持将字符串转换回字符串,我也看不到它有太多实际用途。跨度>
猜你喜欢
  • 1970-01-01
  • 2011-12-05
  • 2011-06-29
  • 2010-12-26
  • 1970-01-01
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多