0
点赞
收藏
分享

微信扫一扫

面试题解析String s3 = new String(“a“) + new String(“b“); s3.intern(); String s4 = “ab“; Sout(s3 == s4);

洲行 2022-03-12 阅读 77
java

该问题解析需要点前提知识

1、new String("a") 一共创建了两个对象,一个new 对象存在堆空间中,一个“a”存在堆空间的字符串常量池中。

2、关于字符串拼接操作

@Test
public void test1(){
    String s1 = "a";
    String s2 = "b";
    String s3 = "ab";
    String s4 = "a" + "b";
    String s5 = s1 + s2;
    System.out.println(s3 == s4);//true
    System.out.println(s3 == s5)//false
}

@Test
public void test2(){
    final String s1 = "a";
    fianl String s2 = "b";
    String s3 = "ab";
    String s4 = "a" + "b";
    String s5 = s1 + s2;
    System.out.println(s3 == s4);//true
    System.out.println(s3 == s5)//true
}

test1()字节码                                                                test2()字节码

 

通过两个方法的字节码文件和反编译字节码文件可以看到

1.如果拼接符号左右有至少一个是String类型的变量,就会创建一个StringBuilder对象,在使用对象的append方法将左右两边的数据添加到该对象中,再使用toString()方法,toString()方法会生成一个新的String对象,再将String对象的索引传给赋值符号右边的变量

2.如果拼接符号左右都是字符串常量和常量引用,则仍然使用编译期优化,即非StringBuilder的方式

 

3、intern()方法

        1.作用

        查询字符串常量池中是否具有该字符串,如果有,返回该字符串的索引,如果没有,在字符串常量池中创建该字符串然后返回索引

        2.jdk6 和 jdk7之后的改变(区别的原因在于字符串常量池存储位置的改变)

        jdk6中字符串常量池仍然保存在方法区中,此时调用intern(),如果该字符串不存在于字符串常量池中,会在池中创建该字符串并返回地址值 。jdk7以后,字符串常量池保存在堆空间中,此时调用intern(),如果该字符串不存在于字符串常量池中且堆空间拥有保存该值的String对象, 则字符串常量池中会保存该String对象的地址,此后,如果有String对象使用字面量的方式定义,该对象直接指向了堆空间中保存该字面量的String对象。

        验证代码如下:

StringBuilder stringBuilder = new StringBuilder();
    //不能直接使用字面量"123"不然会在常量池中创建该字符串
    stringBuilder.append("1");
    stringBuilder.append("2");
    stringBuilder.append("3");
    String s5 = stringBuilder.toString();
    s5.intern();
    String s = "123";
    System.out.println(s == s5); //jdk7 true jdk6 false

4、面试题 

    String s3 = new String("a") + new String("b");
    s3.intern();
    String s4 = "ab";
    System.out.println(s3 == s4);  //jdk 6 false jdk 7以后true

该段代码一共创建了6个对象:"a" "b" 两个new出来的String对象 StringBuilder对象 StringBuilder.toString()中创建的String对象 (详情见字符串拼接操作)

那为什么字符串常量池中没有"ab"的创建呢?主要在于StringBuildertoString()方法中创建String对象的构造方法与new String("ab")的构造方法不同。

    #StringBuilder的toString()方法
    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

    #toString调用的构造器
    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

    #new String 调用的构造器
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

从上面可以清晰的看出toString()调用的构造方法是直接复制StringBuilder数组中的值,并没有使用到字面量"ab",字符串中常量池中不会创建字符串"ab",满足了字符串不存在于字符串常量池中 。调用构造方法后会在堆空间生成保存"ab"的String对象,结合intern()jdk6和jdk7之前的改变可知,s4直接指向了s3对象实体的位置,所以s3 == s4 为true

举报

相关推荐

0 条评论