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

String 进阶

字符串拼接

// 常量与常量的拼接结果放在常量池
// 常量池中不会存在相同的常量
String str1 = "a" + "b";
System.out.println(str1 == "ab");
// 拼接时有一个为变量,则结果会放在堆中。
// 变量拼接的原理是 StringBuilder append 最后toString 
// 查看字节码指令就可以看到详细过程
// 就是在堆空间中 new String
String a = "a";
String str2 = a + "b";
System.out.println(str2 == "ab");
// 拼接结果调用 intern 方法,则主动将常量池中还没有的字符串对象放入字符串常量池,并返回对应地址
// 如果常量池中有对应字符串对象,则返回已有的字符串对象地址
String b = "b";
String str3 = ("a" + b).intern();
System.out.println(str3 == "ab");
// final 修饰的变量,在编译时就会进行赋值确定
String str4 = a + b;
System.out.println(str4 == "ab");// false
final String c = "c";
final String d = "d";
String str5 = c + d;
System.out.println(str5 == "cd");// true

使用 StringBulider 进行拼接

int i = 0;
String str = "";
while (i < 1000){str += "a";
}

循环中每次 str + “a” 都会创建一个 StringBuilder ,然后toString。效率极低。

int i = 0;
StringBuilder sb = new StringBuilder();
while (i < 1000){sb.append("a");
}
String str = sb.toString();

只 new 了一个StringBuilder,且只 toString 一次。

进一步优化

int i = 0;
StringBuilder sb = new StringBuilder(1000);
while (i < 1000){sb.append("a");
}
String str = sb.toString();

优化方式和ArrayList一样。如果我们能大概确定要生成的字符串长度,我们可以初始化 StringBuilder 的底层 char[] 数组的长度,避免超过长度时的扩容操作。

new String 会创建几个对象?

String s = new String(“ab”) 会创建两个对象。一个在堆空间,一个在字符串常量池。此时 s 的地址为指向堆空间的字符串对象。

具体可以在 idea 中下载 jclasslib 插件,查看字节码的方式来解释
在这里插入图片描述

引申

new String("a") + new String("b") // 创建了几个对象?
1. new String("a") 创建两个对象,堆和常量池
2. new String("b") 创建两个对象,堆和常量池
3. 两个非常量相加,会创建一个 StringBuilder 对象使用其 append 方法,最后 toString
4. toString 方法会 new 一个 String 对象(但其不会在常量池生成对象"ab"

intern 方法的使用

public native String intern();

intern 是一个 native 方法,如果当前常量池没有当前字符串对应相等(equals 为 true)的字符串,则将对象放入字符串常量池,并返回对应地址。如果常量池中有对应字符串对象,则返回已有的字符串对象地址

示例

String a = new String("a");
a.intern();
System.out.println(a == "a"); //jdk6 false jdk1.7+ falseString b = new String("b") + new String("b");
b.intern();
System.out.println(b == "bb");//jdk6 false jdk1.7+ true

对象 a 指向的时堆中的字符串对象地址,“a” 放在常量池的,所以为 false

new String(“b”) + new String(“b”) 不会在常量池创建对象 “ab”,其主要问题是,jdk1.7+ 环境中,在调用 intern 方法的时候,不是在常量池中创建一个新的对象 “ab”,而是将当前堆中 new 的 “ab” 的引用赋值给了常量池的引用,导致堆和常量池中的引用都指向了同一个地址。基于此特性,下面的示例结果就能够解释了。

String c = new String("c") + new String("c");
String cc = "cc";
c.intern(); // 当前 "cc" 在常量池已经存在,且和堆中的对象引用不同
System.out.println(c == cc);// jdk1.7+ falseString d = new String("d") + new String("d");
d.intern();// 此时 "dd" 在常量池还不存在,基于上面的解释,此时堆和常量池中的"dd"对象的引用是一致的
String dd = "dd";
System.out.println(d == dd);//  jdk1.7+  true

intern 使用技巧

大量的 String 对象使用的时候,比如:String 数组或大的集合中存放 String 对象,可以对String对象先调用 intern 方法返回常量池引用后存放。这样的好处是,最后这些大量的引用都引用的常量池的对象,堆中的对象可以正常 GC 释放。此方式特别在有大量重复字符串对象的时候能节省大量的空间。

http://www.lryc.cn/news/180356.html

相关文章:

  • ESP32设备通信-两个ESP32间UART通信
  • LCR 052.递增顺序搜索树
  • Mysql集群技术问答
  • 2023版 STM32实战4 滴答定时器精准延时
  • ESP32设备驱动-数据持久化到Flash
  • Swift data范围截取问题
  • PICO首届XR开发者挑战赛正式启动,助推行业迈入“VR+MR”新阶段
  • 【计算机网络】应用层协议原理
  • buuctf-[WUSTCTF2020]CV Maker
  • 数据库表操作详解
  • axios配置代理ip
  • Apache Commons Pool2 池化技术
  • 二叉树的最近公共祖先LCA
  • AWS SAA知识点整理(作成中)
  • C++模板大全(持续更新,依不同网站整理而成)
  • 《CTFshow-Web入门》10. Web 91~110
  • 计组--总线
  • Git中的HEAD
  • 软件设计师_数据库系统_学习笔记
  • 毛玻璃态计算器
  • 常说的I2C协议是干啥的(电子硬件)
  • C/C++进程超详细详解【中部分】(系统性学习day07)
  • S型速度曲线轨迹规划(约束条件为速度和位移)
  • 从零手搓一个【消息队列】实现数据的硬盘管理和内存管理(线程安全)
  • 自动驾驶中的感知模型:实现安全与智能驾驶的关键
  • 【CVPR 2023】DSVT: Dynamic Sparse Voxel Transformer with Rotated Sets
  • MySQL超入门(1)__迅速上手掌握MySQL
  • 四、浏览器渲染过程,DOM,CSSDOM,渲染,布局,绘制详细介绍
  • 2021-06-10 51单片机设计一个蜂鸣器报警电路每秒
  • D‘Agostino-Pearson正态检验|偏度skewness和峰度kurtosis