Анализ исходного кода строк (1)

Java

предисловие

верить какJAVAER, наиболее часто используемый в кодировании должен бытьStringстроку, и я считаю, что должно быть много людей дляStringизapiЯ знаком с ним, но реализации его исходного кода не видел.На самом деле, я лично считаю, что для использования api первым этапом является просмотр его официальной документации.По мере накопления опыта разработки вы следует попытаться взглянуть на реализацию исходного кода, что очень важно для ваших собственных способностей, улучшение имеет решающее значение.После того, как вы поймете исходный код, вам будет удобнее использовать API!

Примечание: Следующие записи основаны наjdk8окрестности

Строка - это просто класс

StringНа самом деле это просто класс, мы можем грубо проанализировать его со следующих сторон:

  1. наследование классов
  2. переменная-член класса
  3. конструктор класса
  4. метод члена класса
  5. Связанные статические методы

отношения наследования

отIDEAАвтономный плагин экспортаStringДиаграмма классов UML выглядит следующим образом:

Как видно сразу из рисунка,Stringреализует интерфейсSerializable,Comparable,CharSequence, кратко представим роль этих трех интерфейсов:

  • Serializable: класс, который реализует этот интерфейс, будет иметь возможность сериализации.У этого интерфейса нет никакой реализации, он используется только для идентификации.
  • Comparable: классы, реализующие этот интерфейс, имеют возможность сравнивать размеры. Например, список (и массив) объектов, реализующих этот интерфейс, может быть определен какCollectionsстатический метод классаsortдля автоматической сортировки.
  • CharSequence: I интерфейс для единообразия последовательности символов. Предоставляет общие методы манипулирования последовательностями символов, обычно некоторыетолько чтениеметод, многие классы, связанные с символами, реализуют этот интерфейс для работы с последовательностями символов, например:String,StringBufferЖдать.

StringКласс определяется следующим образом:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence{
        ...
    }

Зависит отfinalмодификаторы известны,StringКлассы — это ненаследуемые, неизменяемые классы.

переменная-член класса

Здесь мы в основном вводим одну из наиболее важных переменных-членовvalue[], который определяется следующим образом:

 /** The value is used for character storage. */
    private final char value[];

Stringэто строка, состоящая из символовcharсостоит из, так что на самом делеStringВнутри на самом деле массив символов, сvalue[]Выражать,Обратите внимание, что значение [] здесь изменено с помощью final, что указывает на то, что значение не может быть изменено..

конструктор класса

StringСуществует множество перегруженных конструкторов, которые описываются следующим образом:

  1. Метод построения пустого параметра, инициализируйте экземпляр строки, по умолчанию используется пустой символ, теоретически не нужно использовать этот метод построения, фактически определите пустой символString = ""инициализирует пустую строкуStringобъект, и этот конструктор также обрабатывает нулевой символvalue[]Просто скопируйте его один раз, исходный код реализован следующим образом:
      public String() {
         this.value = "".value;
     }
    
  2. Создан со строковым аргументомStringобъект, который фактически будет параметромvalueа такжеhashПрисвоение объекту экземпляра в качестве инициализации эквивалентно глубокому копированию формального параметра.Stringобъект, исходный код выглядит следующим образом:
      public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }
    
  3. построить новый из массива символовStringобъект, используемый здесьArrays.copyOfметод копирования массива символов
     public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);
        }
    
  4. На основе исходного массива символов через смещение (начальную позицию) и количество символов перехватить и построить новыйStringобъект.
    public String(char value[], int offset, int count) {
            //如果偏移量小于0,则抛出越界异常
            if (offset < 0) {
                throw new StringIndexOutOfBoundsException(offset);
            }
            if (count <= 0) {
                //如果字符数量小于0,则抛出越界异常
                if (count < 0) {
                    throw new StringIndexOutOfBoundsException(count);
                }
                //在截取的字符数量为0的情况下,偏移量在字符串长度范围内,则返回空字符
                if (offset <= value.length) {
                    this.value = "".value;
                    return;
                }
            }
            // Note: offset or count might be near -1>>>1.
            //如果偏移量大于字符总长度-截取的字符长度,则抛出越界异常
            if (offset > value.length - count) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
            //使用Arrays.copyOfRange静态方法,截取一定范围的字符数组,从offset开始,长度为offset+count,赋值给当前实例的字符数组
            this.value = Arrays.copyOfRange(value, offset, offset+count);
        }
    
  5. На основе исходного целочисленного массива по смещению (начальной позиции) и количеству символов перехватить и построить новыйStringобъект. Представление целочисленного массива здесьЦелочисленное значение ASCII, соответствующее символу
        public String(int[] codePoints, int offset, int count) {
        //如果偏移量小于0,则抛出越界异常
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            //如果字符数量小于0,则抛出越界异常
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            //在截取的字符数量为0的情况下,偏移量在字符串长度范围内,则返回空字符
            if (offset <= codePoints.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        如果偏移量大于字符总长度-截取的字符长度,则抛出越界异常
        //if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
    
        final int end = offset + count;
    
        // 这段逻辑是计算出字符数组的精确大小n,过滤掉一些不合法的int数据
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }
    
        // 按照上一步骤计算出来的数组大小初始化数组
        final char[] v = new char[n];
        
        //遍历填充字符数组
        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }
        //赋值给当前实例的字符数组
        this.value = v;
    }
    
  6. Через исходный байтовый массив, по определенному диапазону, перехватываем длины длин от смещения, и инициализируемStringinstance, и кодировку символов можно указать одновременно.
    public String(byte bytes[], int offset, int length, String charsetName)
            throws UnsupportedEncodingException {
        //字符编码参数为空,抛出空指针异常
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        //静态方法 检查字节数组的索引是否越界
        checkBounds(bytes, offset, length);
        //使用 StringCoding.decode 将字节数组按照一定范围解码为字符串,从offset开始截取length个长度
        this.value = StringCoding.decode(charsetName, bytes, offset, length);
    }
    
  7. Аналогичен 6-й конструкции, за исключением того, что параметр кодирования перегружен какCharsetТипы
      public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }
    
  8. Создайте экземпляр строки с помощью исходного массива байтов и укажите кодировку символов. Конкретная реализация на самом деле состоит в том, чтобы вызвать шестой конструктор, начальная позиция равна 0, а длина перехвата - это длина массива байтов.
     public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
    
  9. Создайте экземпляр строки с помощью исходного массива байтов и укажите кодировку символов. Конкретная реализация на самом деле заключается в вызове седьмого конструктора, начальная позиция равна 0, а длина перехвата — это длина массива байтов.
     public String(byte bytes[], Charset charset) {
        this(bytes, 0, bytes.length, charset);
    }
    
  10. Через исходный байтовый массив, по определенному диапазону, перехватываем длины длин от смещения, и инициализируемStringЭкземпляр, в отличие от шестого конструктора, использует системную кодировку символов по умолчанию.
     public String(byte bytes[], int offset, int length) {
        //检查索引是否越界
        checkBounds(bytes, offset, length);
        //使用系统默认字符编码解码字节数组为字符数组
        this.value = StringCoding.decode(bytes, offset, length);
    }
    
  11. Создайте экземпляр строки с помощью исходного массива байтов и используйте кодировку системы по умолчанию. Конкретная реализация на самом деле состоит в том, чтобы вызвать 10-й конструктор, начальная позиция равна 0, а длина перехвата - это длина массива байтов.
    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }
    
  12. будетStringBufferпостроить в новомString, что является более особенным, так это то, что этот метод имеетsynchronizedБлокировки позволяют только одному потоку получить доступ к этому за разbufferвстроить вStringобъект, который является потокобезопасным
     public String(StringBuffer buffer) {
        //对当前 StringBuffer 对象加同步锁
        synchronized(buffer) {
            //拷贝 StringBuffer 字符数组给当前实例的字符数组
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
    
  13. будетStringBuilderпостроить в новомString, в отличие от 12-го конструктора, этот конструктор не является потокобезопасным
     public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }
    

метод члена класса

  • Получить длину строки, фактически получить длину массива символов

      public int length() {
        return value.length;
    }
    
  • Судить о том, пуста ли строка, на самом деле означает, равна ли длина сложного массива символов 0

    public boolean isEmpty() {
        return value.length == 0;
    }
    
  • Получить символ на основе параметра индекса

     public char charAt(int index) {
        //索引小于0或者索引大于字符数组长度,则抛出越界异常
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        //返回字符数组指定位置字符
        return value[index];
    }
    
  • Получить код ASIC указанного символа (тип int) в соответствии с параметром index

      public int codePointAt(int index) {
        //索引小于0或者索引大于字符数组长度,则抛出越界异
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        //返回索引位置指定字符ASSIC码(int类型)
        return Character.codePointAtImpl(value, index, value.length);
    }
    
  • Возвращает код ASIC (тип int) предыдущего элемента элемента в позиции индекса

    public int codePointBefore(int index) {
        //获得index前一个元素的索引位置
        int i = index - 1;
        //检查索引是否越界
        if ((i < 0) || (i >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointBeforeImpl(value, index, 0);
    }
    
  • Метод возвращает количество кодовых точек, которое является фактическим количеством символов.Функция похожа на length().Для обычного String нет разницы между методом длины и codePointCount, которые оба возвращают количество символов. Но есть разница, когда String имеет тип Unicode. Например: String str = "/uD835/uDD6B" (даже 'Z'), length() = 2, codePointCount() = 1

     public int codePointCount(int beginIndex, int endIndex) {
        if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
            throw new IndexOutOfBoundsException();
        }
        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
    }
    
  • Это также относится к набору символов Unicode. От позиции индекса сместите позиции codePointOffset и верните позицию смещения. Например, index = 2, codePointOffset = 3, возможно, верните 5

    public int offsetByCodePoints(int index, int codePointOffset) {
        if (index < 0 || index > value.length) {
            throw new IndexOutOfBoundsException();
        }
        return Character.offsetByCodePointsImpl(value, 0, value.length,
                index, codePointOffset);
    }
    
    
  • Это не внешний метод, который вызывается внутри String, потому что он не имеет модификатора доступа и разрешает доступ к параметрам только классам в том же пакете: dst[] — целевой массив, dstBegin — смещение целевого массива. , Чтобы скопировать прошлую начальную позицию (перезаписать откуда в целевом массиве), функция состоит в том, чтобы скопировать все значение массива символов String в массив символов dst и начать копирование с позиции dstBegin массива dst.

    void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }
    
  • Чтобы получить массив символов char, принцип заключается в том, что метод getChars() копирует символы строки в целевой массив символов. Параметры: srcBegin — начальная позиция исходной строки, srcEnd — позиция после конца копируемого символа в исходной строке (то есть область копирования не включает srcEnd) dst[] — целевой массив символов, dstBegin — это смещение копии целевого количества символов, скопированные символы перезаписываются, начиная с позиции dstBegin целевого массива символов.

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
    
  • Получите массив байтов строки и декодируйте строку в массив байтов в соответствии с указанной кодировкой символов.

    public byte[] getBytes(String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null) throw new NullPointerException();
        return StringCoding.encode(charsetName, value, 0, value.length);
    }
    
  • Получите массив байтов строки и декодируйте строку в массив байтов в соответствии с указанной кодировкой символов.

    public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return StringCoding.encode(charset, value, 0, value.length);
    }
    
  • Получите массив байтов строки и декодируйте строку в массив байтов в соответствии с системной кодировкой символов по умолчанию.

     public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);
    }
    

простое резюме

  • StringИзмененоfinalМодифицированный, не может быть унаследован, неизменяемый класс
  • StringвыполнитьSerializableинтерфейс, который можно сериализовать
  • StringвыполнитьComparableинтерфейс, который можно использовать для сравнения размеров
  • StringвыполнитьCharSequenceИнтерфейс, представляющий всегда упорядоченную последовательность символов, реализующий общий метод последовательности символов.
  • StringЯвляется последовательностью символов, внутренняя структура данных фактически является массивом символов, и все методы операций являются операциями вокруг этого массива символов.
  • Stringчасто используется вSystemКатегорияarraycopyметод, целью которого является копирование массива символов

В конце концов

По космическим причинам,StringПервое резюме находится здесь первым, а последующая часть напишет еще одну запись. Она будет опубликована в общедоступном аккаунте [Чжан Шаолинь] как можно скорее. Добро пожаловать, обратите внимание!