String-面试常考问题剖析
1. 为什么 String 类型要用 final 修饰?
为啥这样设计呢?, 请带着这一个疑问看下去,从 String 类的源码我们可以看出 String 是被 final 修饰的不可继承类,源码如下:
Java 语言之父 James Gosling 的回答是:
他会更倾向于使用 final,因为它能够缓存结果,当你在传参时不需要考虑谁会修改它的值;如果是可变类的话,则有可能需要重新拷贝出来一个新值进行传参,这样在性能上就会有一定的损失。
那么从 James Gosling的回答,可以知道他从两个方面来考虑设计String类型为 “final 的”
1.安全
当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,它的内部的值又被改变了,这样有可能会引起严重的系统崩溃问题,这是迫使String类设计成不可变类的一个重要原因。
2.高效
以 JVM 中的字符串常量池来举例,如下两个变量:
String str1 = "java";
String str2 = "java";
只有字符串是不可变时,我们才能实现字符串常量池,字符串常量池可以为我们缓存字符串,提高程序的运行效率,如下图所示:
试想一下如果 String 是可变的,那当 s1 的值修改之后,s2 的值也跟着改变了,这样就和我们预期的结果不相符了,因此也就没有办法实现字符串常量池的功能了。
2. == 和 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;
}
//判断类型是否为String
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;
}
3. String 和 StringBuilder、StringBuffer 有什么区别?
因为String类型是不可变的,所以在字符串拼接的时候如果使用String的话性能会很低,因此我们就需要使用另一个数据类型StringBuffer,它提供了append和insert方法可用于字符串的拼接,它使用 synchronized 来保证线程安全,如下源码所示:
因为它使用了synchronized来保证线程安全,所以性能不是很高,于是在JDK1.5就有了StringBuilder,它同样提供了append和insert的拼接方法,但它没有…
4. String 的 intern() 方法有什么含义?
String常见的创建方式有两种,直接赋值的方式“Strings1=“Java”;”和“Strings2=newString(“Java”);”的方式,但两者在JVM的存储区域却截然在JDK1.8中,变量s1会先去字符串常量池中找字符串“Java”,如果有相同的字符则直接返回常量句柄,如果没有此字符串则会先在常量池中创建此字符串,然后再返回常量句柄;而变量s2是直接在堆上创建一个变量,如果调用 intern 方法才会把此字符串保存到常量池中,如下代码所示:
5.String 类型在 JVM(Java 虚拟机)中是如何存储的?编译器对 String 做了哪些优化?
JDK 1.7 之后把永生代换成的元空间,把字符串常量池从方法区移到了 Java 堆上。
除此之外编译器还会对 String 字符串做一些优化,例如以下代码:
虽然 s1 拼接了多个字符串,但对比的结果却是 true,我们查看Class 文件可以知道JDK优化了
String s1 = "Java";
String s2 = "Java";
//结果为true
System.out.println(s1 == s2);
上一篇: 阿里巴巴Java成神之路-笔记(7)枚举类型和泛型
下一篇: Swift 函数指定参数默认值