面试String的特点及重要方法(二)

上期呢我们对String的基本构造方法,源码进行了简单的总结,这期我们看看它比较更有魅力的地方,废话不多,直接上:
飞上期

一,==和equals()的区别:

==对于基本数据类型来说,是用于比较"值"是否相等,而对于引用类型来说,是用来比较引用地址是否相同。
继续源码,我们可以看出Object中也有equals()方法,如下:

public boolean equals(Object obj) {
    return (this == obj);
}

可以看出,Object中的equals方法其实就是==,而String中重写了equals()方法把他修改成比较两个字符串是否相等。源码如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

二,final修饰的好处

从String的源码我们可已看出,String是被final修饰的不可继承类,源码如下:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    }

那么问题来了,为什么要这样设计呢?

他会更倾向于使用final,因为他能够缓存结果,当你在传参是不需要考虑谁会修改他的值;如果是可变类的话,则有可能需要重新拷贝出来一个新的值进行传参,这样在性能上会有一定的损失。

这是James Gosling的回答,他还说,迫使String类设计成不可变的原因是为了安全,当你在调用其他方法时,比如调用一些系统操作指令前,可能会有一系列二的校验,如果是可变类的话,可能在你校验之后,他内部的值改变了,这样有肯能引起系统崩溃,这也是final修饰String的重要原因之一。

总结来说,使用final修饰第一是为了安全,第二是为了高效。以JVM中的字符串常量池来说,如下两个变量:

String s1 = "java";
String s2 = "java";

只有字符串不可变时,我们才能实现字符串常量池,他可以为我们缓存字符串,提高运行效率,上图:
在这里插入图片描述

试想一下,如果String是可变的,那当S1的值修改后,s2的值也变了,这样就和我们预期的结果不相符了,因此也就没办法实现字符串常量池的功能了。

三,String,StringBuilder,Stringbuff的区别

因为String的类型是不可变的,所以在字符串拼接的时候如果使用String的话性能会很低,因此我们需要使用另一个数据类型Stringbuff,它提供的append和insert方法可用于字符串的拼接,他是用synchronized来保证线程安全,源码如下:

@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

因为他使用了synchronized来保证线程安全,所以性能不用很高,于是在JDK1.5之后又有了StringBuilder,他同样提供append和insert方法,但是他没有使用synchronized修饰,因此在性能上要优于StringBuffer,所以在非并发操作的环境下可以使用StringBuilder来进行字符拼接。

四,String和JVM

String常见的创建方式有两种:

String s1 = "Java";//第一种
String s2 = new String("Java");//第二种

但是两者在JVM的存储区域截然不同,在JDK1.8中,变量s1会先去字符串常量池中找字符串“Java”,如果有,则直接返回常量句柄,如果没有,则会现在常量池中创建常量,然后再返回常量句柄,而s2直接实在堆上创建一个变量,如果调用intern方法才会把字符串存在常量池中,如下代码:

String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2);//false
System.out.println(s2 == s3);//true

他们在JVM存储位置如下:
在这里插入图片描述

除此之外编译器还会对String字符串做一些优化,例如以下代码:

String s1 = "Ja"+"va";
String s2 = "Java";
System.out.println(s1 == s2);

虽然s1拼接多个字符串,但是比较结果却为true,我们使用反编译工具看到如下:
在这里插入图片描述
从#2可以看出,代码"Ja"+"va"被直接编译成Java,因此,s1==s2的结果才为true,这就是编译器对字符串优化的结果。

本期对Sring的总结就到这里,有不足的地方可以指出。谢谢关注!

发布了8 篇原创文章 · 获赞 16 · 访问量 272

猜你喜欢

转载自blog.csdn.net/weixin_44740789/article/details/104886700
今日推荐