Оригінал: Create Java String Using ” ” or Constructor? By X Wang 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
посилаються на два різні об'єкти в пам'яті (у купі). Різні об'єкти мають різні посилання. Ця діаграма ілюструє дві вищеописані ситуації:
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
substring()
3. substring() в JDK 6
Строковий тип підтримується масивом типуchar
. У JDK 6 клас String
містить 3 поля: char value[]
, int offset
, int count
. Вони використовуються для збереження реального масиву символів, індексу першого символу в масиві, числа символів у рядку. Коли викликаний метод substring()
, він створює новий рядок, але значення змінної все ще вказує на той самий масив у купі. Різниця між двома рядками полягає в їх числі символів та значення індексу початкового символу в масиві. Нижченаведений код спрощений і містить тільки основне для демонстрації проблеми.
//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 в хіпі знаходиться один-єдиний масив . Після виконання рядків коду с
String
substring()
String
L
b
ongL
c
aLongLongString
aLongLongString
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 7substring()
дійсно створює новий масив у купі.
//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);
}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ