JVM-字符串拼接原理

1.那些字符串会进入字符串常量池

  1. 直接写的字面量
  2. 字面量的拼接结果(注意:如果字符串拼接中有变量则结果不会进入字符串常量池)
  3. 调用String的intern方法可以将String存入字符串常量池

2. 字面量的拼接原理

  • java源码

    package com.hgy;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class hello {
    
    	public static void main(String[] args) {
    		String a = "hello" + " world";
    	}
    }
    
    
  • 在idea中查看编译后的class文件

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.hgy;
    
    public class hello {
        public hello() {
        }
    
        public static void main(String[] args) {
            String a = "hello world";
        }
    }
    
    
  • 结论:

    • 以上面两个文件我们可以看出,这种字符串的拼接在编译期间就已经优化了,直接就合并为一个字符串;并且这个字符串存放在字符串常量池

3. 字符串和变量拼接原理

  • java源码

    package com.hgy;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class hello {
    
    	public static void main(String[] args) {
    		String v = "java";
    		String a = v + "hello" + " world";
    	}
    }
    
  • 利用jclasslib查看main方法的字节码命令

    • 如果一下名词不明白请阅读java虚拟机栈
    • 我们可以发现就简单的两行代码,产生了这么多的字节码命令;在代码中我简单解释了每一行的作用,
     0 ldc #2 <java>   // 从字符串常量池加载java
     2 astore_1  	   // 存储常量到索引为1的局部变量表中
     3 new #3 <java/lang/StringBuilder>  //给StringBuilder对象分配内存空间
     6 dup
     7 invokespecial #4 <java/lang/StringBuilder.<init>> //执行StringBuilder的构造方法
    10 aload_1   //获取局部变量表索引为1的引用地址,
    11 invokevirtual #5 <java/lang/StringBuilder.append> //把上面加载的内容作为参数传递给append方法
    14 ldc #6 <hello world> // 从字符串常量池加载hello world
    16 invokevirtual #5 <java/lang/StringBuilder.append> //把上面加载的内容作为参数传递给append方法
    19 invokevirtual #7 <java/lang/StringBuilder.toString> //调用toString方法
    22 astore_2 //结果存储到局部变量表
    23 return
    
  • 以上内容我们可以知道字符串拼接实际上就是创建了一个StringBuilder对象然后向里面append内容,最后调用toString方法获得结果

3.1 为什么结果没有存储在常量池
  • 从上述字节码指令已经知道了字符串拼接结果是StringBuilder的toString方法的结果,那么toString里面具体做了什么事情,又是为什么结果不在常量池?

  • 一下是StringBuilder.toString的源码以及字节码指令

        @Override
        public String toString() {
            // Create a copy, don't share the array
            //此处value为一个char数组【我的jdk版本为jdk8】
            return new String(value, 0, count);
        }
    
    
     0 new #80 <java/lang/String>
     3 dup
     4 aload_0
     5 getfield #234 <java/lang/StringBuilder.value>
     8 iconst_0
     9 aload_0
    10 getfield #233 <java/lang/StringBuilder.count>
    13 invokespecial #291 <java/lang/String.<init>>
    16 areturn
    
  • 以上代码可以很好的解释实际上最终是调用了String的构造方法传入一个char数组,那么最终的结果肯定也就在咱么的堆空间

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐