Ви колись замислювалися над різними способами ініціалізації рядка в Java? Ви використовуєте літерали рядка (наприклад, String str = "abc"
) чи застосовуєте ключове слово new
, як це: String str = new String("abc")
? На перший погляд, здається, що обидва підходи досягають одного й того ж результату — створення нового рядка. Однак спосіб управління пам'яттю за лаштунками має велике значення.
Зліва: Літерали рядка, як-от "abc" і "xyz", зберігаються в пулі рядків. Справа: Коли новий рядок створюється з використанням new, він додається ще в одну копію в купу поза пулом рядків.
Літерали Рядків
Коли ви використовуєте літерали рядків, як-от String str = "abc"
, значення рядка "abc"
додається до Пулу рядків (особлива область в пам'яті купи, резервована для зберігання літералів рядків). Змінна str
потім вказує на цей пулований рядок. Якщо рядок "abc"
вже існує в Пулі рядків, str
буде вказувати на існуючий рядок замість того, щоб створювати новий. Якщо пізніше ви присвоїте str
інше значення, наприклад, "xyz"
, новий рядок "xyz"
також додається до пулу рядків (якщо його там ще немає), і тепер str
вказує на нього.
Рядки з використанням ключового слова new
Використання ключового слова new
, наприклад, String str = new String("abc")
, створює новий об'єкт у пам'яті купи щоразу. Навіть якщо вміст рядка той самий, буде створено новий об'єкт. Додатково:
- Цей новий об'єкт посилається на значення рядка з пулу рядків, якщо воно вже існує.
- Якщо рядок ще не існує в Пулі рядків, він також буде доданий туди.
Ця поведінка може призвести до більшого споживання пам'яті, оскільки кожен виклик з ключовим словом new
виділяє пам'ять для нового об'єкта, навіть якщо рядок вже існує в Пулі рядків.
Незмінність Рядків
Рядки в Java є незмінними, що означає, що після створення рядка його значення не можна змінити. Наприклад:
- Коли ви переопризначаєте
str
на нове значення, наприкладstr = "xyz"
, новий рядок створюється в Пулі рядків (якщо його там ще немає), і теперstr
вказує на новий рядок. - Оригінальний рядок залишається незмінним, хоча з часом він може бути зібраний сміттям, якщо на нього більше немає посилань.
Інтернування Рядків
Ви можете явно додавати рядки до Пулу рядків, використовуючи метод intern()
. Наприклад:
String str = new String("abc");
String internedStr = str.intern();
Тут internedStr
буде вказувати на пулований варіант рядка, забезпечуючи існування лише одного екземпляра цього рядка в Пулі рядків.
Наслідки для Продуктивності
Використання літералів рядків зазвичай є більш ефективним у плані пам'яті, оскільки Пул рядків запобігає дублюванню однакових рядків. Ключове слово new
завжди створює новий об'єкт в пам'яті купи, незалежно від того, чи існує вже цей рядок у Пулі рядків. Надмірне використання new String(...)
може призвести до зайвого виділення пам'яті та перевантаження збору сміття.
Ключові моменти
- Літерали рядків працюють швидше і є більш ефективними з точки зору пам'яті, ніж використання ключового слова
new
. Тому використовуйте літерали, коли це можливо. - Будьте обережні щодо впливу на пам'ять при створенні кількох об'єктів рядків без потреби.
- Використовуйте
intern()
, щоб переконатися, що рядки потрапляють у пул, коли це необхідно, особливо у випадках, коли є багато дубльованих рядків.
Розуміння цих відмінностей допомагає писати більш ефективний та оптимізований код, особливо в сценаріях, коли рядки часто створюються або обробляються.
Перекладено з: Understanding String Initialization in Java