$ sudo teach IT
МОДУЛЬ 2 · УРОК 3

Символы и строки — char, Unicode и String

Изучаем тип char, знакомимся с Unicode, учимся работать со строками String, узнаём об иммутабельности и безопасном сравнении

~25 минут Для новичков Java

Тип 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 вопросов

Работа со строками

Premium