Java深入理解之String 类

本文深入探讨了Java中String对象的不可变性原理及其带来的好处,详细解释了字符串拼接过程中涉及的常量池机制,并介绍了如何使用intern方法管理字符串资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 为什么String对象是不可变的

 打开String类,查看源码,可以看到这几个成员变量

private final char[] value;

private final int offset;

private final int count;

可以看到String类是char数组的封装

count表示String的长度

offset表示偏移量

String的真实内容为偏移量和长度在数组中进行定位和截取

 

String的成员变量被final修饰,也就是说对象new之后,就无法改变其成员变量的值。所以说String对象是不可变的

 

这里有个误区就是

String a="abc";

       a="123";

这里a为啥可变?   其实这里要注意String 对象是指在堆区创建的对象。这里的a只是引用类型指向实际对象的地址。

 

2 关于字符串拼接和常量池

public class StringTest {

 

    public String append(String id) {

       .String a="abc" ;

        return s;

     }

 

 

}

  1. .String a="abc";

  2. String a="a"+"b"+"c";

  3. String a=new String("abc");

  4. String a=b+"_";

 

  • 替换1编译查看字节码

Constant pool:

   #1 = Methodref          #4.#19         //  java/lang/Object."<init>":()V

   #2 = String             #20            //  abc

   #3 = Class              #21            //  StringTest

   #4 = Class              #22            //  java/lang/Object

public java.lang.String append(java.lang.String);

   flags: ACC_PUBLIC

   Code:

     stack=1, locals=3, args_size=2

        0: ldc           #2                  // String abc

        2: astore_2

        3: aload_2

        4: areturn

可以看到字节码常量池里面已经有字符串abc了 

在方法append字节码中首先是idc #2 表示将常量池里面的值推入栈顶 并没有new一个string对象。所有可以理解为a此时为常量池abc的引用

 

  • 替换2编译查看字节码

 

Constant pool:

   #1 = Methodref          #4.#19         //  java/lang/Object."<init>":()V

   #2 = String             #20            //  abc

   #3 = Class              #21            //  StringTest

   #4 = Class              #22            //  java/lang/Object

 

常量池同样出现abc,说明代码编译的时候,已经将静态字符串已经做了拼接放入了常量池

 

 

  • 替换3编译查看字节码

 

Constant pool:

 

  #23 = Utf8               abc

public java.lang.String append(java.lang.String);

    flags: ACC_PUBLIC

    Code:

      stack=3, locals=3, args_size=2

         0: new           #2                  // class java/lang/String

         3: dup

         4: ldc           #3                  // String abc

         6: invokespecial #4                  // Method java/lang/String."<init>

":(Ljava/lang/String;)V

         9: astore_2

        10: aload_2

        11: areturn

 

常量池还是出现了abc ,并且在堆区创建了一个string对象。并且用常量池里面的字符串作初始化

 

 

    * 替换4编译查看字节码

 

 

  #26 = Utf8               abc  

public java.lang.String append(java.lang.String);

    flags: ACC_PUBLIC

    Code:

      stack=2, locals=3, args_size=2

         0: new           #2                  // class java/lang/StringBuilder

         3: dup

         4: invokespecial #3                  // Method java/lang/StringBuilder.

"<init>":()V

         7: aload_1

         8: invokevirtual #4                  // Method java/lang/StringBuilder.

append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

        11: ldc           #5                  // String abc

        13: invokevirtual #4                  // Method java/lang/StringBuilder.

append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

        16: invokevirtual #6                  // Method java/lang/StringBuilder.

toString:()Ljava/lang/String;

        19: astore_2

        20: aload_2

        21: areturn

      LineNumberTable:

        line 23: 0

        line 24: 20

 

字节码显示在用“+”拼接的时候,编译时创建了StringBuilder对象,并用其做字符串拼接,最后返回toString方法。但是在for循环里面做字符串拼接的时候需注意

 

 

public String append() {

    String s = "";

    for (int i = 0; i < 100; i++) {

        s += "abc";

 

    }

 

    return s;

}

 

 public java.lang.String append();

   flags: ACC_PUBLIC

   Code:

     stack=2, locals=3, args_size=1

        0: ldc           #2                  // String

        2: astore_1

        3: iconst_0

        4: istore_2

        5: iload_2

        6: bipush        100

        8: if_icmpge     37

       11: new           #3                  // class java/lang/StringBuilder

       14: dup

       15: invokespecial #4                  // Method java/lang/StringBuilder.

<init>":()V

       18: aload_1

       19: invokevirtual #5                  // Method java/lang/StringBuilder.

ppend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

       22: ldc           #6                  // String abc

       24: invokevirtual #5                  // Method java/lang/StringBuilder.

ppend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

       27: invokevirtual #7                  // Method java/lang/StringBuilder.

oString:()Ljava/lang/String;

       30: astore_1

       31: iinc          2, 1

       34: goto          5

       37: aload_1

       38: areturn

 

字节码显示每一次for循环都会创建StringBulid对象,所以此处用StringBulid来拼接,避免性能消耗

 

3 intern方法

 

String.intern() 作用 如果字符串已经包含一个等于String对象的字符串,则返回池中的对象,否则把对象包含的字符串添加到常量池,并返回引用, 但在jdk1.7中不再实现复制实例,只是在常量池记录首次出现的实例的引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值