Клас String
Цей клас є послідовністю символів. Усі визначені у програм рядкові літерали, на зразок This is String - це екземпляри класу String. String має дві фундаментальні особливості:- це immutable (постійний) клас
- це final клас
final
) і екземпляри класу не можна змінити після створення ( immutable
). Це дає класу String кілька важливих переваг:
-
Завдяки незмінності, хешкод екземпляра класу String кешується. Його не потрібно обчислювати щоразу, тому що значення полів об'єкта ніколи не зміняться після його створення. Це дає високу продуктивність при використанні даного класу як ключ для
HashMap
. -
Клас String можна використовувати у багатопотоковому середовищі без додаткової синхронізації.
-
Ще одна особливість класу String — для нього перевантажений оператор
+
Java в Java. Тому конкатенація (складання) рядків виконується досить просто:
public static void main(String[] args) {
String command = "Follow" + " " + "the" + " " + "white" + " " + "rabbit";
System.out.println(command); // Follow the white rabbit
}
Під капотом конкатенація рядків виконується класом StringBuilder або StringBuffer (на розсуд компілятора) і способом append
(про ці класи поговоримо трохи пізніше). Якщо ми складатимемо екземпляри класу String з екземплярами інших класів, останні будуть наводитися до рядкового подання:
public static void main(String[] args) {
Boolean b = Boolean.TRUE;
String result = "b is " + b;
System.out.println(result); //b is true
}
Це ще одна цікава властивість класу String: об'єкти будь-яких класів можна привести до рядкового уявлення, використовуючи метод toString()
, визначений у класі Object
та успадкований усіма іншими класами. Часто метод toString() об'єкта викликається неявно. Наприклад, коли ми виводимо щось на екран або складаємо String з об'єктом іншого класу. Клас String має ще одну особливість. Усі строкові літерали, визначені в Java коді, на кшталт "asdf", на етапі компіляції кешуються і додаються у пул рядків. Якщо ми запустимо наступний код:
String a = "Wake up, Neo";
String b = "Wake up, Neo";
System.out.println(a == b);
Ми побачимо в консолі true , тому що змінні a
і b
насправді посилатимуться на той самий екземпляр класу String, доданий в пул рядків на етапі компіляції. Тобто різні екземпляри класу з однаковим значенням не створюються, і пам'ять економиться.
Недоліки:
Неважко здогадатися, що клас String потрібен насамперед для роботи з рядками. Але в деяких випадках перелічені вище особливості класу String можуть перетворитися з переваг на недоліки. Після створення рядків у коді Java-програми з ними часто відбувається безліч операцій:- переведення рядків у різні регістри;
- вилучення підрядок;
- конкатенація;
- і т.д.
public static void main(String[] args) {
String s = " Wake up, Neo! ";
s = s.toUpperCase();
s = s.trim();
System.out.println("\"" + s + "\"");
}
З першого погляду здається, що ми перевели рядок "Wake up, Neo!" у верхній регістр видалабо з цього рядка зайві прогалини і обернули в лапки. Насправді, через незмінність класу String, у результаті кожної операції створюються нові екземпляри рядків, а старі відкидаються, породжуючи велику кількість сміття. Як уникнути нераціонального використання пам'яті?
Клас StringBuffer
Щоб впоратися зі створенням тимчасового сміття через модифікації об'єкта String, можна використовувати клас StringBuffer. Цеmutable
клас, тобто. змінюється. Об'єкт класу StringBuffer може містити певний набір символів, довжину і значення якого можна змінити через виклик певних методів. Подивимося, як працює цей клас. Для створення нового об'єкта використовується один із його конструкторів, наприклад:
- StringBuffer() — створить порожній (який не містить символів) об'єкт
- StringBuffer(String str) — створить об'єкт на основі змінної str (що містить усі символи str у тій самій послідовності)
StringBuffer sb = new StringBuffer();
StringBuffer sb2 = new StringBuffer("Not empty");
Конкатенація рядків через StringBuffer Java виконується за допомогою методу append
. Взагалі метод append
у класі StringBuffer перевантажений таким чином, що може приймати практично будь-який тип даних:
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(new Integer(2));
sb.append("; ");
sb.append(false);
sb.append("; ");
sb.append(Arrays.asList(1,2,3));
sb.append("; ");
System.out.println(sb); // 2; false; [1, 2, 3];
}
Метод append
повертає об'єкт, на якому був викликаний (як і багато інших методів), що дозволяє викликати його "ланцюжком". Приклад вище можна написати так:
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(new Integer(2))
.append("; ")
.append(false)
.append("; ")
.append(Arrays.asList(1,2,3))
.append("; ");
System.out.println(sb); // 2; false; [1, 2, 3];
}
Для роботи з рядками клас StringBuffer має ряд методів. Перерахуємо основні:
delete(int start, int end)
- Видаляє підстроку символів починаючи з позиціїstart
, закінчуючиend
deleteCharAt(int index)
- Видаляє символ в позиціїindex
insert(int offset, String str)
- Вставляє рядокstr
у позиціюoffset
. Методinsert
також перевантажений і може приймати різні аргументиreplace(int start, int end, String str)
— замінить усі символи, починаючи з позиціїstart
до позиціїend
наstr
reverse()
- Змінює порядок всіх символів на протилежнийsubstring(int start, int end)
— поверне підрядок, починаючи з позиціїstart
до позиціїend
substring(int start)
— поверне підрядок, починаючи з позиції
start
public static void main(String[] args) {
String numbers = "0123456789";
StringBuffer sb = new StringBuffer(numbers);
System.out.println(sb.substring(3)); // 3456789
System.out.println(sb.substring(4, 8)); // 4567
System.out.println(sb.replace(3, 5, "ABCDE")); // 012ABCDE56789
sb = new StringBuffer(numbers);
System.out.println(sb.reverse()); // 9876543210
sb.reverse(); // Повернем початковий порядок
sb = new StringBuffer(numbers);
System.out.println(sb.delete(5, 9)); // 012349
System.out.println(sb.deleteCharAt(1)); // 02349
System.out.println(sb.insert(1, "One")); // 0One2349
}
Переваги:
-
Як вже сказано, StringBuffer — клас, що змінюється, тому при роботі з ним не виникає такої ж кількості сміття в пам'яті, як зі String. Тому, якщо над рядками проводиться багато модифікацій, краще використовувати
StringBuffer
. -
StringBuffer – потокобезпечний клас. Його методи синхронізовані, а екземпляри можуть бути використані кількома потоками одночасно.
Недоліки:
З одного боку, потокобезпека – перевага класу, а з іншого – недолік. Синхронізовані методи працюють повільніше не синхронізовані. І тут у гру вступає StringBuilder. Давайте розберемося, що це за клас Java - StringBuilder, які методи є і в чому його особливості.Клас StringBuilder
StringBuilder в Java - клас, що представляє послідовність символів. Він дуже схожий на StringBuffer у всьому, крім потокобезпеки. StringBuilder надає API, аналогічний API StringBuffer'a. Продемонструємо це вже на знайомому прикладі, замінивши оголошення змінних з StringBufer'а на StringBuilder:public static void main(String[] args) {
String numbers = "0123456789";
StringBuilder sb = new StringBuilder(numbers);
System.out.println(sb.substring(3)); //3456789
System.out.println(sb.substring(4, 8)); //4567
System.out.println(sb.replace(3, 5, "ABCDE")); //012ABCDE56789
sb = new StringBuilder(numbers);
System.out.println(sb.reverse()); //9876543210
sb.reverse(); // Повернем початковий порядок
sb = new StringBuilder(numbers);
System.out.println(sb.delete(5, 9)); //012349
System.out.println(sb.deleteCharAt(1)); //02349
System.out.println(sb.insert(1, "One")); //0One2349
}
Різниця лише в тому, що StringBuffer є потокобезпечним, і всі його методи синхронізовані, а StringBuilder — ні. Це єдина особливість. StringBuilder в Java працює швидше StringBuffer'а завдяки несинхронізованості методів. Тому в більшості випадків, крім багатопотокового середовища, краще використовувати для програми Java StringBuilder. Резюмуємо все у порівняльній таблиці трьох класів:
String vs StringBuffer vs StringBuilder
String | StringBuffer | StringBuilder | |
---|---|---|---|
Змінність | Immutable (ні) |
mutable (так) |
mutable (так) |
Розширюваність | final (ні) |
final (ні) |
final (ні) |
Потокобезпека | Так, за рахунок незмінності | Так, за рахунок синхронізації | Ні |
Коли використовувати | При роботі з рядками, які рідко модифікуватимуться | При роботі з рядками, які часто модифікуватимуться в багатопотоковому середовищі | При роботі з рядками, які часто модифікуватимуться, в однопотоковому середовищі |
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ