浅谈String的特点和具体的源码实现
String的基本使用是Java入门的一个必修课,在面试中有时候也往往会是第一道面试题,一些互联网大厂也喜欢从最基础的知识点入手,然后追问技术实现细节。所以本博客通过源码和对比方式对一些实现细节简单分析
以jdk8版本的源码来说,String是以final修饰的类,实际存储的数据结构为char类型的数组
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
// 用于存储字符串的值
private final char value[];
/** Cache the hash code for the string */
// 缓存字符串的hashcode
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
...
}
挑出String
里的主要几个构造方法,String
不仅可以传入String
参数、char
数组,而且可以传入StringBuffer
和StringBuilder
// String 为参数的构造方法
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// char[] 为参数构造方法
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
// StringBuffer 为参数的构造方法
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
// StringBuilder 为参数的构造方法
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
equals(Object)
方法比较两个字符串是否相等。String
类重写了Object
类的equals(Object)
方法,比较的是字符串每个字符。
/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*
* @param anObject
* The object to compare this {@code String} against
*
* @return {@code true} if the given object represents a {@code String}
* equivalent to this string, {@code false} otherwise
*
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
// 对象引用相同直接返回TRUE
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 v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 循环对比字符串的每个字符
while (n-- != 0) {
// 其中有一个字符不相同返回FALSE
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
equals(Object)
方法传入一个Object
对象,会用instanceof
判断是否为String
类型,不是String
类型直接返回FALSE
Object oStr = "123";
Object oInt = 123;
// 返回 true
System.out.println(oStr instanceof String);
// 返回 false
System.out.println(oInt instanceof String);
equals(Object)
方法注释里,作者也提到了两个类似方法,@see #compareTo(String) @see #equalsIgnoreCase(String)
,equalsIgnoreCase(String)
方法用于忽略字符串的大小写之后的字符串比较,compareTo(String)
也是用于字符串比较的,具体和equals(Object)
有什么不同?
compareTo(String)
:用于比较两个字符串,返回的结果为 int 类型的值
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
// 获取两个字符串长度值最小的length值
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
// 对比每一个字符
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
// 有字符不相等就返回差值
return c1 - c2;
}
k++;
}
return len1 - len2;
}
从源码也可以看出compareTo(String)
方法也是遍历字符串的每一个字符,当两个字符串中有任意字符不相同时,返回c1 - c2
。比如,两个字符串分别存储的是 1 和 2,返回值-1;如果两个字符串存储的是1和1,返回值0,;如果两个字符串存储的是2和1,返回值是1
和equals(String)
方法一样,compareTo(String)
方法也有一个忽略大小写的比较方法compareToIgnoreCase(String)
,compareToIgnoreCase(String)
用于用于忽略大小写后比较两个字符串。
compareTo(String) 方法和 equals(Object) 方法,这两个方法都可以用于比较字符串
上面对String的常用方法做了一个比较简单的介绍,下面给出面试中一个很常见的面试题,进行介绍,主要是学习理解,并非为了面试而面试
总而言之就是用两个优点,一个是安全,另外一个是性能问题
== 和 equals 的区别是什么?
==:对比的是栈中的值,基本数据类型对比的是变量值,引用数据类型对比的是堆中内存对象的地址
equals:Object中默认也是常用==进行比较,而String的equals进行重写,比较的是两个字符串的内容
String 和 StringBuilder、StringBuffer 有什么区别?
String是不可变的,如果尝试修改String字符串,会重新生成一个对象;而StringBuffer、StringBuilder是可变的
StringBuffer是线程安全的,StringBuilder是线程不安全的,所以StringBuilder的执行效率要快于StringBuffer
String 的 intern() 方法有什么含义?
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = "abc";
// s1 == s2?
String s3 = s1.intern();
// s2 == s3?
}
答案:
String
的intern()
方法,会检查字符串常量池中是否有“abc”字符串,如果有,返回改字符串引用,否,将“abc”添加到常量池中。
详情可以参考博客深入解析String#intern 美团技术团队
String和new String两张创建方式有什么区别?
String str1 = “abc”; 最多创建一个String对象,最少是不创建对象。如果常量池中有“abc”,那么str1直接引用就可以,否则创建“abc”的内存空间,如何再引用。引号创建的字符串是直接量,在编译器就会存储到常量池中
String str2 = new String(“abc”);最多创建两个对象,最少创建1个对象。new关键字,会在堆创建内存区域,在运行期才创建。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://smilenicky.blog.csdn.net/article/details/124470126
内容来源于网络,如有侵权,请联系作者删除!