首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将字符串与在Java中声明为最终的==进行比较

将字符串与在Java中声明为最终的==进行比较
EN

Stack Overflow用户
提问于 2013-10-17 04:41:11
回答 6查看 13K关注 0票数 224

关于Java中的字符串,我有一个简单的问题。下面的简单代码段只是将两个字符串连接起来,然后将它们与==进行比较。

代码语言:javascript
运行
复制
String str1="str";
String str2="ing";
String concat=str1+str2;

System.out.println(concat=="string");

比较表达式concat=="string"返回明显的false (我理解equals()==之间的区别)。

当这两个字符串声明为final时,

代码语言:javascript
运行
复制
final String str1="str";
final String str2="ing";
String concat=str1+str2;

System.out.println(concat=="string");

比较表达式concat=="string",在本例中返回true。为什么final会起作用呢?它一定要对实习生泳池做些什么吗?还是我只是被误导了?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2013-10-17 04:49:10

当您将一个String (它是不可变的)变量声明为final,并使用编译时常量表达式初始化它时,它也成为一个编译时常量表达式,它的值由使用它的编译器内联。因此,在第二个代码示例中,在插入值后,编译器将字符串连接转换为:

代码语言:javascript
运行
复制
String concat = "str" + "ing";  // which then becomes `String concat = "string";`

"string"相比,这将为您提供true,因为字符串文本是内部的。

来自变量

原语类型或类型String的变量,即final,由编译时常量表达式初始化(§15.28),称为常量变量。

也来自JLS§15.28 -常量表达式:

String类型的编译时常量表达式总是“内部”的,以便使用String#intern()方法共享唯一的实例。

在您的第一个代码示例中,情况并非如此,其中String变量不是final。因此,它们不是编译时常量表达式.那里的连接操作将延迟到运行时,从而导致创建一个新的String对象。您可以通过比较这两个代码的字节代码来验证这一点。

第一个代码示例(non-**final** 版本)编译为以下字节码:

代码语言:javascript
运行
复制
  Code:
   0:   ldc     #2; //String str
   2:   astore_1
   3:   ldc     #3; //String ing
   5:   astore_2
   6:   new     #4; //class java/lang/StringBuilder
   9:   dup
   10:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   13:  aload_1
   14:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_2
   18:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   24:  astore_3
   25:  getstatic       #8; //Field java/lang/System.out:Ljava/io/PrintStream;
   28:  aload_3
   29:  ldc     #9; //String string
   31:  if_acmpne       38
   34:  iconst_1
   35:  goto    39
   38:  iconst_0
   39:  invokevirtual   #10; //Method java/io/PrintStream.println:(Z)V
   42:  return

显然,它将string存储在两个独立的变量中,并使用StringBuilder执行级联操作。

然而,第二个代码示例(**final** 版本)如下所示:

代码语言:javascript
运行
复制
  Code:
   0:   ldc     #2; //String string
   2:   astore_3
   3:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:   aload_3
   7:   ldc     #2; //String string
   9:   if_acmpne       16
   12:  iconst_1
   13:  goto    17
   16:  iconst_0
   17:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
   20:  return

因此,它直接插入最后一个变量,以便在编译时创建字符串string,该字符串由ldc操作在步骤0中加载。然后,第二个字符串文本由ldc操作在步骤7中加载。它不涉及在运行时创建任何新的String对象。字符串在编译时就已经知道,并且它们是内部的。

票数 235
EN

Stack Overflow用户

发布于 2013-10-17 04:52:23

根据我的研究,所有的final String都是用Java实现的。其中一篇博文中写道:

因此,如果确实需要使用==或!=比较两个字符串,请确保在进行比较之前调用String.intern()方法。否则,总是更喜欢字符串比较的String.equals( String )。

这意味着,如果调用String.intern(),可以使用==运算符比较两个字符串。但是这里没有必要使用String.intern(),因为final String在内部是内部的。

您可以找到更多关于使用==算子的字符串比较String.intern()方法的信息。

有关更多信息,请参阅此堆栈过流帖子。

票数 31
EN

Stack Overflow用户

发布于 2013-10-17 04:52:36

如果你看一下这个方法

代码语言:javascript
运行
复制
public void noFinal() {
    String str1 = "str";
    String str2 = "ing";
    String concat = str1 + str2;

    System.out.println(concat == "string");
}

public void withFinal() {
    final String str1 = "str";
    final String str2 = "ing";
    String concat = str1 + str2;

    System.out.println(concat == "string");
}

并使用javap -c ClassWithTheseMethods版本解压缩,您将看到

代码语言:javascript
运行
复制
  public void noFinal();
    Code:
       0: ldc           #15                 // String str
       2: astore_1      
       3: ldc           #17                 // String ing
       5: astore_2      
       6: new           #19                 // class java/lang/StringBuilder
       9: dup           
      10: aload_1       
      11: invokestatic  #21                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
      14: invokespecial #27                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      17: aload_2       
      18: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: invokevirtual #34                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      ...

代码语言:javascript
运行
复制
  public void withFinal();
    Code:
       0: ldc           #15                 // String str
       2: astore_1      
       3: ldc           #17                 // String ing
       5: astore_2      
       6: ldc           #44                 // String string
       8: astore_3      
       ...

因此,如果字符串不是最终编译器,则必须使用StringBuilder连接str1str2,所以

代码语言:javascript
运行
复制
String concat=str1+str2;

将被汇编成

代码语言:javascript
运行
复制
String concat = new StringBuilder(str1).append(str2).toString();

这意味着concat将在运行时创建,因此不会来自字符串池。

另外,如果字符串是最终的,那么编译器就可以假设它们永远不会改变,所以它可以安全地连接它的值,而不是使用StringBuilder

代码语言:javascript
运行
复制
String concat = str1 + str2;

可以更改为

代码语言:javascript
运行
复制
String concat = "str" + "ing";

并连成

代码语言:javascript
运行
复制
String concat = "string";

这意味着concate将变成串文本,它将被嵌入到字符串池中,然后在if语句中与来自该池的相同字符串文本进行比较。

票数 21
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19418427

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档