当前位置: 首页 > article >正文

StringTable

字符串比较“==”

创建字符串的几种方式

  1. 字符串字面量:如果你通过字符串字面量来创建字符串(例如String s1 = "hello";),并且这些字面量在编译时就能够确定,那么编译器会尝试将这些字符串放入类的常量池中。在运行时,如果两个字符串字面量具有相同的内容,并且它们都属于同一个类或同一个字符串常量池(在Java 7及更高版本中,字符串常量池是Java堆的一部分,但逻辑上仍然与类的常量池分开管理),那么它们会引用常量池中的同一个字符串对象。因此,在这种情况下,使用==来比较这两个字符串会返回true

  2. 通过new关键字创建的字符串:每次你使用new String(...)构造函数来创建一个新字符串时,无论内容是否相同,都会在堆上创建一个新的String对象。即使两个新创建的字符串对象包含完全相同的字符序列,它们也是不同的对象,因为它们位于内存中的不同位置。因此,在这种情况下,使用==来比较这两个字符串会返回false

  3. 字符串连接:当你使用+运算符来连接字符串时,结果取决于连接操作是在编译时还是在运行时进行的。如果连接操作涉及的是编译时常量(即字符串字面量),那么编译器会进行常量折叠,并尝试将结果放入类的常量池中。然而,如果连接操作涉及的是运行时变量,那么每次连接都会创建一个新的StringBuilder对象(在Java 5及更高版本中),并使用其toString()方法来生成一个新的字符串对象。

  4. intern()方法String类的intern()方法提供了一种将字符串放入字符串常量池中的方式(如果它还不存在的话)。如果你有两个不同的字符串对象,但它们的字符序列相同,并且你调用了其中一个对象的intern()方法,那么这两个对象在之后的比较中(使用==)可能会返回true,前提是另一个对象也已经通过某种方式(例如,直接作为字面量或之前调用过intern())被放入了字符串常量池中。

  5. String.CASE_INSENSITIVE_ORDER和其他比较器:值得注意的是,==运算符总是进行引用比较。如果你需要进行内容比较而不考虑大小写或其他因素,你应该使用String类的equals()方法或String.CASE_INSENSITIVE_ORDER等比较器。

inter()

总结String的intern()的使用:

jdkl.6中,将这个字符串对象尝试放入串池。

》如果串池中有,则并不会放入。返回已有的串池中的对象的地址

》如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址

Jdk1.7起,将这个字符串对象尝试放入串池。

》如果串池中有,则并不会放入。返回已有的串池中的对象的地址

》如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址

public static void main(String[] args) {

        String s=new String("1");
        s.intern(); //调用此方法前,字符串常量池中已经存在了"1"
        String s2="1";
        System.out.println(s==s2);//jdk6:false  jdk7/8:false

        String s3=new String("1")+new String("1");
//        执行完上一行代码以后,字符串常量池中,是否存在"11"?  不存在
        s3.intern();//在字符串常量池中生成“11”,jdk6:创建了一个新的对象“11”,也就有新的地址
        //                                jdk7:常量池中并没有创建,而是把对象的引用地址复制一份,放入串池,并返回串池中的引用地址

        String s4="11";//s4变量记录的地址:使用的是上一行代码执行时,在常量池中生成的“11”的地址
        System.out.println(s3==s4);//jdk6: false jdk7/8:  true

    }

 

 

public void test4{
    //用final修饰变成常量
    final String s1="a";
    final String s1="b";
    String s3="ab";
    String s4=s1+s2;
    System.out.println(s3==s4); //true
}

字符串拼接操作不一定使用的是StringBuilder

如果拼接符号左右两边都是字符串常量或者常量引用,则仍然使用编译期优化

 保证变量S指向字符串常量池中

 典例

从字节码角度分析

public static void main(String[] args) {
    String s1="aaaa"+"bbbb";
    String s2="aaaabbbb";
    String s3="aaaa";
    String s4="bbbb";
    
    String s5=s3+s2;
    //拼接符号前后出现了变量,则相当于在堆空间new String()
    //字符串拼接(使用+)在Java中通常会导致创建一个新的String对象。
//编译器可能会优化这个操作,使用StringBuilder来执行拼接,但这并不改变最终结果是一个新的String对象的事实。
    String s6="aaaa"+s4;
    String s7=s3+"bbbb";
    System.out.println(s6==s7);

    System.out.println(s1==s2);
    System.out.println(s5==s2);
    System.out.println(s6==s2);
    System.out.println(s7==s2);
    //intern():判断字符串常量池中是否存在“aaaabbbb”值,如果存在,则返回常量池中的“aaaabbbb”的地址
    //如果不存在,则在常量池中加载一份“aaaabbbb”,并返回此对象的地址
    String s8=s6.intern();
    System.out.println(s2==s8);
}
true
false
false
false
false
false
true
 0 ldc #2 <aaaabbbb>
  2 astore_1
  3 ldc #2 <aaaabbbb>
  5 astore_2
  6 ldc #3 <aaaa>
  8 astore_3
  9 ldc #4 <bbbb>
 11 astore 4
 13 new #5 <java/lang/StringBuilder>
 16 dup
 17 invokespecial #6 <java/lang/StringBuilder.<init> : ()V>
 20 aload_3
 21 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
 24 aload_2
 25 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
 28 invokevirtual #8 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>
 31 astore 5
 33 new #5 <java/lang/StringBuilder>
 36 dup
 37 invokespecial #6 <java/lang/StringBuilder.<init> : ()V>
 40 ldc #3 <aaaa>
 42 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
 45 aload 4
 47 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
 50 invokevirtual #8 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>
 53 astore 6
 55 new #5 <java/lang/StringBuilder>
 58 dup
 59 invokespecial #6 <java/lang/StringBuilder.<init> : ()V>
 62 aload_3
 63 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
 66 ldc #4 <bbbb>
 68 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
 71 invokevirtual #8 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>
 74 astore 7
 76 getstatic #9 <java/lang/System.out : Ljava/io/PrintStream;>
 79 aload_1
 80 aload_2
 81 if_acmpne 88 (+7)
 84 iconst_1
 85 goto 89 (+4)
 88 iconst_0
 89 invokevirtual #10 <java/io/PrintStream.println : (Z)V>
 92 getstatic #9 <java/lang/System.out : Ljava/io/PrintStream;>
 95 aload 5
 97 aload_2
 98 if_acmpne 105 (+7)
101 iconst_1
102 goto 106 (+4)
105 iconst_0
106 invokevirtual #10 <java/io/PrintStream.println : (Z)V>
109 getstatic #9 <java/lang/System.out : Ljava/io/PrintStream;>
112 aload 6
114 aload_2
115 if_acmpne 122 (+7)
118 iconst_1
119 goto 123 (+4)
122 iconst_0
123 invokevirtual #10 <java/io/PrintStream.println : (Z)V>
126 getstatic #9 <java/lang/System.out : Ljava/io/PrintStream;>
129 aload 7
131 aload_2
132 if_acmpne 139 (+7)
135 iconst_1
136 goto 140 (+4)
139 iconst_0
140 invokevirtual #10 <java/io/PrintStream.println : (Z)V>
143 return

下面是对这段字节码的逐条讲解:

  1. 0 ldc #2 <aaaabbbb>:加载常量池中的第2项(aaaabbbb字符串)到操作数栈。

  2. 2 astore_1:将操作数栈顶的元素(aaaabbbb字符串)存储到局部变量表的第1个位置。

  3. 3 ldc #2 <aaaabbbb>:再次加载常量池中的第2项(aaaabbbb字符串)到操作数栈。

  4. 5 astore_2:将操作数栈顶的元素(aaaabbbb字符串)存储到局部变量表的第2个位置。

  5. 6 ldc #3 <aaaa>:加载常量池中的第3项(aaaa字符串)到操作数栈。

  6. 8 astore_3:将操作数栈顶的元素(aaaa字符串)存储到局部变量表的第3个位置。

  7. 9 ldc #4 <bbbb>:加载常量池中的第4项(bbbb字符串)到操作数栈。

  8. 11 astore 4:将操作数栈顶的元素(bbbb字符串)存储到局部变量表的第4个位置。

接下来的部分涉及创建StringBuilder对象,并使用它来拼接字符串:

  1. 13 new #5 <java/lang/StringBuilder>:在堆上分配一个新的StringBuilder对象引用。

  2. 16 dup:复制操作数栈顶的元素(新分配的StringBuilder对象引用)。

  3. 17 invokespecial #6 <java/lang/StringBuilder.<init> : ()V>:调用StringBuilder的构造方法初始化对象。

  4. 20 aload_3:加载局部变量表中第3个位置的元素(aaaa字符串)到操作数栈。

  5. 21 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>:调用StringBuilderappend方法,将aaaa字符串追加到StringBuilder对象中。

  6. 24 aload_2:加载局部变量表中第2个位置的元素(aaaabbbb字符串)到操作数栈。

  7. 25 invokevirtual #7:再次调用append方法,将aaaabbbb字符串追加到StringBuilder对象中。

  8. 28 invokevirtual #8 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>:调用toString方法,将StringBuilder对象转换为字符串。

  9. 31 astore 5:将操作数栈顶的字符串存储到局部变量表的第5个位置。

接下来的部分重复上述步骤,但使用不同的字符串和局部变量槽:

  1. 创建一个新的StringBuilder对象,并使用aaaa和局部变量表第4个位置的字符串(bbbb)构建一个新的字符串,存储在局部变量表的第6个位置。

  2. 再次创建一个新的StringBuilder对象,并使用aaaa和常量池中的bbbb字符串构建另一个字符串,存储在局部变量表的第7个位置。

最后的部分涉及条件判断和打印输出:

  1. 使用if_acmpne指令比较局部变量表第1个和第2个位置的字符串是否不相等,如果不相等,跳转到指定的指令处(用于控制打印输出布尔值false)。

  2. 类似的条件判断用于比较局部变量表第5个和第2个位置的字符串、第6个和第2个位置的字符串、以及第7个和第2个位置的字符串,每次比较后都打印一个布尔值。

  3. 143 return:方法返回。


http://www.kler.cn/a/378132.html

相关文章:

  • 4种鼓励创业创新的方法
  • Elasticsearch 实战应用详解!
  • Spring的常用注解之@Component——day1
  • energy 发布 v2.4.5
  • 九,数据类型存储
  • 【开源免费】基于SpringBoot+Vue.JS新闻推荐系统(JAVA毕业设计)
  • 利用ExcelJS封装一个excel表格的导出
  • git 入门作业
  • 学习记录:基于Z-Stack 3.0.1的Zigbee智能插座实现
  • Django-分页
  • 构建后端为etcd的CoreDNS的容器集群(七)、编写适合阅读的域名管理脚本
  • Vue2.0 通过vue-pdf-signature@4.2.7和pdfjs-dist@2.5.207实现PDF预览
  • 目前最新最好用 NET 混淆工具 .NET Reactor V6.9.8
  • Claude 3.5 新功能 支持对 100 页的PDF 图像、图表和图形进行可视化分析
  • diffusion model 学习笔记
  • nodejs入门教程14:nodejs querystring模块
  • ssm基于SSM的校内信息服务发布系统的设计与实现+vue
  • 移植 AWTK 到 纯血鸿蒙 (HarmonyOS NEXT) 系统 (1) - 让 OpenGLES 应用跑起来
  • YOLOv11改进策略【卷积层】| CVPR-2020 Strip Pooling 空间池化模块 处理不规则形状的对象 含二次创新
  • Node.js 应用程序中的文件写入提升为 RCE
  • 江协科技STM32学习- P25 UART串口协议
  • 系统安全与加解密技术
  • 【网络】传输层协议TCP
  • 甄选学习平台 优化员工培训体验
  • huggingface利用bert-base-chinese实现中文情感分类
  • Jenkins面试整理-如何在 Jenkins 中配置构建任务?