String的一些面试题

参考文章:JVM Class详解之二 Method字节码指令

首先要注意:

  1. 常量池≠字符串常量池(常量池有:字符串常量池、运行时常量池、静态常量池)
  2. 字符串常量池在堆中(JDK1.7觉得字符串经常创建和销毁,放在方法区不合适就改到了堆中,这样GC回收概率大些)
  3. 这里字节码文件删除了部分无关内容
  4. ldc指令将int、float或String型常量值从常量池中推送至栈顶
  5. 我发现个规律: 貌似在Constant pool中的Utf8都是字符串常量池的❓

字节码文件怎么看

主要看打⭐的地方,这里只是大致说明

Constant pool:  /*⭐常量池,不是字符串常量池⭐*/
   #1 = Methodref          #11.#27        // java/lang/Object."<init>":()V  
   #2 = Class              #28            // java/lang/StringBuilder
   #3  .... 			/*⭐这些记录了常量池的信息⭐*/
{
  public StringTest();    /*⭐执行的类⭐*/
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:			
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:		/*⭐记录了编译出来的字节码指令和源码的对应关系⭐*/
        line 10: 0
      LocalVariableTable:				/*⭐每个方法都有LocalVariableTable,是本地变量表。⭐*/
        Start  Length  Slot  Name   Signature
            0       5     0  this   LStringTest;

  public static void main(java.lang.String[]);  /*⭐执行的方法⭐*/
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=2, args_size=1
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4:......		 /*⭐执行的代码⭐*/
      LineNumberTable:					/*⭐记录了编译出来的字节码指令和源码的对应关系⭐*/
        line 15: 0
        line 17: 35
      LocalVariableTable:			/*⭐每个方法都有LocalVariableTable,是本地变量表。⭐*/
        Start  Length  Slot  Name   Signature
            0      36     0  args   [Ljava/lang/String;
           35       1     1    s4   Ljava/lang/String;
}

面试题一:String s1 = “a” 创建了多少个对象?

创建了0个或1个对象,来看看该字节码文件,在#2可以看到创建了对象a(字符串常量池中),如果字符串a在执行到该句之前就存在字符串常量池则创建0个对象,否则在堆中的字符串常量池创建1个对象

Constant pool: 		
	.....
{

  public static void main(java.lang.String[]);
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // ⭐String a
         2: astore_1
         3: return
      LineNumberTable:
        line 12: 0
        line 17: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;
            3       1     1    s1   Ljava/lang/String;
}


面试题二:String s2 = new String(“b”) 创建了多少个对象?

创建1个或2个,该句的执行逻辑是:JVM先在堆中创建String对象,然后查看字符串常量池是否存在b,存在就直接返回字符串引用,不存在就创建然后再返回字符串引用。来看看该字节码文件,可以看到#2先创建了String对象(堆中),然后#3创建了b对象(字符串常量池中)

Constant pool:
	.....
{
  public static void main(java.lang.String[]);
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // ⭐class java/lang/String
         3: dup
         4: ldc           #3                  // ⭐String b
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: return
      LineNumberTable:
        line 13: 0
        line 17: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  args   [Ljava/lang/String;
           10       1     1    s2   Ljava/lang/String;
}


面试题三:String s2 = “a” + “b” 创建了多少个对象?

创建0个或1个对象,因为这里会经过编译器优化,等价于String s2 = “ab”,所以和面试题一是一样的,就不展示字节码文件了


面试题四:String s1 = “a”; String s2 = new String(“b”);String s3 = s1 + s2;第三句创建了多少个对象?

2个对象,说明如下:
执行第一句时,创建了0个或1个对象;第二句创建1个或2个(同面试题二);执行第三句时,会先创建StringBuilder对象(见字节码#6),等价于new StringBuilder().append("a").append("b").toString() ,由于toString()也是会创建一个String对象,所以第三句是创建了两个对象(ab没有放在字符串常量池)

Constant pool:
	......
{
  public static void main(java.lang.String[]);
    Code:
      stack=3, locals=4, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: new           #3                  // class java/lang/String
         6: dup
         7: ldc           #4                  // String b
         9: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        12: astore_2
        13: new           #6                  // ⭐class java/lang/StringBuilder
        16: dup
        17: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        20: aload_1
        21: invokevirtual #8      
         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: aload_2
        25: invokevirtual #8                  
        // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: invokevirtual #9                  // ⭐Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: astore_3
        32: return
      LineNumberTable:
        line 12: 0
        line 13: 3
        line 14: 13
        line 17: 32
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      33     0  args   [Ljava/lang/String;
            3      30     1    s1   Ljava/lang/String;
           13      20     2    s2   Ljava/lang/String;
           32       1     3    s3   Ljava/lang/String;
}


面试题五:String s4 = new String(“a”) + new String(“b”)创建了多少个对象?(假设字符串常量池在此之前为空)

6个;首先会创建一个StringBuilder对象用来等会拼接字符串,然后new String(“a”)和new String(“b”)分别各创建了2个对象(共4个),StringBuilder的toString()方法会new String(value,0,count)再创建一个对象,注意在字符串常量池中中没有ab(因为编译期间不能确定)。(⭐刚好对应创建的6个对象)

Constant pool:
   ......
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=2, args_size=1
         0: new           #2                  // ⭐class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: new           #4                  //⭐ class java/lang/String
        10: dup
        11: ldc           #5                  // ⭐String a
        13: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
        19: new           #4                  // ⭐class java/lang/String
        22: dup
        23: ldc           #8                  // ⭐String b
        25: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
        31: invokevirtual #9                  //⭐ Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: astore_1
        35: return
      LineNumberTable:
        line 15: 0
        line 17: 35
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      36     0  args   [Ljava/lang/String;
           35       1     1    s4   Ljava/lang/String;
}


---

总结

可以看出,如果是编译器可以确定的,字符串就会放入到字符串常量池,否则动态创建的字符串只会在堆中的对象,不会放入字符串常量池(字符串常量池也在堆中),除非调用String.intern()方法,其作用是将指定的字符串对象的引用保存在字符串常量池中

  • 2022.10.03 第一次修正
    – 修正了题四的错误
  • 2023.03.10 第二次修正
    – 修正了题四、五错误,精简了文章篇幅,删掉了大量无用的字节码篇幅
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值