java String创建字符串对象——面试java基础

2019-06-20  本文已影响0人  small瓜瓜

在一次面试中,被问到这方面的问题,搞得我一脸蒙逼,那时候还没去了解JVM,为了以后不再出现这样的情况,我就在这里好好梳理一下String创建会创建多少个对象呢?

先看代码

public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "a" + "b" + "c";
        System.out.println("结果1:" + (str1 == str2));
        String str3 = "a";
        String str4 = str3 + "bc";
        System.out.println("结果2:" + (str1 == str4));
        System.out.println("结果3:" + (str1 == str4.intern()));
        final String str5 = "a";
        String str6 = str5 + "bc";
        System.out.println("结果4:" + (str1 == str6));
    }

代码运行结果:

结果1:true
结果2:false
结果3:true
结果4:true

上面的运行结果需要从两个方面来说明:
一:java编译器
二:java字符串常量池

java编译器

我们来看看java编译器将上面的源码编译成了什么样子。(反编译结果)

public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";
        System.out.println("结果1:" + (str1 == str2));
        String str3 = "a";
        String str4 = str3 + "bc";
        System.out.println("结果2:" + (str1 == str4));
        System.out.println("结果3:" + (str1 == str4.intern()));
        String str5 = "a";
        String str6 = "abc";
        System.out.println("结果4:" + (str1 == str6));
    }

源代码和编译后的代码有很多不同点。str2和str6都变成了直接赋值为"abc",而str4却没有变化,这里也是java新手不太清楚的地方,其实由于常量字符串是在编译的时候就是可以被确定的,又因"a","b"和"c"都是字符串常量,因此变量str2的值在编译时就可以确定,同理因为str5被final关键字修饰,也相当于字符串常量,因此变量str6的值也在编译时就可以确定,代码编译后就和String str="abc"一样了。
也是因为上面的原因str4没有改变,因为str4并不能在编译期确定。

java字符串常量池

在执行代码String str1 = "abc"时,JVM先去字符串常量池中查找,看是否已经存在值为”abc”的对象,如果存在,则不再创建新的对象,而直接返回已存在对象的引用;反之,则先创建一个新的对象,然后加入到字符串池中,再将其引用返回。

String str1 = "abc";  // 会创建一个新的对象"abc"在字符串常量池中
String str2 = "abc";  // 因为字符串常量池中已经存在"abc",所以不会创建对象

这样结果1true,就可以理解了

String str3 = "a";  // 会创建一个新的对象"abc"在字符串常量池中
/* 
下面这行代码一共会创建三个对象:
  1. 会在字符串常量池中创建一个"bc"对象
  2. str3与 "bc" 进行字符串连接时,底层通过StringBuffer进行连接,生成一个
    StringBuffer对象(StringBuffer内部字符不考虑)
  3. 然后通过StringBuffer的append方法连接,通过toString()方法,将StringBuffer对象
    转为String,此时会产生一个新的堆内存地址,str4指向这个新的内存地址。
*/
String str4 = str3 + "bc"; 
// 因为str4在堆内存变量中,str1在字符串常量池中,所以==地址比较为false
System.out.println("结果2:" + (str1 == str4));
// str4.intern() 返回在字符串常量池中的"abc"引用,就和str1是同一对象,为true
System.out.println("结果3:" + (str1 == str4.intern()));

字符串相加推荐文章

看完上面的代码相信您已经对字符串创建对象个数有了一定的了解,接下来我们再来看看另一种情况。

/*
下面的代码,一共会创建两个对象:
  1. 在字符串常量池中创建"abc"对象
  2. 在堆中创建的原实例字符串对象
*/
String str1 = new String("abc");
// str1只是java栈中的对象引用

注意:String类型的引用是存在栈里。而字符串"abc"是存在字符串常量池中

// 在字符串常量池中创建一个"abc"对象
String str1 = "abc";
// 由于"abc"已经在字符串常量池中已经存在,所以不会再创建。
// 使用new关键字创建字符串,一定会在堆中创建一个实例,所以下面代码会创建一个对象
String str2 = new String("abc");
// str1只是java栈中的对象引用

注意:String类型的引用是存在栈里。而字符串"abc"是存在字符串常量池中

下面提升难度

  public static void main(String[] args) {
        String str1 = "a" + new String("b");
        String str2 = "ab";
        System.out.println(str1 == str2);
        String str3 = str2 + "c";
        str3.intern();
        String str4 = "abc";
        System.out.println(str3 == str4);
    }

运行结果:

false
true

注意:上面的运行结果和jdk版本有关,1.7和1.8都是上面的结果,1.7以下不是
上面的结果重点就是intern方法的作用,这里我只是抛砖引玉,想了解更多的读者可以看看下面的这篇博客:
深入解析String#intern

笔者个人能力有限,如有错误,请联系我修改。

上一篇 下一篇

猜你喜欢

热点阅读