52.String内存结构位置和String拼接操作(面试)

 

1.String内存结构位置

1.Java中的8种基本数据类型以及String,为了使他们在运行过程中更快,更节省内存,都提供了常量池的概念。
2.8种基本数据类型是由系统协调的,String类型的常量池主要的使用方法有两种:直接使用双引号的方式声明字符串常量;使用String提供的intern()方法。
在这里插入图片描述
3.JDK6及以前,字符串常量池放在永久代。JDK7及以后,字符串常量池放在堆区。
为什么StringTable要从永久代放到堆区?
在这里插入图片描述

2.String的拼接操作

1.常量与常量的拼接结果放在常量池,原理是编译期优化
2.常量池中不会存放相同内容的常量
3.拼接操作,只要其中一个是变量,结果就放在堆中。底层使用的是StringBuilder
4.如果拼接后的结果调用intern()方法,则会先去常量池中检查存不存在该字符串,如果不存在,就会将该字符串放入常量池中,否则直接返回常量池中已经存在的字符串的地址
在这里插入图片描述
例子:

/**
 * 字符串拼接操作
 */
public class StringTest5 {
    @Test
    public void test1(){
        String s1 = "a" + "b" + "c";//常量与常量的拼接操作,结果放在常量池。编译期优化:等同于"abc"
        String s2 = "abc"; //"abc"一定是放在字符串常量池中,将此地址赋给s2
        /*
         * 在编译阶段,上面的代码就已经被替换成如下的代码了
         * String s1 = "abc";
         * String s2 = "abc"
         */
        System.out.println(s1 == s2); //true
        System.out.println(s1.equals(s2)); //true
    }
 
    @Test
    public void test2(){
        String s1 = "javaEE";
        String s2 = "hadoop";
 
        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";//编译期优化
        //如果拼接符号的前后出现了变量,则相当于在堆空间中new String(),具体的内容为拼接的结果:javaEEhadoop
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;
 
        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//false
        System.out.println(s3 == s7);//false
        System.out.println(s5 == s6);//false
        System.out.println(s5 == s7);//false
        System.out.println(s6 == s7);//false
        //intern():判断字符串常量池中是否存在javaEEhadoop值,如果存在,则返回常量池中javaEEhadoop的地址;
        //如果字符串常量池中不存在javaEEhadoop,则在常量池中加载一份javaEEhadoop,并返回次对象的地址。
        String s8 = s6.intern();
        System.out.println(s3 == s8);//true
    }
}

5.体会字符串拼接操作,底层StringBuilder的使用
3点讲过,字符串拼接操作,只要其中一个是变量,结果就放在堆中。底层使用的是StringBuilder
具体的细节是:先new一个StringBuilder出来,然后调用append方法,将字符串加入new出来的StringBuilder中,最后调用toString方法生成最终的字符串。(例如代码中的s1 + s2操作)
6.字符串拼接操作不一定使用的是StringBuilder。如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非StringBuilder的方式。
例如代码中的s1s2使用final修饰,实际上它们已经成为了字符串常量。如果将它们进行拼接,相当于是常量与常量的拼接,结果会直接放在常量池中。

final String s1 = "a"; // final 修饰,s1实际上已经成为了字符串常量
final String s2 = "b"; // final 修饰,s1实际上已经成为了字符串常量
String s4 = s1 + s2; // 这里相当于是常量和常量的拼接,所以结果是放在常量池中

例子:

/**
 * 字符串拼接操作
 */
public class StringTest5 {

    @Test
    public void test3(){
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        /*
        如下的s1 + s2 的执行细节:(变量s是我临时定义的,底层使用的是StringBuilder,但不一定是s)
        ① StringBuilder s = new StringBuilder();
        ② s.append("a")
        ③ s.append("b")
        ④ s.toString()  --> 约等于 new String("ab")
 
        补充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer
         */
        String s4 = s1 + s2;//
        System.out.println(s3 == s4);//false
    }
    /*
    1. 字符串拼接操作不一定使用的是StringBuilder!
       如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非StringBuilder的方式。
    2. 针对于final修饰类、方法、基本数据类型、引用数据类型的量的结构时,能使用上final的时候建议使用上。
     */
    @Test
    public void test4(){
        final String s1 = "a"; // final 修饰,s1实际上已经成为了字符串常量
        final String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2; // 这里相当于是常量和常量的拼接,所以结果是放在常量池中
        System.out.println(s3 == s4);//true
    }
    //练习:
    @Test
    public void test5(){
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.out.println(s1 == s3);//false
 
        final String s4 = "javaEE";//s4:常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5);//true
 
    }
}

 

posted @ 2020-11-18 21:50  跃小云  阅读(170)  评论(0编辑  收藏  举报