Хочешь уверенно проходить IT-интервью?

Мы понимаем, как сложно подготовиться: стресс, алгоритмы, вопросы, от которых голова идёт кругом. Но с AI тренажёром всё гораздо проще.
💡 Почему Т1 тренажёр — это мастхэв?
- Получишь настоящую обратную связь: где затык, что подтянуть и как стать лучше
- Научишься не только решать задачи, но и объяснять своё решение так, чтобы интервьюер сказал: "Вау!".
- Освоишь все этапы собеседования, от вопросов по алгоритмам до диалога о твоих целях.
Зачем листать миллион туториалов? Просто зайди в Т1 тренажёр, потренируйся и уверенно удиви интервьюеров. Мы не обещаем лёгкой прогулки, но обещаем, что будешь готов!
Реклама. ООО «Смарт Гико», ИНН 7743264341. Erid 2VtzqwP8vqy
Сравнение строк – увлекательная тема. Мы не только рассмотрим строки в Java, но также проанализируем популярные приемы и решим задачу.
У нас как всегда много опаздывающих к началу курса, так что только вчера провели второе занятие среди нового потока "Разработчик Java". Но это так, мелочи жизни, а пока что мы продолжаем публикацию серии статей Java Challengers, перевод которых подготовили для вас.
В Java класс String
инкапсулирует массив char
(прим. переводчика – с Java 9 это уже массив byte
, см. Компактные строки в Java 9). Говоря просто, String
– это массив символов, используемый для составления слов, предложений или других конструкций.
☕ Подтянуть свои знания по Java вы можете на нашем телеграм-канале «Библиотека Java для собеса»
Инкапсуляция – это одна из самых мощных концепций объектно-ориентированного программирования. Благодаря инкапсуляции вам не нужно знать, как работает класс String
. Вам достаточно знать методы его интерфейса.
Когда вы смотрите на класс String
в Java, вы можете увидеть, как инкапсулирован массив char
:
public String(char value[]) { this(value, 0, value.length, null); }
Чтобы лучше понять инкапсуляцию, представьте физический объект: машину. Нужно ли вам знать, как работает автомобиль под капотом, чтобы управлять им? Конечно, нет, но вы должны знать, что делают интерфейсы автомобиля: педаль газа, тормоза и рулевое колесо. Каждый из этих интерфейсов поддерживает определенные действия: ускорение, торможение, поворот налево, поворот направо. То же самое и в объектно-ориентированном программировании.
Первая статья в серии Java Challengers была про перегрузку методов, которая широко используется в классе String
. Перегрузка может сделать ваши классы действительно гибкими:
public String(String original) {} public String(char value[], int offset, int count) {} public String(int[] codePoints, int offset, int count) {} public String(byte bytes[], int offset, int length, String charsetName) {} // И так далее ...
Вместо того, чтобы пытаться понять, как работает класс String
, эта статья поможет вам понять что он делает, и как использовать его в вашем коде.
Что такое пул строк (String pool)
Класс String
, возможно, наиболее часто используемый класс в Java. Если новый объект создавать в динамической памяти (memory heap) каждый раз, когда мы используем String
, то мы потратим впустую много памяти. Пул строк (String pool) решает эту проблему, сохраняя только один объект для каждого значения строки.

Хотя мы создали несколько переменных String
со значениями Duke
и Juggy
, но в динамической памяти (куче) создаётся и хранится только два объекта. Для доказательства посмотрите следующий пример кода.
Напомним, что в Java оператор "==
" используется для сравнения двух объектов и определения того, один и тот же это объект или нет.
String juggy = "Juggy"; String anotherJuggy = "Juggy"; System.out.println(juggy == anotherJuggy);
Этот код вернет true
, потому что две переменные String
указывают на один и тот же объект в пуле строк. Их значения одинаковые.
🧩☕ Интересные задачи по Java для практики можно найти на нашем телеграм-канале «Библиотека задач по Java»
Исключение – оператор new
Теперь посмотрите на этот код – он выглядит похожим на предыдущий пример, но здесь есть отличие.
String duke = new String("duke"); String anotherDuke = new String("duke"); System.out.println(duke == anotherDuke);
На основе предыдущего примера можно подумать, что этот код вернёт true
, но это не так. Добавление оператора new
приводит к созданию нового объекта String
в памяти. Таким образом, JVM создаст два разных объекта.
Native-методы
Native-методы в Java – это методы, которые будут компилироваться с использованием языка C, обычно с целью управления памятью и оптимизации производительности.
Пулы строк и метод intern()
Для хранения строк в пуле используется способ, называемый "интернирование строк" (String interning).
Вот, что Javadoc говорит нам о методе intern()
:
/** * Возвращает каноническое представление для строкового объекта. * * Пул строк (первоначально пустой) управляется классом {@code String}. * * Когда вызывается метод intern, если пул уже содержит строку, * равную этому объекту {@code String}, определяемому через * метод {@link #equals(Object)}, тогда возвращается строка из пула. * Иначе, этот объект {@code String} добавляется к * пулу и возвращается ссылка на этот объект {@code String}. * * Из этого следует, что для любых двух строк {@code s} и {@code t}, * {@code s.intern() == t.intern()} будет {@code true} * тогда и только тогда, когда {@code s.equals(t)} равно {@code true}. * * Все литеральные строки и строковые константы интернируются. * Строковые литералы определяются в разделе 3.10.5 The Java™ Language Specification. * * @returns строка, которая имеет то же самое содержание как эта строка, * но, гарантируется, что она будет из пула уникальных строк. * * @jls 3.10.5 String Literals */ public native String intern();
Метод intern()
используется для хранения строк в пуле строк. Во-первых, он проверяет, существует ли уже созданная строка в пуле. Если нет, то создает новую строку в пуле. Логика пула строк основана на паттерне Flyweight.
Теперь обратите внимание, что происходит, когда мы используем new
для создания двух строк:
String duke = new String("duke"); String duke2 = new String("duke"); System.out.println(duke == duke2); // Здесь результат будет false System.out.println(duke.intern() == duke2.intern()); // Здесь результат будет true
В отличие от предыдущего примера с ключевым словом new
, в данном случае сравнение вернёт true
. Это потому, что использование метода intern()
гарантирует, что строка будет в пуле.
Метод equals
в классе String
Метод equals()
используется для того, чтобы проверить одинаковое или нет состояние двух классов. Поскольку equals()
находится к классе Object
, то каждый Java-класс наследует его. Но метод equals()
должен быть переопределен, чтобы он работал правильно. Конечно, String
переопределяет equals()
.
Взгляните:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; }
Как вы видите, значение класса String
сравнивается через equals()
, а не через ссылку на объект. Не имеет значения, если ссылки на объекты разные: будут сравниваться состояния.
Наиболее распространенные методы String
Есть ещё одна вещь, которую вам нужно знать, прежде чем решить задачку на сравнение строк.
Рассмотрим наиболее распространённые методы класса String
:
// Удаляет пробелы в начале и в конце строки trim() // Получает подстроку по индексам substring(int beginIndex, int endIndex) // Возвращает длину строки length() // Заменяет строку, можно использовать регулярное выражение replaceAll(String regex, String replacement) // Проверяет, есть ли указанная последовательность CharSequence в строке contains(CharSequences)
Решите задачку на сравнение строк
Давайте проверим, что вы узнали о классе String
, решив небольшую задачку.
В этой задаче вы сравните несколько строк, используя изученные концепции. Глядя на код ниже, можете ли вы определить значение каждой переменной result
?
public class ComparisonStringChallenge { public static void main(String... doYourBest) { String result = ""; result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; result += "flexibleCode" == "flexibleCode" ? "2" : "3"; result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; System.out.println(result); } }
Каков будет вывод?
- A: 02468
- B: 12469
- C: 12579
- D: 12568
Правильный ответ приведён в конце статьи.
Что сейчас произошло? Понимание поведения String
В первой строке мы видим:
result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1";
В этом случае результат false
, ведь когда метод trim()
удаляет пробелы, он создаёт новый String
с помощью оператора new
.
Далее мы видим:
result += "flexibleCode" == "flexibleCode" ? "2" : "3";
Здесь нет никакой тайны, строки одинаковы в пуле строк. Это сравнение возвращает true
.
Затем имеем:
result += new String("doYourBest") == new String("doYourBest") ? "4" : "5";
Использование new
приводит к созданию двух новых строк, и не важно, равны их значения или нет. В этом случае сравнение будет false
даже если значения одинаковые.
Далее:
result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7";
Поскольку мы использовали метод equals()
, будет сравниваться значение строки, а не экземпляр объекта.
В этом случае не имеет значения, разные объекты или нет, поскольку сравнивается значение. Результат true
.
В итоге:
result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9";
Как вы видели ранее, метод intern()
помещает строку в пул строк. Обе строки указывают на один и тот же объект, поэтому в данном случае true
.
Распространенные ошибки со строками
Бывает трудно определить, указывают ли две строки на один и тот же объект или нет, особенно когда строки содержат одно и то же значение. Полезно помнить, что использование new
всегда приводит к созданию нового объекта в памяти, даже если значения строк одинаковые.
Использование методов класса String
для сравнения ссылок на объекты также может быть сложным. Особенность в том, что если метод изменяет что-то в строке, то будут разные ссылки на объекты.
Несколько примеров, которые помогут прояснить:
System.out.println("duke".trim() == "duke".trim());
Это сравнение будет истинным, потому что метод trim()
не создает новую строку.
System.out.println(" duke".trim() == "duke".trim());
В этом случае первый метод trim()
генерирует новую строку, так как метод будет выполнять свою работу, и поэтому ссылки разные.
Наконец, когда trim()
выполнит свою работу, он создает новую строку:
// Реализация метода trim в классе String new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
Что нужно помнить о строках
- Строки неизменяемые, поэтому состояние строки изменить нельзя.
- Для экономии памяти JVM хранит строки в пуле строк. При создании новой строки JVM проверяет ее значение и указывает на существующий объект. Если в пуле нет строки с этим значением, JVM создаёт новую строку.
- Оператор "
==
" сравнивает ссылки на объект. Методequals()
сравнивает значения строк. То же правило будет применяться ко всем объектам. - При использовании оператора
new
будет создана новая строка в хипе (Прим. переводчика – в оригинале написано, что в пуле, но это не так, спасибо zagayevskiy), даже если есть строка с тем же значением.
Ответ
Ответ на эту задачу – D. Вывод будет 12568.
Продолжение следует...
Комментарии