Среди встроенных в Java типов данных, строки стоят особняком. У них настолько необычное поведение, что его нужно разбирать отдельно. Строки — это объекты класса java.lang.String
, а не примитивные значения, как можно было бы подумать. Но они существуют в двух формах для производительности. Иногда строки ведут себя как примитивные значения, иногда как объекты.
Зачем строки сделаны объектами? По двум причинам. У строк много полезного поведения, которое удобно реализовать в виде методов. Строки имеют непостоянную длину, по техническим причинам, такие данные внутри памяти хранятся не как примитивные объекты.
Строки как объекты:
// Такой способ работы называется String Literal
// Когда строка создается из литерала, специального
// синтаксиса для создания строки (текст обрамленный кавычками)
var name1 = "Hexlet";
name1.toUpperCase(); // "HEXLET"
// Этот способ называется String Object
var name2 = new String("Hexlet");
// Тут то же самое
name2.toUpperCase(); // "HEXLET"
С другой стороны, если мы попытаемся сравнить две строки, то получим неожиданный результат:
var name1 = "Hexlet";
var name2 = "Hexlet";
name1 == name2; // true
По логике, описанной в этом уроке, два создания строки должны приводить к созданию двух разных объектов с разными ссылками. В случае строк все хитрее из-за требований к производительности.
В реальных приложениях строки создаются в больших количествах на каждом шагу. Постоянное создание новых объектов приводило бы к огромному расходу памяти. Разработчики Java схитрили. Если строка создается как String Literal, то объект создается только при первом появлении такого значения строки. Второе и последующие создания строки с уже существующим значением будут ссылаться на ту первую строку:
// Внутри JVM создается объект new String()
var lang1 = "Java";
//
// "Java" уже была, поэтому объект не создаем
// ссылаемся на уже созданный ранее
var lang2 = "Java";
// Тоже ссылка на первую строку
var lang3 = "Java";
lang1 == lang2; // true
lang2 == lang3; // true
lang1 == lang3; // true
Но если строки созданы как объекты сразу, например через new String()
, то они будут сравниваться по стандартной схеме:
var lang1 = new String("Java");
var lang2 = new String("Java");
lang1 == lang2; // false
Логично, но неудобно. В реальном коде сравнивать строки по ссылкам бессмысленно, строки сравнивают по значению. Программисты, в своем коде редко создают строки как объекты и, казалось бы проблемы нет. Если использовать String Literal, то все будет хорошо. К сожалению, эта логика не работает, Java много где выполняет автоматическое преобразование String Literal в String Object:
var lang = "Java";
// Казалось бы одинаковые значения
lang.toUpperCase(); // "JAVA"
lang.toUpperCase(); // "JAVA"
// А теперь сравним
lang.toUpperCase() == lang.toUpperCase(); // false
Вот это уже проблема. И она настолько важная, что в строки встроили метод equals()
, который сравнивает их по значению независимо ни от чего:
var lang1 = new String("Java");
var lang2 = new String("Java");
lang1.equals(lang2); // true
Общее правило звучит так. Если у нас нет цели сравнивать ссылки, то всегда используем метод equals()
, так мы защитимся от случайных ошибок.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.