解读String字符串拼接的原理

 更新时间:2023年07月31日 16:26:08   作者:努力努力再努力c.  
这篇文章主要介绍了关于String字符串拼接的原理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

前言

明白什么是引用,什么是该引用指向的真正对象。

==对于基本数据类型比较的是值,对于引用数据类型比较的是指向的对象的地址,即两者指向的是否是同一个对象。

String s = "gzc";

上述代码中s为变量引用,它存在于栈中,而“gzc”则是该变量引用所指向的真正数据,它存在于字符串常量池中。

言归正传

字符串拼接主要有2种情况:

1、常量与常量拼接

String s1 = "g"+"zc";//常量“g”与常量“zc”拼接

常量与常量拼接的原理:

字符串常量与常量之间的拼接操作其实在未加载到JVM内存之前就已经完成了,即在编译期间就会对字符串常量之间的拼接操作进行优化如下图,

进行反编译后,我们不难发现在编译完之后,s4已经被直接拼接好了。而且此时s3和s4指向的是字符串常量池中的同一个对象,即两者存储的对象地址是相同的。

所以s3==s4其结果为true。

2、涉及到变量的字符串拼接

2.1 变量与常量拼接

String s1 = "g";
String s2 = s1+"zc";//变量s1与常量“zc”拼接

2.2 变量与变量拼接

String s1 = "g";
String s2 = "zc";
String s3 = s1+s2;//变量s1与变量s2拼接

涉及到变量的字符串拼接原理:

只要字符串拼接其中涉及到变量,不管是几个变量,那么其拼接原理都如下:

当涉及到变量时,字符串用+进行字符串拼接的本质,其实就是利用StringBuilder类里的append()方法,将每一个字符串都一一添加进去,然后返回一个StringBuilder对象,所以可以不用新创建一个对象去接收返回值,直接链式编程得到最终添加的结果,最后再调用toString()方法将其转换为我们想要的字符串String类型。

如下图:

特别注意:

StringBuilder的toString()方法调用的是String重载的构造器方法,是以字符数组为字符串实际内容进行创建的,并未直接以字面量方式创建String对象,即:

所以如果我们上述代码没有定义s3和s4两个变量,只定义了String s5 = s1+s2; 的话,那么其实字符串常量池中是不存在“gzc”这个字符串的,而是只有“g”和“zc”。

因为只有通过字面量定义一个字符串以及调用String的intern()方法,这两种方式才会在字符串常量池中生成对应的对象。

而StringBuilder调用toString()方法创建的String对象则会直接在堆中为其分配内存,常量池中不会存在对应的对象。

所以如果判断s3==s5,则结果为false,因为s3指向的是字符串常量池中的“gzc”,而s5指向的是堆中的“gzc”对象,二者指向的对象地址不同,则比较结果自然为false。

特殊情况

若变量被声明为final类型,即为常量,则就遵循字符串常量拼接的规则了。

如下图:

jdk 1.8 对String字符串拼接并没有优化

String s = new String("1") + new String("1");
		String s2 = s + "1" + "1" + "1";
		//String s = "1" + "1";
		String s1 = "11";
		System.out.println(s.intern() == s1);
public static void main(java.lang.String[] args);
     0  new java.lang.StringBuilder [16]
     3  dup
     4  new java.lang.String [18]
     7  dup
     8  ldc <String "1"> [20]
    10  invokespecial java.lang.String(java.lang.String) [22]
    13  invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [25]
    16  invokespecial java.lang.StringBuilder(java.lang.String) [29]
    19  new java.lang.String [18]
    22  dup
    23  ldc <String "1"> [20]
    25  invokespecial java.lang.String(java.lang.String) [22]
    28  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30]
    31  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [34]
    34  astore_1 [s]
    35  new java.lang.StringBuilder [16]
    38  dup
    39  aload_1 [s]
    40  invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [25]
    43  invokespecial java.lang.StringBuilder(java.lang.String) [29]
    46  ldc <String "1"> [20]
    48  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30]
    51  ldc <String "1"> [20]
    53  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30]
    56  ldc <String "1"> [20]
    58  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30]
    61  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [34]
    64  astore_2 [s2]
    65  ldc <String "11"> [38]
    67  astore_3 [s1]
    68  getstatic java.lang.System.out : java.io.PrintStream [40]
    71  aload_1 [s]
    72  invokevirtual java.lang.String.intern() : java.lang.String [46]
    75  aload_3 [s1]
    76  if_acmpne 83
    79  iconst_1
    80  goto 84
    83  iconst_0
    84  invokevirtual java.io.PrintStream.println(boolean) : void [49]
    87  return
      Line numbers:
        [pc: 0, line: 18]
        [pc: 35, line: 19]
        [pc: 65, line: 21]
        [pc: 68, line: 22]
        [pc: 87, line: 23]
      Local variable table:
        [pc: 0, pc: 88] local: args index: 0 type: java.lang.String[]
        [pc: 35, pc: 88] local: s index: 1 type: java.lang.String
        [pc: 65, pc: 88] local: s2 index: 2 type: java.lang.String
        [pc: 68, pc: 88] local: s1 index: 3 type: java.lang.String
      Stack map table: number of frames 2
        [pc: 83, full, stack: {java.io.PrintStream}, locals: {java.lang.String[], java.lang.String, java.lang.String, java.lang.String}]
        [pc: 84, full, stack: {java.io.PrintStream, int}, locals: {java.lang.String[], java.lang.String, java.lang.String, java.lang.String}]
}

从class文件中可以看出,依然new了两个stringBuilder对象

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java持久层框架mybatis防止sql注入的方法

    java持久层框架mybatis防止sql注入的方法

    下面小编就为大家带来一篇java持久层框架mybatis防止sql注入的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • Java 使用Socket正确读取数据姿势

    Java 使用Socket正确读取数据姿势

    这篇文章主要介绍了Java 使用Socket正确读取数据姿势,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • IDEA 使用 SpotBugs 找出你代码中的bug问题

    IDEA 使用 SpotBugs 找出你代码中的bug问题

    这篇文章主要介绍了IDEA 使用 SpotBugs 找出你代码中的bug问题,重点给大家介绍SpotBugs 在 idea 中的安装和使用,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • Java Property类使用详解

    Java Property类使用详解

    这篇文章主要介绍了Java Property类使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • spring源码阅读--aop实现原理讲解

    spring源码阅读--aop实现原理讲解

    这篇文章主要介绍了spring源码阅读--aop实现原理讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • SpringBoot中的@EnableConfigurationProperties注解原理及用法

    SpringBoot中的@EnableConfigurationProperties注解原理及用法

    在SpringBoot中,@EnableConfigurationProperties注解是一个非常有用的注解,它可以用于启用对特定配置类的支持,在本文中,我们将深入探讨@EnableConfigurationProperties注解,包括它的原理和如何使用,需要的朋友可以参考下
    2023-06-06
  • SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法

    SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法

    本篇文章主要介绍了SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法,详细的介绍了Spring Schedule 与 Quartz 整合的两种方法,有兴趣的可以了解一下。
    2017-03-03
  • Java入门绊脚石之Override和Overload的区别详解

    Java入门绊脚石之Override和Overload的区别详解

    重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
    2021-10-10
  • Java多线程之readwritelock读写分离的实现代码

    Java多线程之readwritelock读写分离的实现代码

    这篇文章主要介绍了Java多线程之readwritelock读写分离的相关内容,文中涉及具体实例代码,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • MyBatis resultMap id标签的错误使用方式

    MyBatis resultMap id标签的错误使用方式

    这篇文章主要介绍了MyBatis resultMap id标签的错误使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01

最新评论