java面试题:new String(“abc“)创建几个对象;String a=“abcd“,String b=new String(“abcd“),String c=“ab“+“cd“区别


还记得String、StringBuilder、StringBuffer区别、String底层原理、实例化、拼接、比较吗?如果忘记可以到这里重温复习 String、StringBuilder、StringBuffer区别;String底层详解,实例化、拼接、比较;String为什么不可变

1.String str1="abc"和String str2=new String(“abc”)区别

有两种方式创建String对象:字面量赋值、new关键字

  • 使用字符串字面值创建String对象,如String str = “abc”:java中有个字符串常量池,当创建一个字面量字符串时,JVM首先检查字符串常量池中是否已经存在该字符串,如果存在 则直接返回字符串对象的引用,否则就创建一个新的字符串对象并放入字符串常量池中,最终将该对象的引用赋值给变量str。引用str指向常量池中字符串"abc"的地址,是在常量池中拿值【字符串常量池中不会存储相同内容的字符串】
    在这里插入图片描述

  • 使用构造函数构建String对象,如String str = new String(“abc”):通过new关键字创建字符串对象,会先检查字符串常量池中是否有相同的字符串,如果有 则拷贝一份放到堆中,然后返回堆中地址;如果没有 就先在字符串常量池中创建"abc"这个字符串,而后再复制一份放到堆中 并把堆地址返回给str。即最终字符串常量池和堆内存都会有这个对象,最后返回的是堆内存的对象引用

在这里插入图片描述

只要使用new方法,不管字符串常量池中是否存在"abc",都会在堆中创建新的对象(注意 和字符串常量池中的"abc"相区分),方式一的效率高于方式二

由于new关键字会在堆中开辟空间,因此开发中一般不建议使用,直接用字面量形式赋值即可。

2.String str=“abc”,String str=new String(“abc”)创建了几个对象

经过上文讲解,我们就知道两者区别在于 创建对象个数不同

  • String str=“abc"创建了几个对象? 0个 或 1个。如果字符串常量池中没有"abc”,则在常量池中创建"abc" 并让str引用指向该对象(1个);如果字符串常量池中有"abc",则一个都不创建 直接返回地址值给str(0个)
  • String str=new String(“abc”)创建了几个对象? 1个 或 2个。如果字符串常量池中没有"abc",则在字符串常量池和堆内存中各创建一个对象,返回堆地址(2个,一个是堆中new的对象,另一个是char[]对应的常量池中数据"abc");如果常量池中有"abc",则只在堆中创建对象并返回地址值给str(1个)。【new相当于在堆中新建了value值,每new一个对象就会在堆中新建,地址值也因此不同,堆中的value存储着指向常量池的引用地址】

在这里插入图片描述

3.String str =“ab”+ “cd”, String str =new String(“ab”) + new String(“cd”) 会创建几个对象

在Java中从".java"文件编译成".class"文件,会有一个优化器去优化我们的代码。

  • String str =“ab”+ “cd”:0个1个。常量相加,经过编译器优化成了String str =“abcd”。如果字符串常量池中没有"abcd”,则在常量池中创建"abcd" 并让str引用指向该对象(1个);如果字符串常量池中有"abcd",则一个都不创建 直接返回地址值给str(0个)
  • String str1=“ab”, String str2=“cd”, String str=str1+str2:String str=str1+str2 至多创建3个对象(String str1=“ab”、str2="cd"没有计入)。变量相加,经过编译器优化成StringBuilder,底层实现为 (new StringBuilder()).append(a).append(b).toString(),new StringBuilder()底层调用new char[capacity],append后调用StringBuilder的toString()进行类型转换,实则为new String(“abcd”)。三个对象分别为——new StringBuilder()、new String(value,0,count)、“abcd”
public class StringExercise05 {
    public static void main(String[] args) {
        String a = "ab"; //创建a对象
        String b = "cd"; //创建b对象
        //解读:先创建一个StringBuilder sb = new StringBuilder();执行 sb.append(a);执行sb.append(b);String c = sb.toString();
        //等价于 (new StringBuilder()).append(a).append(b).toString()
        //最后其实是 c 指向堆中对象的(String) value[]->池中 "abcd"
        String c = a + b;  
        String d = "abcd";
        System.out.println(c == d); //false

        String e = "ab" + "cd";
        System.out.println(d == e);//true
    }
}

一共创建3个对象,内存布局图如下

在这里插入图片描述
查看源码 StringBuilder构造方法、append方法、

//StringBuilder构造方法
public StringBuilder() {
    super(16);
}
//追溯到父类AbstractStringBuilder的构造方法,分配一个长度为16的char数组
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}


//StringBuilder的append方法
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
//追溯到父类AbstractStringBuilder的append方法
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);   //getChars方法见下
    count += len;
    return this;
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin < 0) {
        throw new StringIndexOutOfBoundsException(srcBegin);
    }
    if (srcEnd > value.length) {
        throw new StringIndexOutOfBoundsException(srcEnd);
    }
    if (srcBegin > srcEnd) {
        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    }
    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

//我们的代码没有超过16个,故不会出现扩展value的情况。append使用arraycopy的复制方法,也没有产生新的对象,而StringBuilder的toString()方法通过前面数组new了一个新String
@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}
  • String str =new String(“ab”) + “cd”:至多5个。分别为 new StringBuilder()、new String(“ab”)、常量池中的"ab"、StringBuilder 的 toString() 调用 new String(“abcd”)、常量池中的"abcd"
  • String str =new String(“ab”) + new String(“cd”):至多7个。分别为 new StringBuilder()、new String(“ab”)、常量池中的"ab"、new String(“cd”)、常量池中的"cd"、StringBuilder 的 toString() 调用 new String(“abcd”)、常量池中的"abcd"

注意:

1)new String(“ab”)时,如果常量池中已有"ab",则不会在常量池中新建"ab",因为在常量池中只能存在一份相同的对象,即字符串常量池中不会存储相同内容的字符串

2)StringBuilder的toString(),在字符串常量池中,没有生成"ab"。如果前后文还有代码、并且字符串常量已在常量池中存在时,相同的常量将不会再创建,故至多6个对象。

参考 https://blog.csdn.net/xiaojin21cen/article/details/106404531

4.String a=“abc”,String b=new String(“abc”),String c=“ab”+"c"比较

上文已介绍三种方式的原理

String a = "abc";    //字符串常量池
String b = "abc";
String c = new String("abc");    //new创建对象,堆和常量池中都会有该对象
String c1 = new String("abc");
String d = "ab" + "c";   //常量与常量拼接,结果在常量池中。查找常量池中是否存在"abc",如果存在 则让d直接引用
String e = new String("ab") + new String("c");

//a、b、c、c1、d、e 使用equals比较,由于字符串内容都相等 所以均会返回true
//故此处重点使用==,观察是否为同一个对象
System.out.println("a==b:" + (a==b));   //true
System.out.println("c==c1:" + (c==c1)); //false
System.out.println("a==c:" + (a==c));   //false
System.out.println("a==d:" + (a==d));   //true
System.out.println("c==d:" + (c==d));   //false

System.out.println("a==e:" + (a==e));   //false
System.out.println("c==e:" + (c==e));   //false
System.out.println("d==e:" + (d==e));   //false



String str1 = "ab";
String str2 = "cd";
String str3 = "ab" + "cd";
String str4 = "abcd";
System.out.println("str3==str4:" + (str3==str4));  //true

//内部实现 String temp = (new StringBuilder()).append(str1).append("cd").toString();
String str5 = str1 + "cd";
System.out.println("str5==str4:" + (str5==str4));  //false
//内部实现 String temp1 = (new StringBuilder()).append(str1).append(str2).toString();
String str6 = str1 + str2;
System.out.println("str6==str4:" + (str6==str4));  //false
str5 = str5.intern();  //将str5放进常量池,并将引用赋给原来的str5
System.out.println("str5==str4:" + (str5==str4));  //true

在这里插入图片描述

  • String str1 =“ab”+“cd”:常量 与 常量 的拼接结果在 常量池,原理是 编译期 优化;常量池中不会存在相同内容的常量;
  • String str2 = str1+“ef”, String str6 = str1+str2:只要其中一个是变量(运行时才能知道),结果就在堆中。变量拼接的原理 是StringBuilder 【只要有变量参与 地址就指向堆中数据】
  • 如果拼接的结果调用 intern() 方法,则主动将常量池中 还没有的字符串对象放入池中,并返回地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值