前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java面试题:new String("abc")创建几个对象;String a="abc",String b=new String("abc")区别

java面试题:new String("abc")创建几个对象;String a="abc",String b=new String("abc")区别

原创
作者头像
AmbitionResponsibility
发布2024-04-21 22:13:22
1160
发布2024-04-21 22:13:22
举报

还记得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 charcapacity,append后调用StringBuilder的toString()进行类型转换,实则为new String("abcd")。三个对象分别为——new StringBuilder()、new String(value,0,count)、"abcd"
代码语言:java
复制
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方法、

代码语言:java
复制
//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"比较

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

代码语言:java
复制
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() 方法,则主动将常量池中 还没有的字符串对象放入池中,并返回地址。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.String str1="abc"和String str2=new String(“abc”)区别
  • 2.String str="abc",String str=new String("abc")创建了几个对象
  • 3.String str =“ab”+ “cd”, String str =new String(“ab”) + new String(“cd”) 会创建几个对象
  • 4.String a="abc",String b=new String("abc"),String c="ab"+"c"比较
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com