String源码 1.8

本文深入探讨Java中String类的源码细节,包括其内部结构、常用方法如charAt、indexOf、substring等,以及String不可变性的重要性与实现原理。同时,对比了String、StringBuilder和StringBuffer的特性与适用场景。

1 类和成员变量

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 */
    private int hash; // Default to 0
    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;
    /**
     * Class String is special cased within the Serialization Stream Protocol.
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
	/**
     * 一个用于不考虑大小写比较字符串的Comparator内部类
     */
	public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
    

2 常用方法

2.1 charAt

检查边界,无错则返回。

    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

2.2 indexOf

查找单个char的坐标。

    public int indexOf(int ch) {
        return indexOf(ch, 0);
    }
	
    public int indexOf(int ch, int fromIndex) {
        final int max = value.length;
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) {
            // Note: fromIndex might be near -1>>>1.
            return -1;
        }

        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            for (int i = fromIndex; i < max; i++) {
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        } else {
            return indexOfSupplementary(ch, fromIndex);
        }
    }

2.3 substring

检查边界,检查长度,若不是原字符串则返回新建字符串。

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

2.4 toCharArray

拷贝新的char数组。
String中的value[]由final修饰,无法修改。

    public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }

2.5 equals

比较类型,比较长度,比较单个char。

    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;
    }

2.5.1 equals和==

"=="操作符的作用

  1. 用于基本数据类型的比较
  2. 判断引用是否指向堆内存的同一块地址

equals在Object类中,不满足String的比较,String对其重写。

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

equals的作用:用于判断两个变量是否是对同一个对象的引用,即堆中的内容是否相同,返回值为布尔类型

String作为一个对象来使用

String s1 = new String("java");
String s2 = new String("java");

System.out.println(s1==s2);            //false
System.out.println(s1.equals(s2));    //true
String s1 = new String("java");
String s2 = s1;

System.out.println(s1==s2);            //true
System.out.println(s1.equals(s2));    //true

String作为一个基本类型来使用

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

System.out.println(s1==s2);            //true
System.out.println(s1.equals(s2));    //true

因此比较字符串内容应该使用equals

https://www.cnblogs.com/tinyphp/p/3768214.html

2.5 testCompareTo

字典序比较大小
头部对其,按char大小比较,若头部相同则比较长度。

    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.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;
    }

2.6 split

split通过调用Pattern类的compile方法,使用正则表达式来划分字符串(下述代码最后一个return处)。

此外对于单个char的划分提供了加速方法,使用indexOf(regex,fromIndex)多次查找。

https://www.jianshu.com/p/3e2e85a0afb6

    public String[] split(String regex) {
        return split(regex, 0);
    }

    public String[] split(String regex, int limit) {
        /* fastpath if the regex is a
         (1)one-char String and this character is not one of the
            RegEx's meta characters ".$|()[{^?*+\\", or
         (2)two-char String and the first char is the backslash and
            the second is not the ascii digit or ascii letter.
         */
        char ch = 0;
        if (((regex.value.length == 1 &&
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 &&
              regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, value.length));
                    off = value.length;
                    break;
                }
            }
            // If no match was found, return this
            if (off == 0)
                return new String[]{this};

            // Add remaining segment
            if (!limited || list.size() < limit)
                list.add(substring(off, value.length));

            // Construct result
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }

3 String不可变性

3.1 享元设计模式

享元模式是池技术的重要实现方式,String常量池、数据库连接池、缓冲池等等都是享元模式的应用。

在java中,常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。string通过这种常量池中相同常量共享对象的方式实现了类似于缓存的享元模式设计。

https://blog.csdn.net/yulungggg/article/details/81039655

3.2 String不可变的原因

  1. 字符串常量池的需要
  2. 允许String对象缓存HashCode
    Java中String对象的哈希码被频繁地使用, 比如在hashMap 等容器中。字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存,不必每次都去计算新的哈希码。
  3. 安全性
    String被许多的Java类(库)用来当做参数,如网络连接地址URL,文件路径path,还有反射机制所需要的String参数等。
  4. 不可变保证了线程安全。

3.3 如何实现不变类

  1. 将类声明为final,所以它不能被继承。
  2. 将所有的成员声明为私有的,这样就不允许直接访问这些成员。
  3. 对变量不要提供setter方法。
  4. 将所有可变的成员声明为final,这样只能对它们赋值一次。
  5. 通过构造器初始化所有成员,进行深拷贝(deep copy)。
  6. 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝。

4 String StringBuilder StringBuffer

  • String:不可变、线程安全,改动较小的情况下使用。
  • StringBuilder:可变、线程不安全,单线程且改动较多的情况下使用。
  • StringBuffer:可变、线程安全(内部方法使用synchronized修饰),,多线程且改动较多的情况下使用。

5 substring在jdk1.6和jdk1.7的区别

6 replaceFirst、replaceAll、replace的区别

7 String对“+”的重载

8 字符串拼接方法

9 String.valueOf 和 Integer.toString的区别

Java—String.valueof()和Integer.toString()的不同

10 switch对String的支持

11 字符串池

12 常量池

12.1 Class常量池

12.2 运行时常量池

13 intern

参考

https://www.cnblogs.com/tinyphp/p/3768214.html
https://www.jianshu.com/p/3e2e85a0afb6
https://blog.csdn.net/yulungggg/article/details/81039655

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值