Ответ
Класс String в Java представляет собой неизменяемую (immutable) последовательность символов в кодировке UTF-16.
Ключевые особенности:
- Неизменяемость: Любая операция, модифицирующая строку (например,
concat(),substring()), возвращает новый объектString. Исходный объект не изменяется. - Пул строк (String Pool): Строковые литералы и строки, созданные с помощью
intern(), хранятся в специальной области памяти (heap). Это позволяет повторно использовать объекты и экономить память."abc" == "abc"→true(один объект в пуле).new String("abc") == "abc"→false(разные объекты).
- Внутреннее представление: До Java 9 использовался массив
char[]. Начиная с Java 9, для оптимизации памяти используется массивbyte[]с флагом кодировки (Latin-1 или UTF-16). - Сравнение: Для сравнения содержимого всегда используйте метод
equals(), а не оператор==.
Пример:
String s1 = "hello"; // Создается или берется из пула строк
String s2 = new String("hello"); // Создается новый объект в куче
String s3 = s1.concat(" world"); // Создается новый объект "hello world"
System.out.println(s1 == s2); // false (разные объекты)
System.out.println(s1.equals(s2)); // true (одинаковое содержимое)
System.out.println(s1); // "hello" (исходная строка не изменилась)
Для работы с изменяемыми последовательно символов используйте:
StringBuilder— для однопоточных сценариев (высокая производительность).StringBuffer— для многопоточных сценариев (потокобезопасный, но медленнее).
Ответ 18+ 🔞
А, блядь, вот эта тема про строки в Java — это просто отдельный вид искусства, а иногда и цирк с конями, ёпта. Слушай сюда, разжую как для себя любимого.
Представь себе, что класс String — это как священная каменная скрижаль. Раз высекли на ней «hello world» — всё, приехали. Переписать её нельзя, блядь. Никак. Это immutable, ёбта, то есть неизменяемый. Если ты такой: «А дай-ка я к своей строке что-нибудь прибавлю!» — и вызываешь concat() или substring(), то Java не переписывает скрижаль, нет. Она берёт, сука, берёт новый кусок гранита, переписывает на него старый текст плюс твои добавки и молча совает тебе в руки. А старый камень так и лежит, не тронутый. Исходный объект — в ахуе, он не менялся.
String s1 = "hello"; // Нашли готовую скрижаль в музее (пуле строк)
String s2 = new String("hello"); // Заказали новую, точную копию, у скульптора. Объект новый!
String s3 = s1.concat(" world"); // Старую скрижаль оставили, высекли новую: "hello world"
System.out.println(s1 == s2); // false — разные же камни, ёпта!
System.out.println(s1.equals(s2)); // true — а вот надписи-то одинаковые!
System.out.println(s1); // "hello" — видишь? Исходная не изменилась ни на йоту.
А теперь про этот ваш String Pool, он же пул строк. Это, блядь, такая хитрая оптимизация, чтоб память не жрать как не в себя. Когда ты пишешь String s = "abc", Java не создаёт новый объект с нуля каждый раз. Она сначала лезет в этот пул — специальную полочку в куче — и смотрит: а нет ли там уже готовой таблички с надписью «abc»? Если есть — просто даёт тебе ссылку на неё. Поэтому "abc" == "abc" будет true, это один и тот же объект, одна и та же табличка на полке! Но если ты настойчивый и пишешь new String("abc"), то тебе, будь добр, вырежут новую табличку, даже если такая уже есть. И тогда == скажет false, потому что объекты-то разные, пиздец.
Под капотом там тоже веселье. Раньше, до Java 9, строки внутри были как массив char[], всё просто. А потом пришли умники и говорят: «А зачем нам тратить по два байта на каждый символ, если половина строк — это латиница?». И теперь там массив byte[] с флажком, который решает, хранить ли всё в компактном Latin-1 или в полнокровном UTF-16. Хитро, блядь, до безобразия.
И главное правило, которое надо выжечь себе на подкорке: для сравнения по смыслу всегда equals(), а == сравнивает ссылки — то есть, одни и те же это камни или просто похожие надписи. Перепутаешь — будешь потом неделю искать, почему твоя программа не работает, волнение ебать.
Ну и наконец, если тебе надо не скрижали создавать, а что-то динамически клепать — строку наращивать, изменять — то String для этого хуйня полная, простите за мой французский. Он для этого не предназначен. Бери:
StringBuilder— твой быстрый друг для однопоточной работы. Лепит строку как хочешь.StringBuffer— то же самое, но с тяжёлой броней потокобезопасности. Медленнее, но если с разных потоков лезут — то он.
Короче, запомни: String — это священная и неизменная хуйня. Хочешь лепить что-то динамически — не мучай его, возьми StringBuilder и будь счастлив. Всё, лекция окончена, можно идти пить чай.