关于 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"的区别是什么?


相关文章
|
14天前
string的使用
string的使用
24 0
|
1月前
|
算法 Linux C语言
【c++】string
【c++】string
25 1
|
2月前
|
索引
String
String当中与获取相关的常用方法有: public int length():获取字符串当中含有的字符个数,拿到字符串长度。 public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串。 public char charAt(int index);获取指定索引位置的单个字符。(索引从0开始。) public int index0f(Stringstr):查找参数字符串在本字符串当中首次出现的索引位置,如果 没有返回-1值。
21 0
string trimming
string trimming
87 0
string.gmatch
string.gmatch
152 0
string.gsub
string.gsub
192 0
|
存储 缓存 Java
你并不了解 String
你并不了解 String
你并不了解 String
|
缓存 安全 Java
String 为什么不可变 ?
String 为什么不可变 ?
String 为什么不可变 ?
|
存储 缓存 安全
为什么 String 是不可变的?
我最喜欢的 Java 面试问题,不好回答,但同时也非常有用。一些面试者也常问这个问题,为什么 String 在 Java 中是 final 的。 字符串在 Java 中是不可变的,因为 String 对象缓存在 String 池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户。
108 0
为什么 String 是不可变的?
|
Java 编译器
String那些事
String那些事
115 0
String那些事

热门文章

最新文章