JavaRush /Java блог /Random UA /Переклад: Створення об'єктів типу String в Java - викорис...
FellowSparrow
12 рівень
Lvov

Переклад: Створення об'єктів типу String в Java - використання "" або конструктора?

Стаття з групи Random UA
Оригінал: Create Java String Using ” ” or Constructor? By X Wang Переклад: Створення об'єктів типу String в Java - використанняJava рядок може бути створена з використанням двох способів:
String x = "abc";
String y = new String("abc");
У чому різниця між використанням подвійних лапок і використанням конструктора?

1. Подвійні лапки vs. Конструктор

На це запитання можна відповісти, розглянувши два прості приклади. Приклад 1:
String a = "abcd";
String b = "abcd";
System.out.println(a == b);  // True
System.out.println(a.equals(b)); // True
a==bІстинно тому, що aй bпосилаються на той самий об'єкт — рядок, оголошений як літерал (рядковий літерал далі) в області методів (відсилаємо читача до джерела на нашому ресурсі: Топ 8 діаграм для розуміння Java , діаграма 8). Коли один і той же рядковий літерал створений більше одного разу, в пам'яті зберігається лише одна копія рядка, лише один його екземпляр (у нашому випадку "abcd"). Це називається "інтернування рядків". Усі строкові константи, що обробляються на етапі компіляції, Java інтернуються автоматично. Приклад 2:
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d);  // False
System.out.println(c.equals(d)); // True
c==dпомилково тому, що cі dпосилаються на два різні об'єкти в пам'яті (у купі). Різні об'єкти мають різні посилання. Ця діаграма ілюструє дві вищеописані ситуації: Переклад: Створення об'єктів типу String в Java - використання

2. Інтернування рядків на етапі виконання програми

Автор дякує LukasEder (коментар нижче належить йому): Інтернування рядків може відбуватися також і під час виконання програми, навіть якщо два рядки створені за допомогою конструкторів:
String c = new String("abcd").intern();
String d = new String("abcd").intern();
System.out.println(c == d);  // Now true
System.out.println(c.equals(d)); // True

3. Коли ж використовувати подвійні лапки і коли конструктори

Внаслідок того, що літерал "abcd" має тип String, використання конструктора створить додатково непотрібний об'єкт. Таким чином, подвійні лапки повинні бути використані, якщо вам потрібно просто створити рядок. Якщо вам дійсно необхідно створити новий об'єкт у купі, ви маєте використовувати конструктор. Тут (оригінал) показано варіанти використання. (Перекладений текст наводжу далі. Але все ж таки рекомендую ознайомитися з кодом коментаторів за цим посиланням.)

Метод substring() в JDK 6 та JDK 7

The substring() Method in JDK 6 and JDK 7 By X Wang Метод substring(int beginIndex, int endIndex)JDK 6 і JDK 7 різняться. Знання цих відмінностей може допомогти вам краще використати цей метод. Задля зручності прочитання далі substring()будемо мати на увазі повний синтаксис, тобто. substring(int beginIndex, int endIndex).

1. Що робить substring()?

Метод substring(int beginIndex, int endIndex)повертає рядок, який починається із символу під номером beginIndexта закінчується символом під номером endIndex-1.
String x = "abcdef";
x = x.substring(1,3);
System.out.println(x);
Output:
bc

2. Що відбувається під час виклику substring()?

Ви можете знати, що внаслідок незмінності xпри присвоєнні x результату вказує на повністю новий рядок (див. діаграму): Однак, ця діаграма не повністю правильна x.substring(1,3); вона не демонструє, що насправді відбувається у купі. Те, що дійсно відбувається, коли викликається , відрізняється в JDK 6 і JDK 7. xПереклад: Створення об'єктів типу String в Java - використанняsubstring()

3. substring() в JDK 6

Строковий тип підтримується масивом типу char. У JDK 6 клас Stringмістить 3 поля: char value[], int offset, int count. Вони використовуються для збереження реального масиву символів, індексу першого символу в масиві, числа символів у рядку. Коли викликаний метод substring(), він створює новий рядок, але значення змінної все ще вказує на той самий масив у купі. Різниця між двома рядками полягає в їх числі символів та значення індексу початкового символу в масиві. Переклад: Створення об'єктів типу String в Java - використанняНижченаведений код спрощений і містить тільки основне для демонстрації проблеми.
//JDK 6
String(int offset, int count, char value[]) {
	this.value = value;
	this.offset = offset;
	this.count = count;
}

public String substring(int beginIndex, int endIndex) {
	//check boundary
	return  new String(offset + beginIndex, endIndex - beginIndex, value);
}

4. Проблема, викликана substring() в JDK 6

Якщо у вас є дуже довгий рядок, але вам потрібна тільки маленька її частина, яку ви отримуєте щоразу використовуючи substring(). Це викликає проблеми при виконанні, з того моменту, як вам потрібна маленька частина, ви все одно змушені зберігати весь рядок цілком. Для JDK 6 рішення полягає в наведеному коді, який приведе рядок до цього підрядку:
x = x.substring(x, y) + ""
Користувач STepeR сформулював питання (див. його коментар), і видалося необхідним доповнити п.4. "Проблема, викликана substring()в JDK 6" найбільшим прикладом. Сподіваюся, це й буде відповіддю і допоможе іншим швидше розібратися, у чому суть проблеми. Ось код:
String a = "aLongLongString";
String b = a.substring(1, 2);
String c = a.substring(2, 6);
Отже, в JDK 7 об'єкти типу b, створені в результаті виклику методу об'єкта типу , будуть посилатися на два новостворених масиву в хіпі - для , для . Ці два нових масиву будуть зберігатися в хіпі НАРЯД з вихідним масивом , на який посилається a. Тобто. вихідний масив нікуди зникає. Тепер повернемося до JDK 6. У JDK 6 в хіпі знаходиться один-єдиний масив . Після виконання рядків коду сStringsubstring()StringLbongLcaLongLongStringaLongLongString
String b = a.substring(1, 2);
String c = a.substring(2, 6);
об'єкти b, cпосилаються все на той же масив у хіпі, відповідний об'єкту a: b- На елементи з 1-го індексу до 2-го, c- На елементи з 2-го індексу до 6-го (нумерація починається з 0-го, нагадування). Тобто. Очевидно, що будь-яке подальше звернення до змінним bабо c в JDK 6 насправді призведе до "відрахування" потрібних елементів вихідного масиву в хіпі. У JDK 7 будь-яке подальше звернення до змінних bабо c викликає звернення до потрібних масивів меншого розміру, які вже створені та "живуть" у хіпі. Тобто. явно JDK 7 у подібних ситуаціях використовує фізично більше пам'яті. Але давайте уявімо можливий варіант: змінним bі cприсвоєні деякі підрядки змінноїa, і надалі всі використовують лише їх - об'єкти bта c. До змінної a просто вже ніхто не звертається, на неї немає посилань (саме це має на увазі автор статті). У результаті в якийсь момент часу спрацьовує збирач сміття, і (в найзагальнішому вигляді, звичайно) отримуємо 2 різних ситуації: JDK 6 : знищений (garbage collected) об'єкт a, АЛЕ - вихідний величезний масив у хіпі живий; його використовують bі c. JDK 7: знищено об'єкт a разом із вихідним масивом у хіпі. Ось цей момент у JDK 6 може призвести до витоку пам'яті (memory leak).

5. substring() в JDK 7

У JDK 7 метод покращено. У JDK 7 substring()дійсно створює новий масив у купі. Переклад: Створення об'єктів типу String в Java - використання
//JDK 7
public String(char value[], int offset, int count) {
	//check boundary
	this.value = Arrays.copyOfRange(value, offset, offset + count);
}

public String substring(int beginIndex, int endIndex) {
	//check boundary
	int subLen = endIndex - beginIndex;
	return new String(value, beginIndex, subLen);
}
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ