浅谈String的特点和具体的源码实现

x33g5p2x  于2022-05-05 转载在 其他  
字(4.6k)|赞(0)|评价(0)|浏览(454)

浅谈String的特点和具体的源码实现

1、String源码本质

String的基本使用是Java入门的一个必修课,在面试中有时候也往往会是第一道面试题,一些互联网大厂也喜欢从最基础的知识点入手,然后追问技术实现细节。所以本博客通过源码和对比方式对一些实现细节简单分析

以jdk8版本的源码来说,String是以final修饰的类,实际存储的数据结构为char类型的数组

  1. public final class String
  2. implements java.io.Serializable, Comparable<String>, CharSequence {
  3. /** The value is used for character storage. */
  4. // 用于存储字符串的值
  5. private final char value[];
  6. /** Cache the hash code for the string */
  7. // 缓存字符串的hashcode
  8. private int hash; // Default to 0
  9. /** use serialVersionUID from JDK 1.0.2 for interoperability */
  10. private static final long serialVersionUID = -6849794470754667710L;
  11. ...
  12. }

2、构造方法

挑出String里的主要几个构造方法,String不仅可以传入String参数、char数组,而且可以传入StringBufferStringBuilder

  1. // String 为参数的构造方法
  2. public String(String original) {
  3. this.value = original.value;
  4. this.hash = original.hash;
  5. }
  6. // char[] 为参数构造方法
  7. public String(char value[]) {
  8. this.value = Arrays.copyOf(value, value.length);
  9. }
  10. // StringBuffer 为参数的构造方法
  11. public String(StringBuffer buffer) {
  12. synchronized(buffer) {
  13. this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
  14. }
  15. }
  16. // StringBuilder 为参数的构造方法
  17. public String(StringBuilder builder) {
  18. this.value = Arrays.copyOf(builder.getValue(), builder.length());
  19. }

3、equals(Object)方法

equals(Object)方法比较两个字符串是否相等。String类重写了Object类的equals(Object)方法,比较的是字符串每个字符。

  1. /**
  2. * Compares this string to the specified object. The result is {@code
  3. * true} if and only if the argument is not {@code null} and is a {@code
  4. * String} object that represents the same sequence of characters as this
  5. * object.
  6. *
  7. * @param anObject
  8. * The object to compare this {@code String} against
  9. *
  10. * @return {@code true} if the given object represents a {@code String}
  11. * equivalent to this string, {@code false} otherwise
  12. *
  13. * @see #compareTo(String)
  14. * @see #equalsIgnoreCase(String)
  15. */
  16. public boolean equals(Object anObject) {
  17. // 对象引用相同直接返回TRUE
  18. if (this == anObject) {
  19. return true;
  20. }
  21. // 判断要对比的值是否String类型,否直接返回FALSE
  22. if (anObject instanceof String) {
  23. String anotherString = (String)anObject;
  24. int n = value.length;
  25. if (n == anotherString.value.length) {
  26. // 将两个字符串转换为字符数组进行对比
  27. char v1[] = value;
  28. char v2[] = anotherString.value;
  29. int i = 0;
  30. // 循环对比字符串的每个字符
  31. while (n-- != 0) {
  32. // 其中有一个字符不相同返回FALSE
  33. if (v1[i] != v2[i])
  34. return false;
  35. i++;
  36. }
  37. return true;
  38. }
  39. }
  40. return false;
  41. }

equals(Object)方法传入一个Object对象,会用instanceof判断是否为String类型,不是String类型直接返回FALSE

  1. Object oStr = "123";
  2. Object oInt = 123;
  3. // 返回 true
  4. System.out.println(oStr instanceof String);
  5. // 返回 false
  6. System.out.println(oInt instanceof String);

equals(Object)方法注释里,作者也提到了两个类似方法,@see #compareTo(String) @see #equalsIgnoreCase(String)equalsIgnoreCase(String)方法用于忽略字符串的大小写之后的字符串比较,compareTo(String)也是用于字符串比较的,具体和equals(Object)有什么不同?

4、compareTo(String)方法

compareTo(String):用于比较两个字符串,返回的结果为 int 类型的值

  1. public int compareTo(String anotherString) {
  2. int len1 = value.length;
  3. int len2 = anotherString.value.length;
  4. // 获取两个字符串长度值最小的length值
  5. int lim = Math.min(len1, len2);
  6. char v1[] = value;
  7. char v2[] = anotherString.value;
  8. int k = 0;
  9. // 对比每一个字符
  10. while (k < lim) {
  11. char c1 = v1[k];
  12. char c2 = v2[k];
  13. if (c1 != c2) {
  14. // 有字符不相等就返回差值
  15. return c1 - c2;
  16. }
  17. k++;
  18. }
  19. return len1 - len2;
  20. }

从源码也可以看出compareTo(String)方法也是遍历字符串的每一个字符,当两个字符串中有任意字符不相同时,返回c1 - c2。比如,两个字符串分别存储的是 1 和 2,返回值-1;如果两个字符串存储的是1和1,返回值0,;如果两个字符串存储的是2和1,返回值是1

equals(String)方法一样,compareTo(String)方法也有一个忽略大小写的比较方法compareToIgnoreCase(String)compareToIgnoreCase(String)用于用于忽略大小写后比较两个字符串。

5、compareTo(String) 和equals(Object) 对比

compareTo(String) 方法和 equals(Object) 方法,这两个方法都可以用于比较字符串

  • compareTo(String) 方法和 equals(Object) 方法的区别:
  • equals() 可以接收一个 Object 类型的参数,而 compareTo() 只能接收一个 String 类型的参数
  • equals() 返回值为 Boolean,而 compareTo() 的返回值则为 int。

6、其他方法

  • indexOf():查询字符串首次出现的下标位置
  • lastIndexOf():查询字符串最后出现的下标位置
  • contains():查询字符串中是否包含另一个字符串
  • toLowerCase():把字符串全部转换成小写
  • toUpperCase():把字符串全部转换成大写
  • length():查询字符串的长度
  • trim():去掉字符串首尾的空格
  • replace():替换字符串中的某些字符
  • split():把字符串按分隔符分割,返回字符串数组
  • join():把字符串数组转为字符串

拓展知识

上面对String的常用方法做了一个比较简单的介绍,下面给出面试中一个很常见的面试题,进行介绍,主要是学习理解,并非为了面试而面试

  • 为什么 String 类型要用 final 修饰?
    final 修饰的类都是一个不可继承类。那这样设计有什么好处呢?
    Java 语言之父 James Gosling的回答是,他会更倾向于使用final,因为它能够缓存结果,当你在传参时不需要考虑谁会修改它的值;如果是可变类的话,则有可能需要重新拷贝出来一个新值进行传参,这样在性能上就会有一定的损失。
    另一个原因是安全,当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,它的内部的值又被改变了,这样有可能会引起严重的系统崩溃问题,这是迫使 String 类设计成不可变类的一个重要原因。

总而言之就是用两个优点,一个是安全,另外一个是性能问题

  1. == 和 equals 的区别是什么?

  2. ==:对比的是栈中的值,基本数据类型对比的是变量值,引用数据类型对比的是堆中内存对象的地址

  3. equals:Object中默认也是常用==进行比较,而String的equals进行重写,比较的是两个字符串的内容

  4. String 和 StringBuilder、StringBuffer 有什么区别?

  5. String是不可变的,如果尝试修改String字符串,会重新生成一个对象;而StringBuffer、StringBuilder是可变的

  6. StringBuffer是线程安全的,StringBuilder是线程不安全的,所以StringBuilder的执行效率要快于StringBuffer

  7. String 的 intern() 方法有什么含义?

  1. public static void main(String[] args) {
  2. String s1 = new String("abc");
  3. String s2 = "abc";
  4. // s1 == s2?
  5. String s3 = s1.intern();
  6. // s2 == s3?
  7. }

答案:

  1. s1 == s2 false
  2. s2 == s2 true

Stringintern()方法,会检查字符串常量池中是否有“abc”字符串,如果有,返回改字符串引用,否,将“abc”添加到常量池中。

详情可以参考博客深入解析String#intern 美团技术团队

  1. String和new String两张创建方式有什么区别?

  2. String str1 = “abc”; 最多创建一个String对象,最少是不创建对象。如果常量池中有“abc”,那么str1直接引用就可以,否则创建“abc”的内存空间,如何再引用。引号创建的字符串是直接量,在编译器就会存储到常量池中

  3. String str2 = new String(“abc”);最多创建两个对象,最少创建1个对象。new关键字,会在堆创建内存区域,在运行期才创建。

相关文章