В Java String — это тип объекта, который может хранить последовательность символов, заключённую в двойные кавычки. Каждый символ занимает 16 бит, то есть используется кодировка UTF 16-bit. Строка ведёт себя аналогично массиву символов. Java предоставляет мощный и гибкий API для работы со строками, позволяющий выполнять различные операции, такие как конкатенация (concatenation), сравнение (comparison) и манипуляции (manipulation).
Пример:
String name = "Geeks";
String num = "1234"
Программа:
// Java Program to demonstrate String
public class Geeks {
// Главная функция
public static void main(String args[]) {
// создание Java строки с помощью ключевого слова new
String str = new String("Geeks");
System.out.println(str);
}
}
Вывод
Geeks
Способы создания строки в Java
Существует два способа создания строки в Java:
- String literal (литерал строки)
- Использование ключевого слова new
Синтаксис:
<String_Type> <string_variable> = "<sequence_of_string>";
1. Литерал строки (Static Memory)
Этот подход экономит память, так как новые объекты не создаются, если такой строковый литерал уже существует в пуле строк (string constant pool).
Пример:
String demoString = "GeeksforGeeks";
2. Использование ключевого слова new (Heap Memory)
-
String s = new String("Welcome");
- В этом случае JVM создаст новый объект строки в обычной (не в пуле) области heap, а литерал «Welcome» будет размещён в пуле постоянных строк. Переменная
s
будет указывать на объект из heap.
В этом примере будет создан один объект. Сначала JVM не найдёт строку «Welcome» в пуле строк, поэтому создаст новый объект. После этого строка с таким значением уже появится в пуле, и при повторном использовании создаётся не новый объект, а возвращается ссылка на существующий.
Пример:
String demoString = new String("GeeksforGeeks");
Интерфейсы и классы для работы со строками в Java
- CharBuffer: класс, реализующий интерфейс CharSequence. Позволяет использовать буферы символов там, где ожидается CharSequence. Пример использования — пакет регулярных выражений java.util.regex.
- String: последовательность символов. В Java объекты String неизменяемы (immutable), то есть после создания их нельзя изменить.
Интерфейс CharSequence
CharSequence предназначен для представления последовательности символов в Java. Классы, реализующие этот интерфейс, предоставляют основные методы, такие как substring, lastOccurrence, firstOccurrence, concatenation, toUpperCase, toLowerCase и другие.
- String
- StringBuffer
- StringBuilder
1. String
Это класс с неизменяемыми объектами, то есть после создания его изменить нельзя. Чтобы изменить строку, нужно создать новый объект. Методы, такие как toUpperCase и toLowerCase, возвращают новый объект, не изменяя исходный. Класс автоматически обеспечивает безопасность при работе с потоками.
Синтаксис:
// Метод 1 String str= "geeks"; // Метод 2 String str= new String("geeks");
2. StringBuffer
StringBuffer — класс-партнёр String, но в отличие от него изменяемый (mutable) и потокобезопасный (thread safe). Его стоит использовать в многопоточной среде, когда объект StringBuffer разделяется между несколькими потоками. За счёт потокобезопасности возникает дополнительная нагрузка, поэтому класс применяют преимущественно в многопоточных программах.
Синтаксис:
StringBuffer demoString = new StringBuffer("GeeksforGeeks");
3. StringBuilder
StringBuilder является альтернативой String и StringBuffer. Он создаёт изменяемую последовательность символов, но не является потокобезопасным. Используется внутри одного потока, что не создаёт дополнительной нагрузки. В основном применяется в однопоточных программах.
Синтаксис:
StringBuilder demoString = new StringBuilder(); demoString.append("GFG");
4. StringTokenizer
Класс StringTokenizer предназначен для разбиения строки на токены (части).
Пример:
Объект StringTokenizer внутренне хранит текущую позицию в строке для токенизации. Некоторые операции двигают эту позицию вперёд. Токен возвращается в виде подстроки исходной строки, из которой был создан объект StringTokenizer.
StringJoiner — класс из пакета java.util, позволяющий создать последовательность строк, разделённую заданным разделителем и опционально начинающуюся с указанного префикса и заканчивающуюся суффиксом. Хотя такая операция реализуется и через StringBuilder с добавлением разделителя после каждой строки, StringJoiner упрощает этот процесс, сокращая объём кода.
Синтаксис:
public StringJoiner(CharSequence delimiter)
Ранее мы посмотрели, что строку можно создать с помощью литерала:
String demoString = "Welcome";
При создании JVM проверяет пул констант строк. Если строка отсутствует, создаётся новый объект, который помещается в пул. Если строка уже есть — создаётся ссылка на существующий объект. Кэш таких объектов называется String Constant Pool (пул констант строк). В версиях Java до JDK 6 пул располагался в области PermGen (Permanent Generation), а с JDK 7 был перенесён в основной Heap.
Неизменяемость строк в Java
В Java объекты строк являются неизменяемыми (immutable). Это значит, что они не могут быть изменены после создания. Если необходимо изменить строку, создаётся новый объект.
Пример:
// Java Program to demonstrate Immutable String in Java
import java.io.*;
class Geeks {
public static void main(String[] args) {
String s = "Sachin";
// Метод concat() добавляет строку в конец
s.concat(" Tendulkar");
// Выведется "Sachin",
// так как строки являются неизменяемыми объектами
System.out.println(s);
}
}
Вывод
Sachin
Здесь строка «Sachin» не изменилась, но при вызове concat
создаётся новый объект со значением “Sachin Tendulkar”. Вот почему строку называют неизменяемой.
Как видно, при создании строки создаются два объекта, но переменная s
всё равно ссылается на «Sachin», а не на «Sachin Tendulkar». Если же явно присвоить результат метода concat()
переменной, то она начнёт указывать на новый объект.
Пример: явное присваивание ссылки после конкатенации
// Java Program to demonstrate Explicitly assigned strings
import java.io.*;
class Geeks {
public static void main(String[] args) {
String name = "Sachin";
name = name.concat(" Tendulkar");
System.out.println(name);
}
}
Вывод
Sachin Tendulkar
Распределение памяти для строк в Java
Литерал строки
Когда объект String создаётся как литерал, он помещается в пул констант строк. Это позволяет JVM оптимизировать использование памяти. Пул расположен в области heap.
Пример 1: создание строки с помощью литерала
String str1 = "Hello";
Пример 2: создание двух строк с одинаковым значением через литералы
String str1 = "Hello"; String str2 = "Hello";
Использование ключевого слова new
Строку также можно создать с помощью оператора new, что выделит память динамически в области heap. Такие объекты не попадают в пул констант строк.
Пример 1: создание строк с помощью new
String str1 = new String("John"); String str2 = new String("Deo");
Если требуется поместить такую строку в пул, нужно вызвать метод intern()
.
Пример 2: использование intern()
для добавления строки в пул
// добавит строку в пул констант строк String internedString = demoString.intern();
Рекомендуется использовать литералы, так как это позволяет JVM эффективнее управлять памятью.
Обратите внимание: при использовании new или литералов строка хранится в памяти, но с помощью литералов и intern() объект попадает в пул констант внутри heap.
Пример создания строк разными способами:
// Java Program to Declare a String
import java.io.*;
import java.lang.*;
class Geeks {
public static void main(String[] args) {
// Объявление строки без использования new
String name = "GeeksforGeeks";
// Вывод строки
System.out.println("String name = " + name);
// Объявление строки с использованием new
String newString = new String("GeeksforGeeks");
// Вывод второй строки
System.out.println("String newString = " + newString);
}
}
Вывод
String name = GeeksforGeeks String newString = GeeksforGeeks
Примечание: объект String создаётся в области Heap, а литералы хранятся в специальной области памяти, известной как пул констант строк.
Перемещение пула строк из PermGen в обычный Heap
Область PermGen ограничена по размеру — обычно 64 МБ. Это создавало проблемы при создании и хранении большого количества строковых объектов. Чтобы решить эту проблему, пул строк был перемещён в общий Heap, который обычно имеет большой размер.
Для повышения эффективности Java стала использовать концепцию строковых литералов. При применении оператора ‘new’ JVM создаёт новый объект строки в обычной области heap, даже если аналогичный объект уже присутствует в пуле.
Пример:
String demoString = new String("Bhubaneswar");
Рассмотрим пример на Java, который поможет визуализировать структуру памяти JVM:
Реализация:
// Java program to visualize JVM memory structure
class Geeks {
public static void main(String args[]) {
// Объявление строк с помощью литералов
String s1 = "TAT";
String s2 = "TAT";
// Объявление строк с использованием new
String s3 = new String("TAT");
String s4 = new String("TAT");
// Вывод всех строк
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
}
}
Вывод
TAT TAT TAT TAT
Примечание: все объекты в Java хранятся в heap. Ссылочные переменные находятся либо в стеке, либо внутри других объектов, тем самым тоже располагаясь в heap.
Пример 1:
// Конструктор строки из подмассива байтов
// Класс с точкой входа
class Geeks {
public static void main(String args[]) {
// Создание ASCII-массива
byte ascii[] = { 71, 70, 71 };
// Создание строки из байтового массива
String firstString = new String(ascii);
System.out.println(firstString);
// Создание строки из байтового массива с указанием индексов
String secondString = new String(ascii, 1, 2);
System.out.println(secondString);
}
}
Вывод
GFG FG
Пример 2:
// Создание строки из другой строки
class Geeks {
public static void main(String args[]) {
// Массив символов
char characters[] = { 'G', 'f', 'g' };
// Создание строки из массива символов
String firstString = new String(characters);
// Создание строки из другой строки
String secondString = new String(firstString);
System.out.println(firstString);
System.out.println(secondString);
}
}
Вывод
Gfg Gfg
—
🔑 Ключевые моменты:
— В Java строки представлены неизменяемыми объектами класса String — любые изменения создают новое значение.
— Строки можно создавать двумя способами: как литералы (хранятся в String Constant Pool) и с помощью оператора new (создаётся новый объект в heap).
— Интерфейс CharSequence реализуют основные классы для работы со строками: String, StringBuffer, StringBuilder.
— StringBuffer безопасен для многопоточного использования, StringBuilder — нет, и потому быстрее для однопоточных задач.
— Пул строк была перенесён из ограниченной области PermGen в основной Heap начиная с JDK 7, что улучшило управление памятью.