Строки (String) в Java. Особенности, проблемы, пул строк, создание и работа со строками

  • DeveloperNotes
  • »
  • Java
  • »
  • Строки (String) в Java. Особенности, проблемы, пул строк, создание и работа со строками

Строки в Java — это большая, и не такая уж простая тема. Чтобы разобраться в строках, придется потратить некоторое время. Строки отличаются от обычных объектов в Java и имеют свои особенности. Если вы думаете, что разбираетесь в строках, давайте я задам вам один вопрос.

Что выведет данная программа?

package question;
public class Question {
    public static void main(String[] args) {
        String s1 = "Something";
        String s2 = new String(s1);
        if (s1 == s2) System.out.println("s1 == s2");
        if (s1.equals(s2)) System.out.println("s1.equals(s2)");
    }
}

Не уверены в ответе? Давайте разбираться подробно и к концу данной статьи вы точно будете знать, что тут происходит.

Что такое строка (string) в Java?

В общем и целом, строка - это последовательность символов. Например, весь текст здесь - это и есть строки. Таким образом, массив char'ов в каком-то смысле тоже является строкой. Но в Java для работы со строками определен конкретный класс, который называется String. Он предоставляет ряд методов для работы со строками, которые крайней полезны в работе. Чтобы проиллюстрировать это, давайте приведет массив char'ов к строке и получим длину строки:

char data[] = {'n', 'o', 't', 'e', 's'};
String str = new String(data);
System.out.println( str.length() ); // выведет "5"

Строки не обязательно созадвать с помощью оператора new. Для их создания можно просто присвоить ссылке на объект (вы же помните, что мы всегда работаем со ссылками, а не с самими объектами?) значение. Кроме того, строки можно соединять с помощью оператора "+" (сложения). Покажем и то и другое на примере:

String a = "abc";
String b = "def";
String c = a + b; // abcdef

В отличие от обычных объектов, которые вы создаете с помощью оператора new, строки - не мутируемые объекты (immutable objects). То есть, создав строку один раз, вы уже не можете ее изменить. Это значит, что при использовании операций, которые изменяют строку — на самом деле будет создана новая строка. Например, в примере выше мы явно создали строку "c". Но что, если пример выглядел бы следующим образом? Я немного изменил программу для большей понятности.

String a = new String("abc");
String b = new String("def");
a = a + b; // что тут случится?

Ответьте на вопрос: сколько объектов в этой программе? Серьезно, подумайте минуту. Окей, я считаю, что у нас тут три объекта, а не два, как можно было бы подумать. Если строки не мутируемы, значит, что когда мы сложили две строки - фактически JVM создала третий объект (но явно мы этого не видим), после чего ссылка "a" была "переведена" на этот созданный объект. А первоначальная ссылка "a" на объект "abc" была потеряна.

Почему это вообще важно? Представьте, что вам нужно обработать 100.000 строк (например, разобрать по частям какую-нибудь книгу или большую базу данных). Если вы будете каждый раз соединять строки таким образом, то вы создадите в программе огромное количество объектов. Всё это будет работать медленно. Поэтому, если вам нужно много изменять строку, лучше использовать StringBuffer или StringBuilder (я про них еще не писал, поищите информацию в интернете, если интересно).

Последнее, что хотелось бы заметить: класс String представляет последовательность символов в формате кодировки UTF-16, которая поддерживает множество языков. И это очень круто!

Я рекомендую хотя бы пролистать официальную документацию по строкам от Oracle, чтобы узнать, какие методы и возможности поддерживает класс String.

Что такое пул строк (string pool) в Java и зачем он нужен?

Пул строк (string pool) в Java, как понятно из описания, это некий пул (или список) объектов класса String, который хранится в специальном месте кучи (Java Heap). Разработчики Java сделали так, чтобы оптимизировать выделение памяти и хранение строк, ускорить и оптимизировать работу с ними.

Пул строк был создан по той простой причине, что строки - это самое используемое в программах на Java. Мы как минимум очень часто, а как максимум - почти всегда работает со строками в программах.

Пул строк работает следующим образом: когда мы создаем строку с помощью конструкции

String str = "HELLO";

Эта строка попадает в пул строк. Когда мы создаем другую строку с тем же значением:

String str2 = "HELLO";

То на самом деле сначала происходит поиск по пулу строк. И если там уже найдена такая строка - то str2 присваивается ссылка на уже созданный объект (на который указывает и str).

По этой причине следующая конструкция выведет true (вы же не забыли, что мы работаем не с самими объектами, а со ссылками на них?):

String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);

Тут может показаться, что мы создаем другой объект строки, но на самом деле это не так - происходит поиск по пулу строк.

Создание строки как нового объекта

Механизм, описанный выше - классный. Он позволяет программисту меньше писать, а JVM использовать меньше памяти и быстрее работать. Но на самом деле мы можем создать объект строки явно, даже в случае, если такая строка уже была создана и записана в пул строк:

String str = new String("HELLO");

В этом случае объект будет создан, причем будет создан в куче. Таким образом следующий код:


String a = new String("abc");
String b = new String("abc");
System.out.println (a == b);

выдаст нам false, ведь ссылки указывают на разные объекты. Поиск по пулу строк тут не происходит.

Всё происходящее отлично иллюстрируется следующим изображением, которое я нашел в интернете (кто я такой, чтобы что-то рисовать, хаха):

Пул строк (string pool) в Java; расположение в куче (heap)

Метод intern() класса String

При создании объекта для новой строки через оператор new мы можем также попросить JVM поискать эту строку в пуле строк с помощью метода intern(), следующим образом:

String a = new String("abc").intern();

В данном случае, если у нас уже есть такая строка в пуле строк, то будет возвращена ссылка на строку из пула строк, и новый объект создан не будет.

Думаю, теперь вы можете ответить на вопрос из начала этой статьи: будет выведено только s1.equals(s2). Потому, что мы не обратились к пулу строк при сздании строки s2, даже несмотря на то, что ее значение взято из s1.

Вопросы пишите в комментарии. Удачи!

Комментарии

  • Михаил - 10 months ago
    Хорошая статья, спасибо! Многое стало гораздо понятнее!

  • DeveloperNotes - 10 months ago
    Хаха, я сделал тут аватарки по умолчанию - Тайлер Дерден! Мне нравится.

Обязательное поле.
Подсказка по Markdown, используется для оформления.

DeveloperNotes.ru © 2018 — 2020