Символы и строки — char, Unicode и String
Изучаем тип char, знакомимся с Unicode, учимся работать со строками String, узнаём об иммутабельности и безопасном сравнении
Тип char — один символ
Тип char хранит один символ. Он занимает 16 бит (2 байта) памяти и может представлять символы из набора Unicode — международного стандарта, включающего буквы, цифры, знаки препинания, эмодзи и символы практически всех письменностей мира.
Символы в Java записываются в одинарных кавычках:
public class CharExample {
public static void main(String[] args) {
char letter = 'A';
char digit = '7';
char symbol = '@';
char space = ' ';
char russian = 'Я';
char emoji = '😀';
System.out.println("Буква: " + letter);
System.out.println("Цифра: " + digit);
System.out.println("Символ: " + symbol);
System.out.println("Пробел: [" + space + "]");
System.out.println("Русская буква: " + russian);
System.out.println("Эмодзи: " + emoji);
}
}
Обратите внимание: char — это знаковый беззнаковый тип. Диапазон от 0 до 65 535 (2¹⁶ - 1). В отличие от других беззнаковых типов в Java, char является единственным беззнаковым примитивом.
Unicode — международная таблица символов
Unicode — это стандарт, который присваивает каждому символу уникальный числовой код. Это позволяет компьютерам работать с буквами любых языков: латиницей, кириллицей, арабским, китайским, японским и тысячами других систем письма.
Каждый символ Unicode имеет свой код — число от 0 до 1 114 111 (0x10FFFF). Java использует кодовую таблицу UCS-2, которая включает основные символы BMP (Basic Multilingual Plane).
public class UnicodeExample {
public static void main(String[] args) {
char latinA = 'A';
char cyrillicA = 'А';
char greekA = 'Α';
char arabicA = 'أ';
char chineseChar = '中';
char yenSign = '¥';
char copyrightSign = '©';
System.out.println("Латинская A: " + latinA + " = " + (int) latinA);
System.out.println("Кириллическая А: " + cyrillicA + " = " + (int) cyrillicA);
System.out.println("Греческая Α: " + greekA + " = " + (int) greekA);
System.out.println("Арабская أ: " + arabicA + " = " + (int) arabicA);
System.out.println("Китайский иероглиф: " + chineseChar + " = " + (int) chineseChar);
System.out.println("Знак yen: " + yenSign + " = " + (int) yenSign);
System.out.println("Знак копирайта: " + copyrightSign + " = " + (int) copyrightSign);
}
}
Приведение char к int показывает численное значение символа в таблице Unicode. Например, 'A' = 65, 'Я' = 1071, '中' = 20013.
Запись символьных литералов
Символ можно записать несколькими способами:
Обычный символ
char a = 'A';
char рус = 'Б';
Экранированные символы
Некоторые символы невозможно напечатать напрямую (перевод строки, табуляция), поэтому используются специальные последовательности:
char newline = '\n'; // Перевод строки
char tab = '\t'; // Табуляция
char carriageReturn = '\r'; // Возврат каретки
char backspace = '\b'; // Затирание символа
char formFeed = '\f'; // Перевод страницы
char backslash = '\\'; // Обратный слэш
char singleQuote = '\''; // Одинарная кавычка
Шестнадцатеричный Unicode
char heart = '\u2764'; // ❤
char smile = '\u263A'; // ☺
char star = '\u2605'; // ★
char check = '\u2714'; // ✔
char copyright = '\u00A9'; // ©
Префикс \u указывает на шестнадцатеричное значение Unicode. За ним должны следовать ровно 4 hex-цифры (дополненные нулями при необходимости).
Числовой литерал
char a = 65; // A (десятичная запись)
char b = 0x41; // A (шестнадцатеричная)
char c = 0101; // A (восьмеричная)
char d = 0b01000001; // A (двоичная)
Управляющие последовательности
Полная таблица управляющих последовательностей в Java:
| Последовательность | Описание | Unicode |
|---|---|---|
| \b | Возврат на шаг (backspace) | \u0008 |
| \t | Табуляция | \u0009 |
| \n | Перевод строки (newline) | \u000A |
| \f | Перевод страницы (form feed) | \u000C |
| \r | Возврат каретки (carriage return) | \u000D |
| \" | Двойная кавычка | \u0022 |
| \' | Одинарная кавычка | \u0027 |
| \\ | Обратный слэш | \u005C |
| \uXXXX | Unicode-символ по коду | Любой |
Строки String — основной тип для текста
Строка (String) — это последовательность символов. В отличие от char, строка может содержать произвольное количество символов: от пустой строки "" до текста в миллионы символов.
Строки записываются в двойных кавычках:
public class StringExample {
public static void main(String[] args) {
String greeting = "Привет, мир!";
String empty = "";
String name = "Александр";
String withNumber = "Мне 25 лет";
String withSpecial = "Цена: $9.99";
String multiLine = "Это\nстрока\nс\nпереводами\nстрок";
System.out.println(greeting);
System.out.println("Имя: " + name);
System.out.println(withNumber);
System.out.println(withSpecial);
System.out.println(multiLine);
}
}
String — это класс, а не примитивный тип! Но в Java он используется так же удобно, как примитивы. Строка — это объект, созданный из класса String.
Многострочные строки (Text Blocks)
Начиная с Java 15, можно создавать многострочные строки с помощью тройных кавычек:
public class TextBlockExample {
public static void main(String[] args) {
String html = """
<html>
<body>
<p>Привет, мир!</p>
</body>
</html>
""";
String sql = """
SELECT *
FROM users
WHERE age > 18
ORDER BY name
""";
System.out.println(html);
System.out.println(sql);
}
}
Текстовые блоки начинаются с """ и заканчиваются """. Отступ перед закрывающими кавычками определяет общее количество пробелов, которые будут отняты от каждого строки.
Конкатенация строк — склеивание
Оператор + со строками выполняет конкатенацию (склеивание):
public class ConcatenationExample {
public static void main(String[] args) {
String firstName = "Иван";
String lastName = "Петров";
String fullName = firstName + " " + lastName;
System.out.println("Полное имя: " + fullName);
int age = 25;
String info = "Мне " + age + " лет";
System.out.println(info);
String result = "1" + 2 + 3;
String result2 = 1 + 2 + "3";
System.out.println("1 + 2 + 3 = " + result);
System.out.println("1 + 2 + 3 = " + result2);
}
}
Осторожно! Порядок операций важен! "1" + 2 + 3 = "123" (строка + число + число), а 1 + 2 + "3" = "33" (число + число + строка). Слева направо: первые два числа складываются (1+2=3), затем конкатенируются со строкой.
Сравнение строк — НЕ через == !
Это одна из самых важных концепций для начинающих. Оператор == сравнивает ссылки (адреса объектов), а не содержимое строк:
public class StringComparison {
public static void main(String[] args) {
String s1 = "Привет";
String s2 = "Привет";
String s3 = new String("Привет");
System.out.println("s1 == s2: " + (s1 == s2));
System.out.println("s1 == s3: " + (s1 == s3));
System.out.println("s1.equals(s2): " + s1.equals(s2));
System.out.println("s1.equals(s3): " + s1.equals(s3));
}
}
s1 == s2 вернёт true (из-за пула строк — строковые литералы переиспользуются), но s1 == s3 вернёт false, потому что s3 создан через new — это другой объект в памяти. Метод equals() сравнивает содержимое строк.
Золотое правило: Всегда сравнивайте строки через equals(), а не через ==. Единственное исключение — когда вы намеренно хотите проверить, являются ли две ссылки одним и тем же объектом.
Методы класса String
Строки в Java提供ают множество полезных методов:
public class StringMethods {
public static void main(String[] args) {
String text = "Привет, Мир!";
System.out.println("Длина: " + text.length());
System.out.println("Буква[0]: " + text.charAt(0));
System.outprintln("Подстрока: " + text.substring(8));
System.out.println("Подстрока 0-5: " + text.substring(0, 6));
System.out.println("В верхнем регистре: " + text.toUpperCase());
System.out.println("В нижнем регистре: " + text.toLowerCase());
System.out.println("Содержит 'Мир'? " + text.contains("Мир"));
System.out.println("Начинается с 'Привет'? " + text.startsWith("Привет"));
System.out.println("Заканчивается на '!'? " + text.endsWith("!"));
System.out.println("Индекс 'Мир': " + text.indexOf("Мир"));
System.out.println("Замена: " + text.replace("Мир", "Java"));
System.out.println("Убрать пробелы: [" + " Привет ".trim() + "]");
}
}
Основные методы строк:
length()— количество символовcharAt(index)— символ по индексу (нумерация с 0)substring(start, end)— подстрока от start до end (не включая end)toUpperCase() / toLowerCase()— смена регистраcontains(str)— содержит ли строка подстрокуstartsWith(str) / endsWith(str)— начало/конец строкиindexOf(str)— позиция подстроки (-1 если не найдено)replace(old, new)— замена подстрокиtrim()— удаление пробелов в начале и концеequals(str)— сравнение содержимогоisEmpty()— пустая ли строкаtoCharArray()— преобразование в массив символов
Иммутабельность строк
Строки в Java неизменяемы (иммутабельны). Это значит, что после создания строку нельзя изменить — ни один метод не меняет саму строку, каждый возвращает новую:
public class ImmutabilityExample {
public static void main(String[] args) {
String original = "Привет";
String upper = original.toUpperCase();
String replaced = original.replace("и", "И");
System.out.println("Оригинал: " + original);
System.out.println("Верхний регистр: " + upper);
System.out.println("После замены: " + replaced);
}
}
Как видите, original осталось «Привет» — методы вернули новые строки, не изменяя оригинал. Это делает строки потокобезопасными и позволяет безопасно переиспользовать строки.
Пула строк (String Pool) —特殊的область памяти, где Java хранит строки-литералы для переиспользования. Когда вы создаёте строку "Привет", Java сначала проверяет, есть ли уже такая строка в пуле. Если да — возвращает существующую. Это экономит память.
StringBuilder — изменяемые строки
Если нужно часто изменять строку (много конкатенаций в цикле), используйте StringBuilder. Он создаёт изменяемую строку и гораздо эффективнее:
public class StringBuilderExample {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("Привет");
sb.append(", ");
sb.append("мир");
sb.append("!");
String result = sb.toString();
System.out.println(result);
StringBuilder reversed = new StringBuilder("Java");
reversed.reverse();
System.out.println("Наоборот: " + reversed);
StringBuilder builder = new StringBuilder("Привет");
builder.insert(6, ", мир");
System.out.println("Вставка: " + builder);
StringBuilder deleter = new StringBuilder("Удалить этот текст");
deleter.delete(8, 12);
System.out.println("Удаление: " + deleter);
}
}
Форматированный вывод
Метод String.format() и System.out.printf() позволяют форматировать строки:
public class FormatExample {
public static void main(String[] args) {
String name = "Александр";
int age = 30;
double salary = 75_000.50;
System.out.printf("Имя: %s, Возраст: %d%n", name, age);
System.out.printf("Зарплата: %,.2f руб.%n", salary);
String formatted = String.format(
"%-10s | %5d | %10.2f",
name, age, salary
);
System.out.println(formatted);
System.out.printf("Число Пи: %.10f%n", Math.PI);
System.out.printf("В двоичной: %s%n", Integer.toBinaryString(42));
}
}
Основные спецификаторы формата:
%s— строка%d— целое число%f— дробное число%n— перевод строки (платформо-независимый)%10s— строка шириной 10 (выравнивание вправо)%-10s— строка шириной 10 (выравнивание влево)%.2f— дробное с 2 знаками после запятой%,d— целое число с разделителем тысяч
Практический пример: генератор приветствий
public class GreetingGenerator {
public static void main(String[] args) {
String name = "Мария";
int hour = 14;
String greeting;
if (hour < 6) {
greeting = "Доброй ночи";
} else if (hour < 12) {
greeting = "Доброе утро";
} else if (hour < 18) {
greeting = "Добрый день";
} else {
greeting = "Добрый вечер";
}
String message = greeting + ", " + name + "!";
System.out.println(message);
String upperName = name.toUpperCase();
String reversed = new StringBuilder(name).reverse().toString();
System.out.println("Имя верхним регистром: " + upperName);
System.out.println("Имя наоборот: " + reversed);
System.out.println("Длина имени: " + name.length() + " символов");
String padded = String.format("[%20s]", name);
System.out.println("С выравниванием: " + padded);
}
}
Преобразование между char и числовыми типами
Поскольку char хранит числовое значение Unicode, его можно преобразовывать в числа:
public class CharConversion {
public static void main(String[] args) {
char ch = 'A';
int code = ch;
System.out.println("Символ: " + ch + ", Код: " + code);
char fromInt = (char) 97;
System.out.println("Код 97: " + fromInt);
char[] chars = {'J', 'a', 'v', 'a'};
String word = new String(chars);
System.out.println("Из массива: " + word);
String str = "Hello";
char[] array = str.toCharArray();
for (char c : array) {
System.out.print(c + " ");
}
System.out.println();
}
}
Частые ошибки новичков
Ошибка 1: Сравнение строк через ==
String s1 = new String("Привет");
String s2 = new String("Привет");
if (s1 == s2) { ... } // НЕНАДЁЖНО!
if (s1.equals(s2)) { ... } // ПРАВИЛЬНО
Ошибка 2: Путаница с индексами
String s = "Привет";
char first = s.charAt(0); // 'П'
char last = s.charAt(5); // 'т'
// s.charAt(6) — StringIndexOutOfBoundsException!
Ошибка 3: Забытые кавычки
char a = 'A'; // Правильно
char b = 'AB'; // ОШИБКА: char — один символ!
String c = "AB"; // Правильно для строки
Итоги урока
- char — 16-битный тип для одного символа, записывается в одинарных кавычках
- Unicode — международный стандарт кодирования символов
- Управляющие последовательности: \n, \t, \\, \", \uXXXX
- String — класс для строк, записывается в двойных кавычках
- Строки иммутабельны — методы возвращают новые строки
- Сравнивайте строки через equals(), НЕ через ==
- Конкатенация через + работает, но неэффективна в циклах
- Для частых изменений используйте StringBuilder
- String.format() и printf() для форматированного вывода
- String — это объект, не примитивный тип
Тест: Символы и строки — char, Unicode, String
10 вопросов