String到底创建了几个对象

本文详细探讨了Java中String对象的创建过程,包括常量池与堆内存的区别,以及不同创建方式下的对象数量。通过分析`String`的`intern()`方法以及字符串拼接的效率问题,强调了`StringBuilder`在性能优化中的作用。此外,还介绍了`==`与`.equals()`在比较字符串时的不同。
摘要由CSDN通过智能技术生成

最近在看java基础知识时,看到一些有关String的面试题,发现有许多有关string的知识点理解不够透彻。在此结合一些常见面试题做一下记录

创建几个对象
实例代码:

    public static void testString1(){
        //此句代码会产生两个对象,一个在编译类加载(解析)阶段产生,一个在运行时产生。
        String s1= new String("weiwei");
        //________________________________________  

        //先在常量池创建对象
        String s2 = "zhang";
        //创建一个对象
        String s3 = new String("zhang");
        //________________________________________

        //创建一个对象
        String s4 = "a"+"b"+"c";
        //________________________________________

        String s5 = "helloworld";
        final String s6 = "hello";
        //没有创建对象
        String s7 = s6+"world";

        //________________________________________ 

        String s8 = "how are you";
        String s9 = "how are";
        //创建一个对象
        String s10 = s7+"you";

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
说明: 
加载执行s1语句的时候,会产生两个对象,第一个对象是在类加载的解析阶段,会将字符串“weiwei”在常量池中创建(首先在常量池查找,此处假设其他地方的代码中没有此字符串常量),第二个对象时在代码运行期间,在堆上创建内容为“weiwei”的对象。

执行s2,s3语句,首先s2语句,在加载阶段和s1语句加载相同,常量池不存在,会在常量池中创建对象。s3语句,在类加载解析阶段,由于s2语句已经确保在常量池中存在“zhang”对象,故不会产生对象,在运行期间,new关键字,会在堆上产生一个对象。故s3语句只会生成一个对象。

执行s4语句,首先java文件编译的时候,会将s4语句优化为 String s4 = “abc”;故s4语句,会在类的加载解析阶段,创建一个“abc”对象存放在常量池中,在运行期间,不会创建新的对象,而是将常量池“abc”的引用,赋值给s4。

执行 s5,s6,s7语句,首先s5语句,同s4语句相同,会在常量池中产生一个对象。s6,s7语句在编译期间会进行优化,s6直接优化成字符串字面常量,s7会优化成 s7=”hello”+”world”也即是 s7=”helloworld”;s7语句不会新建对象。

执行s8,s9,s10,s8和s9同s2语句执行逻辑相同。s10在编译期间,会将+号编译成 StringBuild.append(),在装载阶段“you”会在字符串常量池产生一个对象; 由于StringBuild会产生一个对象,故s10语句会产生两个对象。 
对应String对象内存图: 


对象是否相等
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1 == s2);                     
        System.out.println(s1.equals(s2)); 
1
2
3
4
输出结果:true,true。 
说明:s1和s2都指向字符串常量池中的“abc”对象,故s1与s2地址相同(==比较地址是否相同,比较是否为同一个对象)。字符串重写了equals方法,同一个对象返回true,内容相同,也返回true。

        String s3= new String("abc");
        String s4 = "abc";
        System.out.println(s3 == s4);                     
        System.out.println(s3.equals(s4));
1
2
3
4
输出结果:false,true。 
说明:s3指向堆中的“abc”对象。s4指向常量池中的对象。s3与s4不指向同一个对象,故第一个返回false。s3与s4内容相同,输出结果true。

        String s5 = "a"+"b"+"c";
        String s6="abc";
        System.out.println(s5== s6);                     
        System.out.println(s5.equals(s6));
1
2
3
4
输出结果:true,true。 
说明:s5在编译期间会优化为 s5=”abc”。此时和上面的s1和s2代码逻辑一样。输出结果和s1 s2相同。

        String s7 = "ab";
        String s8 = "abc";
        String s9 = s7+"c";

        System.out.println(s9 == s8);
        System.out.println(s9.equals(s8));
1
2
3
4
5
6
输出结果:false,true。 
说明:s8指向字符串常量池中的“abc”对象,s9语句会在编译期间,优化为stringBuild(s7).append(“c”)。也就是s9最终指向stringBuild.toString()方法返回的“abc”对象(在堆上)。故s9!=s8;内容相同,所以equals返回true。

        String s10 = "wenwei";
        final String s11 = "wen";
        String s12 = s11+"wei";
        System.out.println(s10==s12);
1
2
3
4
输出结果:true。 
说明:s10指向常量池中的“wenwei”对象。由于s11字符串被final修饰,也就是不可改变常量,s12语句在编译的时候,会优化成s12=”wen”+”wei”。此时s10和s12和s5 s6指向逻辑相同。

        String s13 = new String("zhang");
        s13.intern();
        String s12 = "zhang";
        System.out.println(s13 == s12);
1
2
3
4
输出结果:false。 
说明:intern(),方法在jdk1.7中执行逻辑是:先检查常量池是否有改字符串对象;如果没有则将s13在堆中的对象引用,赋值给常量池中的变量,并返回本引用,如果存在则直接返回引用。s13代码会在类加载解析阶段,在常量池中生成“zhang”对象,s13.intern()方法,检查常量池存在,不会将s13堆中的对象引用赋值到常量池中;s12语句,指向的是常量池中的对象,s13指向堆中新建的对象。故输出结果为false。

        String s14 = new String("1") + new String("1");
        s14.intern();
        String s15 = "11";
        System.out.println(s14 == s15);
1
2
3
4
输出结果:true。 
说明:s14语句会在堆中产生一个“11”对象,s14.intern()语句,会将对堆中的“11”对象引用记录的常量池中(jdk1.7以后)。s15语句指向后,s15的值即为常量池中引用的值,也指向堆中的“11”对象。故输输出值为true。

代码优劣
//代码片段1
        String result1="";
        for(int i =0;i<100;i++){
            result1+=i;
        }
        System.out.println(result1);

//代码片段2
        StringBuilder sb = new StringBuilder();
        for(int i =0;i<100;i++){

            sb.append(i);
        }
        System.out.println(sb.toString());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
说明:代码片段1中的result+=i语句编译器编译会优化为,reuslt = new StringBuilder(result).append(i).toString()。这样片段1代码执行100次循环,会创建100个StringBuilder对象,和100个result对象(toString()方法)。代码片段2执行100次循环,就创建了1个StringBuilder和1个String对象。故代码片段2内存占用更少,性能最佳。注意,在java中字符串的+操作,如果连接的不是常量字符串或final修饰的字符变量,编译后都会编译成StringBuilder().append()的形式。 
希望对您有所帮助!


————————————————
版权声明:本文为CSDN博主「GeeK_1024Wei」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_22494029/article/details/79306182

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值