Объект Java String, вы действительно понимаете?

Java

Реализация объектов String

StringОбъекты — одни из наиболее часто используемых объектов в Java, поэтому Java-компании постоянноStringРеализация объекта оптимизирована с целью улучшенияStringПроизводительность объекта, посмотрите на следующую картинку, давайте разбираться вместеStringПроцесс оптимизации объекта.

1. В Java 6 и более ранних версиях

StringОбъект — это объект, который инкапсулирует массив символов. Он в основном имеет четыре переменных-члена: массив символов, смещение смещения, количество символов и хеш-значение.

StringОбъект находит массив char[] через свойства offset и count, чтобы получить строку. Это может эффективно и быстро совместно использовать объекты массива, экономя место в памяти, но такой подход может привести к утечкам памяти.

2. С версии Java7 на версию Java8

Начиная с версии Java 7, Java имеетStringВ классе произошли некоторые изменения.StringВ классе больше нет переменных offset и count. Польза от этогоStringОбъект занимает немного меньше памяти, а метод String.substring больше не разделяет char[], что решает проблему утечки памяти, которая может быть вызвана использованием этого метода.

3. Из версии Java9

Изменил массив char[] на массив byte[], зачем вам это нужно? Мы знаем, что char — это два байта, но если хранить однобайтовые символы немного расточительно, то в целях экономии места компания Java изменила его на однобайтовые байты для хранения строк. Это позволяет избежать потерь при хранении одного байта символов.

В Java9 поддерживается новый атрибут coder, который является идентификатором формата кодирования.При вычислении длины строки или вызове функции indexOf() необходимо судить о том, как вычислить длину строки по этому полю . Свойство coder по умолчанию имеет два значения 0 и 1, 0 представляет Latin-1 (однобайтовая кодировка), а 1 представляет кодировку UTF-16. еслиStringСудя по тому, что строка содержит только Latin-1, значение атрибута coder равно 0, иначе 1.

Как создаются объекты String

1. С помощью строковых констант

String str= "pingtouge"Когда строка создается в таком виде, JVM сначала проверит, существует ли объект в пуле строковых констант. Если он существует, она вернет ссылочный адрес объекта. Если он не существует, он будет создан в пуле строковых констант. пул строковых констант.Строковый объект и возврат ссылки. Преимущество использования этого метода для создания заключается в том, что он позволяет избежать повторного создания строк с одним и тем же значением и экономит память.

2. Способ конструктора String()

String str = new String("pingtouge")Процесс создания строковых объектов таким способом более сложен и делится на два этапа: во-первых, во время компиляции строкаpingtougeОн будет добавлен в структуру констант, а строка будет создана в пуле констант при загрузке класса. Затем, когда вызывается new(), JVM вызываетStringконструктор, ссылаясь на постоянный пулpingtougeнить, Создать в куче памятиStringобъект и возвращает адрес ссылки в куче.

понялStringСуществует два способа создания объектов. Давайте проанализируем следующий код, чтобы углубить наше понимание этих двух способов. В следующем фрагменте кодаstrРавен ли онstr1Шерстяная ткань?

  String str = "pingtouge";
  String str1 = new String("pingtouge");
  system.out.println(str==str1)

Давайте проанализируем эти строки кода одну за другой, начиная сString str = "pingtouge"В начале здесь используется метод строковой константы для создания строкового объекта.pingtougeКогда используется строковый объект, JVM обращается к пулу констант, чтобы выяснить, существует ли строка. Ответ здесь определенно нет, поэтому JVM создаст строковый объект в пуле констант и вернет ссылку на адрес объекта. , такstrуказывает наpingtougeАдресная ссылка строкового объекта в пуле констант.

ПослеString str1 = new String("pingtouge")В этой строке кода метод конструктора используется для создания строкового объекта.Согласно нашему пониманию метода конструктора для создания строкового объекта,str1должен получить кучуpingtougeСсылочный адрес строки. так какstrуказывает наpingtougeАдрес строкового объекта в пуле констант относится кstr1указывает на кучуpingtougeадрес строки в кавычках, поэтомуstrточно не равноstr1.

Неизменяемость объектов String

из того, что мы знаемStringС момента объекта, я думаю, все знаютStringОбъекты неизменны. Так как же это неизменно?JavaКаковы преимущества этого? Давайте кратко обсудим вместе, давайте сначала посмотримStringКусок исходного кода объекта:

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

Как видно из этого исходного кода,StringКласс использует модификатор final Мы знаем, что когда класс модифицируется окончательно, это означает, что класс не может быть унаследован, поэтомуStringКлассы не могут быть унаследованы. ЭтоStringНеизменная первая точка

Посмотрите дальше вниз, используется для хранения строкchar value[]массивprivateиfinalмодификации, мы знаем, что дляfinalПеременная примитивного типа данных, ее значение не может быть изменено после инициализации. ЭтоStringНеизменный второй пункт.

Зачем компании JavaStringУстановите его как неизменяемый, в основном из следующих трех аспектов:

  • 1. Обеспечьте безопасность объектов String. Предполагая, что объекты String изменяемы, объекты String могут быть злонамеренно изменены.
  • 2. Чтобы значение хеш-атрибута не менялось часто, чтобы обеспечить уникальность, чтобы контейнер, аналогичный HashMap, мог реализовать соответствующую функцию кэширования ключ-значение.
  • 3. Можно реализовать пул строковых констант

Оптимизация объектов String

Строка - это то, что мы обычно используемJavaОдин из типов, поэтому работа со строками неизбежна.При работе со строками при неправильном использовании производительность будет сильно отличаться. Итак, на что следует обращать внимание в процессе манипуляций со строками?

элегантно соединенные строки

Конкатенация строк — одна из наиболее часто используемых операций со строками, поскольку мы знаем, чтоStringОбъекты неизменяемы, поэтому при сплайсинге мы используем как можно меньше+Выполнить конкатенацию строк или подсознательно думать, что ее нельзя использовать+Выполните конкатенацию строк, подумайте об использовании+Выполнение конкатенации строк создает много бесполезных объектов. Это действительно так? Давайте проведем эксперимент. Мы используем+чтобы объединить следующую строку.

String str8 = "ping" +"tou"+"ge";

Давайте проанализируем, сколько объектов сгенерирует этот код? Если мы проанализируем его так, как мы его понимаем, мы сначала создадимpingобъект, затем создайтеpingtouобъект, который будет создан последнимpingtougeобъект, всего создается три объекта. Это действительно так? На самом деле это не так.Компания Java боится, что наши программисты допустят ошибки, поэтому оптимизирует компилятор.Приведенная выше конкатенация строк будет оптимизирована нашим компилятором и оптимизирована вString str8 = "pingtouge";объект. Помимо оптимизации константной конкатенации строк, для использования+Нет. Динамически соединяя строки, компилятор также делает соответствующие оптимизации для улучшенияStringпроизводительность, например следующий код:

String str = "pingtouge";

for(int i=0; i<1000; i++) {
      str = str + i;
}

Компилятор поможет нам оптимизировать в этом


String str = "pingtouge";

for(int i=0; i<1000; i++) {
        	  str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}

Видно, что Java сделала много оптимизации для этого блока, не позволяя программистуStringПроизводительность быстро падает. Хотя компания Java сделала соответствующие оптимизации в компиляторе, мы все еще можем увидеть недостатки оптимизации компании Java. При динамическом соращирении строки, хотя StringBuilder используется для сращивания строки, каждый каждый цикл будет генерировать новый StringBuilder Экземпляр, который также уменьшит производительность системы.

Поэтому, когда мы выполняем сращивание строк, нам нужно оптимизировать на уровне кода,При динамическом соединении строк, если безопасность потоков не задействована, мы используем StringBuilder для объединения для повышения производительности системы.Если безопасность потоков задействована, мы используем StringBuffer для объединения строк.

Умное использование метода intern()

     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     public native String intern();

Это официальный комментарий к функции intern(), который, вероятно, означает, что функция intern используется для возврата строки в пуле констант.Если строка уже существует в пуле констант, она напрямую вернет ссылку на объект в постоянном пуле. В противном случае добавьте объект в пул констант и верните ссылку.

Существует одинTwitterинженер вQConНа Глобальной конференции по разработке программного обеспечения они поделилисьStringДело об оптимизации объекта, они используют преимуществаString.intern()Этот метод оптимизирует предыдущее хранилище памяти 20G до нескольких сотен мегабайт памяти. Этого достаточно, чтобы показатьString.intern()Сила , давайте рассмотрим пример, простойString.intern()использование.

    public static void main(String[] args) {
        String str = new String("pingtouge");
        String str1 = new String("pingtouge");
        System.out.println("未使用intern()方法:"+(str==str1));
        System.out.println("未使用intern()方法,str:"+str);
        System.out.println("未使用intern()方法,str1:"+str1);

        String str2= new String("pingtouge").intern();
        String str3 = new String("pingtouge").intern();
        System.out.println("使用intern()方法:"+(str2==str3));
        System.out.println("使用intern()方法,str2:"+str2);
        System.out.println("使用intern()方法,str3:"+str3);

    }

Как видно из результатов, неиспользованныеString.intern()метод, создавать строковые объекты с одинаковым значением и возвращать разные ссылочные адреса объектов, использоватьString.intern()После метода при построении строкового объекта того же значения возвращается тот же адрес ссылки на объект. Это экономит нам много места

String.intern()Хотя метод хорош, мы должны использовать его в сочетании со сценой и не можем использовать его без разбора, потому что реализация константного пула аналогичнаHashTableспособ реализации,HashTableЧем больше хранимых данных, тем больше временная сложность обхода. Если данные слишком велики, это увеличит нагрузку на весь пул строковых констант.

Гибкое разделение строк

Разбиение строк — одна из распространенных операций строковых операций.Для разбиения строк большинство людей используют метод Split(), а метод Split() в большинстве случаев использует регулярные выражения.В самом методе нет ничего плохого. , но поскольку производительность регулярных выражений очень нестабильна, неправильное использование вызовет проблемы с возвратом, что может привести к высокой загрузке ЦП. Метод Split() не использует регулярные выражения в следующих двух случаях:

  • Если входящий параметр имеет длину 1 и не содержит метасимвол регулярного выражения ".$|()[{^?*+\", регулярное выражение использоваться не будет.
  • Если длина переданного параметра равна 2, первый символ — обратная косая черта, а второй символ — не число ASCII или буква ASCII, регулярное выражение не будет использоваться.

Поэтому при разделении строк следует использовать метод Split() с осторожностью. Во-первых, рассмотрите возможность использования метода String.indexOf() для разделения строк. Если String.indexOf() не соответствует требованиям разделения, используйте метод Split() и используйте метод Split.() для разделения строки, вам нужно обратить внимание на проблему поиска с возвратом.

Что касается недостатков статьи, я надеюсь, что вы можете дать больше указаний, учиться вместе и вместе добиваться прогресса.

использованная литература

  • Практика настройки производительности Java Лю Чао

Наконец

Сделайте небольшую рекламу, добро пожаловать, чтобы отсканировать код и подпишитесь на общедоступную учетную запись WeChat: «Технический блог брата Пинтоу», давайте вместе добьемся прогресса.

平头哥的技术博文