JAVA中String对象涉及到的面试题

来源:互联网 发布:知乎 推荐最新的电影 编辑:程序博客网 时间:2024/05/17 11:04

我们以这段代码来做分析,阐述String对象的神奇之处

private class a{    }    /**     * @param Jovi     */    public static void main(String[] args) {        String s1 = "JAVA";        String s2 = "JAVA";        String s3 = new String("JAVA");        String s4 = new String("JAVA");        String s5 = "JA";        String s6 = "VA";        String s7 = "JA" + "VA";        String s8 = s5 + s6;        System.out.println(s1 == s2);       //true:S1 = S2,都是直接赋值的,共同指向线程池中的一个JAVA字符串        System.out.println(s1 == s3);       //flase:S3是new出来的,在堆中必定存在新的String对象,因此必定不等        System.out.println(s1 == s7);       //true:直接的字符串拼接,不涉及对象的重新创建,因此视为同一对象        System.out.println(s1 == s8);       //false:两个String对象通过+号链接,实际上产生了新的对象        System.out.println(s3 == s4);       //false:同第二点        System.out.println(s1 == s8.intern());//true:由于s1 equals s6 intern后常量池认为s6就对应了s1        System.out.println(s1 == s3.intern());//true:        System.out.println(s3 == s3.intern());//false:    }}

上述代码的结果已经在每条对应的备注上标明, 至于为何是这样的结果,且听我慢慢道来。
在JAVA中,String是一个特殊的对象,JVM为了提升运行效率,专门为其设计了一个名为字符串常量池的内存空间,专门来管理String对象。
在JAVA中创建String可以分为两类方法:
1、字面量直接赋值

String s1 = "java";

2、使用new关键字开辟

String s2 = new String("java");

同时由于有字符串拼接存在,又可以衍生出一下4种创建方式
1-1:字面量+字面量

String s3 = "ja"+"va";

1-2:字面量+变量

String s4 = "va";String s5 = "ja"+s4;

2-1:new(字面量+字面量)

String s6 = new String("ja"+"va");

2-2:new(字面量)+变量

String s4 = "va";String s7 = new String("ja")+s4;

我们首先来说一下第一种字面量赋值方式,JVM是如何处理的呢?首先,它会先到之前提到的字符串常量池中去寻找,有没有和它一样的(是否一样可以通过equals方法判断,其实就是字符串所有字母及排列顺序完全相同)字符串,如果有的话,那就不创建任何对象了,直接返回这个已经存在于常量池中的字符串的对象地址给s1变量。如果常量池中不存在呢,那就在池中创建该字符串,然后再返回引用。因此可以发现,通过这种方式创建的话,常量池中不会存在重复的字符串,这样就可以起到节省资源的作用

再来说第二种new方法:JVM首先在字符串池中查找有没有”java”这个字符串对象,如果有,则不在池中再去创建这个对象了,直接在堆中创建一个”java”字符串对象,然后将堆中的这个”java”对象的地址返回赋给引用s2,这样,s2就指向了堆中创建的这个”java”字符串对象,相当于创建了一个对象;如果没有,则首先在字符串池中创建一个”java”字符串对象,然后再在堆中创建一个”java”字符串对象,然后将堆中这个”java”字符串对象的地址返回赋给s2引用,这样,s2指向了堆中创建的这个”java”字符串对象,相当于直接创建了两个对象。

而对于字符串拼接,不管是1-1还是2-1,其实他们都属于字面量+字面量的方法,在编译期间就已经被拼在一起了,因此它们两种方式分别对应我上述提到的两种方式。

而对于1-2和2-2这种对应方式+变量的方式,实际上是分别在堆上创建了两个字符串对象”ja”和”va”然后将“+”号转换成StringBuffer.append()方法(中间过程涉及到JAVA的底层代码),然后JVM就可以通过append方法来拼接”ja”和”va”,最后生成java对象,这个过程实际上产生了好几个对象,因此字符串“+”拼接方法不宜多用。

好了,各种创建方式都已经介绍好了,接下来我们来讲解一下上述第一段代码块的执行结果。
1、s1==s2 (true)首先要明白,s1和s2都是通过字面量赋值的方式产生的,前面说过,这种方式只会在常量池中产生对象,最后返回给变量的是常量池中的对象地址,而常量池中如果存在了一个字符串对象,再用字面量赋值方式产生一个相同的字符串时,常量池会自动优化,直接把池中已经存在的对象地址返回给新建变量,保证常量池中永远只有不重复字符串。因此s1==s2为true肯定没毛病。

2、s1==s3(false)s1是由字面量方法创建,s3是由new方法创建,两者最大的不同是是s1只在常量池中有一份对象并且指向了常量池中的对象,s3不仅在常量池中有一份,在堆中还会由一份对象,并且s3指向的是堆中的对象。因此两者的引用明显不同,当然会是false。

3、s3=s4(false)这个也很好理解,两者都是new方法产生的,在常量池中两者都有唯一对应的对象(然并卵,他俩并不和常量池进行引用关联),在堆中,他俩都会创建属于自己的堆对象(可能因为堆空间大的缘故把- -),所以它俩引用的实际是堆中的两个不同的”java”对象。

4、s1==s3.intern()==s8.intern()为true:intern()是一个神奇的方法,它可以使一个原本指向堆中的变量指向其在常量池中的唯一对象。它们三个实际都指向了常量池中唯一的“java”对象。而s3 ==s3.intern()为false的原因就是因为s3仍然指向着堆,而s3.intern()已经指向常量池了。

5、s1==s8为false,毫无疑问,因为s1指向了常量池,而s8指向了堆对象。

原创粉丝点击