关于 String 那些不得不说的那些事

简介: 关于 String 那些不得不说的那些事

一、String、StringBuilder和StringBuffer的区别

1. String是字符串常量,StringBuilder和StringBuffer是字符串变量


String对象创建完成之后,如果对其更改,都是重新创建一个字符串对象,让引用变量重新指向其引用地址,而StringBuilder和StringBuffer都是可变的;


2. StringBuilder是线程不安全的,StringBuffer是线程安全的


StringBuffer相关方法添加了线程同步关键字synchronize关键字,故线程安全的,但效率低比StringBuilder低。


二、String为什么设计成不可变(immutable)

1. 字符串常量池的优化需要


字符串常量池针对String字符串使用的一种优化策略,创建字符串对象前,先检查字符串常量是否已经有该字符串(obj1.equal(obj2)),有直接返回字符串在字符串常量池的引用,如果String为可变的,这种优化策略则无效;


2. 允许String对象缓存HashCode

字符串的不变性保证了hashcode唯一性,不可变的hashcode可以被缓存而不用重新计算,提升了像使用String作为键值的hashmap的效率,这也侧面反映了hashmap为什么多数使用String作为键值的原因了;


3. 多线程使用安全性


字符串不可变,所以在多线程可以共享一个字符串实例,而不需要做额外的线程同步;


4. 类加载器需要


类加载器用到字符串,不可变性提供了安全性,以便类的正确加载;例如在加载java.sql.Connection类,如果这个值被改成myhacked.Connection,则会对数据库造成不可知的破坏;


5. 安全性


如果字符串是不可变的,则会引起很严重的安全问题;例如数据库的用户名和密码都是以字符串形式传入获得数据库的连接,socket编程中,主机名等都是以字符串形式传入,如果字符串可变,黑客可以很容易改变字符串对象的值,造成安全漏洞。


三、String直接创建对象(String s=“abc”)和intern()方法的区别

两者在创建字符串对象先去字符串常量池查找,如果有,直接返回该字符串的引用,没有则在字符串常量池创建并返回引用,看上去两者无差别,但是这样那么intern存在的意义为何?


测试代码 Test.java


   String s1 = "ab";

   String s2 = "c";

   String s3 = "abc";

 

   System.out.println(s3=="ab"+"c");           //true

   System.out.println(s3==s1+s2);              //false

   System.out.println(s3==(s1+s2).intern());   //true


编译代码 Test.class


   String s1 = "ab";

   String s2 = "c";

   String s3 = "abc";

   System.out.println(s3 == "abc");

   System.out.println(s3 == s1 + s2);

   System.out.println(s3 == (s1 + s2).intern());


“ab”+“c”;字符串拼接在编译期可以确定其值,进而可以在编译阶段确定该字符串是否存在于字符串常量池;但是s1+s2;字符串引用拼接需要在运行期才能得到结果,指望不上编译器的字符串常量池优化策略了,这时候intern方法作用便体现了,在运行期确定常量池是否有需要创建的字符串对象,如果有,返回其字符串常量池的引用。故得出的结论:String直接赋值和intern方法在字符串常量池的优化策略上,一个体现在编译期,一个则在运行期。


四、StringBuilder和"+"号的区别?

1. 拼接字符串常量


测试类


    String s1 = "a" + "b" + "c";

    String s2 = new StringBuilder().append("a").append("b").append("c").toString();

编译class


   String s1 = "abc";

   String s2 = "a" + "b" + "c";


字节件ByteCode


   L0

    LINENUMBER 6 L0

    LDC "abc"

    ASTORE 1

   L1

    LINENUMBER 7 L1

    NEW java/lang/StringBuilder

    DUP

    INVOKESPECIAL java/lang/StringBuilder.<init> ()V

    LDC "a"

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    LDC "b"

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    LDC "c"

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;

    ASTORE 2


使用“+”进行字符串常量的拼接在编译时就已经完成,而使用 StringBuilder 进行字符串拼接需要在运行时完成。所以单纯的字符串常量拼接“+”的效率 应该高于 StringBuilder


2. 拼接字符串和引用


测试类


   String s1 = "a";

   String s2 = new StringBuilder().append(s1).append("b").append("c").toString();

   String s3 = s1 + "b" + "c";

编译class


   String s1 = "a";

   String s2 = s1 + "b" + "c";

   String s3 = s1 + "bc";

``

字节码ByteCode

```c

L0

    LINENUMBER 6 L0

    LDC "a"

    ASTORE 1

   L1

    LINENUMBER 7 L1

    NEW java/lang/StringBuilder

    DUP

    INVOKESPECIAL java/lang/StringBuilder.<init> ()V

    ALOAD 1

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    LDC "b"

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    LDC "c"

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;

    ASTORE 2

   L2

    LINENUMBER 8 L2

    NEW java/lang/StringBuilder

    DUP

    INVOKESPECIAL java/lang/StringBuilder.<init> ()V

    ALOAD 1

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    LDC "bc"

    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;

    ASTORE 3


从字节码可以看出“+”拼接的字符串引用底层还是使用StringBuilder


参考链接

Java中的String,StringBuilder,StringBuffer三者的区别?


java中String类为什么不可变?


java String中的intern和String a="abc"的区别是什么?


相关文章
|
17天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
8天前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
|
12天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
1044 33
|
11天前
|
机器学习/深度学习 人工智能 搜索推荐
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
近期发生了什么自 2025 年 2 月 OpenAI 正式发布Deep Research以来,深度研究/深度搜索(Deep Research / Deep Search)正在成为信息检索与知识工作的全新范式:系统以多步推理驱动大规模联网检索、跨源证据。
805 55
|
9天前
|
文字识别 测试技术 开发者
Qwen3-VL新成员 2B、32B来啦!更适合开发者体质
Qwen3-VL家族重磅推出2B与32B双版本,轻量高效与超强推理兼备,一模型通吃多模态与纯文本任务!
697 11
下一篇
开通oss服务