1.String 是如何实现的?它有哪些重要的方法?
-
在JDK1.8中,String内部实际存储结构为cahr数组
-
有多个构造函数,无参构造函数,有参构造函数(char[]数组, StringBuilder,StringBuffer)
-
equals方法,主要用来比较两个字符串是否相等,可以传递Object参数,首先会通过instanceof方法判断是否为String类型。最后通过比较每个字符是否相等来判断。
-
compareTo方法比较两个字符串,返回结果为int类型的值,当两个字符不一样的时候返回char1-char2
2.为什么String类型要用final修饰
- 高效:使用final能够缓存结果,在传参的时候不需要考虑谁会修改它的值。如果是可变类则可能需要重新拷贝出一个新值进行传参,性能上就差了。以JVM中字符串常量池进行举例,只有字符串不可变时才能实现字符串常量池
- 安全:在调用系统级操作指令之前,可能会进行校验,如果是可变类,校验之后,它的值可能又改变了,可能会造成严重的系统崩溃
3. == 和 equals 的区别
== 对于基本数据类型来说,是用于比较 “值”是否相等的;而对于引用类型来说,是用于比较引用地址是否相同的。
查看源码我们可以知道Object中也有equals()方法,源码如下 其实就是==
public boolean equals(Object obj) {
return (this == obj);
}
字符串则重写了equals方法
public boolean equals(Object anObject) {
//对象引用相同就直接返回了
if (this == anObject) {
return true;
}
// 判断需要对比的值是否为 String 类型,如果不是则直接返回 false
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
//转换成char数组进行比较
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;
}
4. String 和 StringBuilder、StringBuffer 的区别
- 因为String是不可变的因此拼接字符串的话性能会比较差
- 因此需要使用StringBuffer,它提供append和insert方法来用于字符串拼接,并且保证其线程安全
由于StringBuffer保证了线程安全地,所以性能不高,在JDK1.5的时候引入了StringBuilder,其append方法与StringBuffer主要区别没有加synchronized。最后都是调用了AbstractStringBuilder的append方法
//这里的obj可以是String, 字符数组char[],还可以是抽象类AbstractStringBuilder的子类,CharSequence的实现类。主要还是前两个
@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;
}
//AbstractStringBuilder的append方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
//确保字符数组的大小够
ensureCapacityInternal(count + len);
//将str的字符全部加到value数组从count开始的下标处
str.getChars(0, len, value, count);
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
5.String和JVM
- String的常见创建方式有两种:new String() 的方式和直接赋值的方式,直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;而 new String() 的方式一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串,如下代码所示:
Strings1=new String("Java");
Strings2=s1.intern();
Strings3="Java";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
它们在JVM的位置如图所示
- 注意点:JDK 1.7 之后把永生代换成的元空间,把字符串常量池从方法区移到了 Java 堆上。
除此之外编译器还会对 String 字符串做一些优化,例如以下代码:
//代码 "Ja"+"va" 被直接编译成了 "Java"
String s1 = "Ja" + "va";
String s2 = "Java";
System.out.println(s1 == s2);