Гуманитарные науки

Естественные науки

⚛️ Физика

Программирование

🐍 PYTHON ЧЕРЕЗ СОЗДАНИЕ ИГР ☕ Java
Лекторий / Java / УРОК 3: Строки (String) в Java. Ссылочные типы данных
Конспект ☕ Java 21.07.2026

УРОК 3: Строки (String) в Java. Ссылочные типы данных

УРОК 3: Строки (String) в Java. Ссылочные типы данных


📌 ЦЕЛИ УРОКА

К концу этого урока ты сможешь:

  1. Объяснить разницу между примитивными и ссылочными типами данных

  2. Понять, где хранятся объекты в памяти (Heap) и как переменные-ссылки на них работают

  3. Объяснить механизм String Pool и его влияние на сравнение строк

  4. Правильно сравнивать строки через equals(), а не ==

  5. Использовать основные методы класса String для работы с текстом

  6. Понимать неизменяемость (immutability) строк и её последствия


СВЯЗЬ С ПРЕДЫДУЩИМИ ТЕМАМИ

В Уроке 2 мы разобрали 8 примитивных типов — они хранят значения "напрямую" в стеке. Сегодня знакомимся с принципиально другой категорией — ссылочными типами (reference types), и String — первый и самый частый пример такого типа, с которым ты столкнёшься.

Мы уже неявно использовали String в предыдущих уроках — каждый раз, когда писали текст в двойных кавычках внутри println("..."). Сегодня разберём, что это такое на самом деле, и почему это принципиально отличается от int или char.


📖 ТЕОРЕТИЧЕСКАЯ ЧАСТЬ

1. Примитивные vs ссылочные типы: фундаментальное отличие

В Java все типы данных делятся на две категории:

 Примитивные типыСсылочные типы
Примеры

int, double, char, boolean

String, массивы, любые классы

Где хранится значениеНапрямую в стекеОбъект — в куче (Heap), в стеке — только ссылка на него
Начинаются смаленькой буквыЗаглавной буквы (по конвенции)
Значение по умолчанию (для полей класса)

0, 0.0, false и т.д.

null

Количество типовРовно 8, фиксированоНеограниченно (можно создавать свои классы)

Ключевая идея: когда ты создаёшь примитивную переменную, в стеке хранится само значение:

int x = 5;   // в стеке: ячейка x содержит непосредственно число 5

Когда ты создаёшь переменную ссылочного типа, в стеке хранится не сам объект, а ссылка (адрес) на него, а сам объект физически лежит в другой области памяти — куче (Heap):

String name = "Alex";   // в стеке: ячейка "name" содержит АДРЕС, 
                          // по которому в куче лежит сам объект-строка "Alex"

2. Стек и куча: визуализация

СТЕК (Stack)                     КУЧА (Heap)
┌─────────────────┐                ┌────────────────────┐
│ main() frame    │                │                    │
│  ┌────────────┐ │                │   "Alex"           │
│  │ x = 5      │ │                │   (объект String)  │
│  │ name = ────┼─┼───────────────>│                    │
│  └────────────┘ │                └────────────────────┘
└─────────────────┘

Почему так устроено?

  • Стек — быстрая, но ограниченная по размеру память. Идеальна для маленьких значений фиксированного размера (примитивы).

  • Куча — большая область памяти для объектов произвольного размера (строка может быть 3 символа, а может быть 3 миллиона). Объекты в куче управляются сборщиком мусора (Garbage Collector) — специальным механизмом JVM, который автоматически удаляет объекты, на которые больше никто не ссылается.

Это принципиальное архитектурное решение: примитивы — быстрые и простые, объекты — гибкие, но требуют больше ресурсов на управление памятью.

3. Что такое String на самом деле

String — это класс (мы подробно разберём классы в Уроке 14, но уже сейчас важно зафиксировать: String — не примитив, а полноценный объект со своими методами).

String greeting = "Hello, World!";

Здесь greeting — это ссылка (переменная), указывающая на объект String с содержимым "Hello, World!", который физически хранится в куче.

Важная деталь синтаксиса: строки в Java пишутся строго в двойных кавычках "...". Одинарные кавычки '...' зарезервированы только для char (одного символа):

String text = "Hello";   // ✅ строка — двойные кавычки
char letter = 'H';        // ✅ символ — одинарные кавычки
String wrong = 'Hello';   // ❌ ОШИБКА компиляции — одинарные кавычки для строки недопустимы

4. Неизменяемость строк (String Immutability)

Это важнейшая концепция, которую нужно понять железно. Строки в Java неизменяемы (immutable) — после создания объект String никогда не может быть изменён. Любая операция, которая "как будто" меняет строку, на самом деле создаёт новый объект в памяти.

String s = "Hello";
s = s + " World";   // это НЕ изменяет исходный объект "Hello"!
                     // создаётся НОВЫЙ объект "Hello World",
                     // а переменная s теперь ссылается на него

Схематично, что происходит:

Шаг 1:                          Шаг 2 (после s = s + " World"):
СТЕК          КУЧА              СТЕК          КУЧА
s ────> "Hello"                 s ────> "Hello World"  (новый объект)
                                          "Hello"        (старый объект — 
                                                           станет мусором, 
                                                           его удалит GC)

Почему строки сделали неизменяемыми? Это осознанное архитектурное решение разработчиков Java по нескольким причинам:

  1. Безопасность — строки часто используются как ключи в системах безопасности (пароли, пути к файлам), их неизменяемость предотвращает случайное или злонамеренное изменение

  2. Потокобезопасность (thread-safety) — неизменяемые объекты автоматически безопасны при работе в многопоточной среде (об этом позже в продвинутых темах)

  3. Оптимизация памяти через String Pool (см. следующий пункт)

⚠️ Практическое следствие: если в цикле ты много раз "изменяешь" строку через конкатенацию, каждый раз создаётся новый объект в куче — это неэффективно. Для интенсивной работы со строками используют класс StringBuilder (разберём в Уроке 23), который является изменяемым.

5. String Pool — специальная оптимизация для строк

Java использует особый механизм — String Pool (пул строк, также называют String Constant Pool) — специальную область в куче, где хранятся строковые литералы (строки, написанные напрямую в коде через кавычки).

Принцип работы: когда ты создаёшь строку через литерал, JVM сначала проверяет, есть ли уже такая строка в пуле:

String a = "Java";
String b = "Java";

Здесь a и b будут ссылаться на один и тот же объект в String Pool, потому что JVM не создаёт дубликат — переиспользует существующий:

СТЕК                    КУЧА (String Pool)
a ──────┐
        ├──────────> "Java"    (один объект на двоих!)
b ──────┘

Но если создать строку через оператор new, JVM принудительно создаёт новый объект, минуя пул:

String c = new String("Java");   // новый отдельный объект, НЕ из пула
СТЕК                    КУЧА
a ──────┐
        ├──────────> "Java"    (String Pool)
b ──────┘

c ─────────────────> "Java"    (отдельный объект, вне пула)

6. Сравнение строк: == vs .equals() — критически важная тема

Это одна из самых частых ошибок начинающих Java-разработчиков, и её обязательно спросят на любом собеседовании.

Оператор == для ссылочных типов сравнивает НЕ содержимое, а адреса в памяти (то есть проверяет, ссылаются ли обе переменные на один и тот же физический объект).

String a = "Java";
String b = "Java";
String c = new String("Java");

System.out.println(a == b);          // true — оба из String Pool, один объект
System.out.println(a == c);          // false — c создан через new, другой объект!
System.out.println(a.equals(c));     // true — содержимое одинаковое!

Правило на всю жизнь: для сравнения СОДЕРЖИМОГО строк всегда используй .equals(), никогда ==.

String userInput = new String("password123");
String correctPassword = "password123";

if (userInput == correctPassword) {          // ❌ НЕПРАВИЛЬНО! Может дать false 
                                               // даже при одинаковом содержимом
    System.out.println("Доступ разрешён");
}

if (userInput.equals(correctPassword)) {     // ✅ ПРАВИЛЬНО!
    System.out.println("Доступ разрешён");
}

(Подробнее про equals() и внутреннюю механику String Pool — отдельный Урок 42, сегодня закладываем правильную привычку сразу.)

Почему это работает так, а не иначе? У примитивов (int a = 5; int b = 5;) оператор == сравнивает сами значения — это логично, ведь там нет никаких "объектов", только числа. У ссылочных типов == сравнивает ссылки (адреса), потому что технически это единственное, что физически хранится в переменной — сам объект лежит отдельно, в куче.

7. Основные методы класса String

String — это класс с десятками полезных методов. Разберём самые важные для старта:

МетодЧто делаетПример

.length()

Возвращает длину строки

"Hello".length()5

.charAt(int index)

Возвращает символ по индексу (нумерация с 0!)

"Hello".charAt(1)'e'

.substring(int begin)

Подстрока с индекса до конца

"Hello".substring(2)"llo"

.substring(int begin, int end)

Подстрока в диапазоне [begin, end)

"Hello".substring(1, 3)"el"

.toUpperCase()

Все буквы в верхний регистр

"hello".toUpperCase()"HELLO"

.toLowerCase()

Все буквы в нижний регистр

"HELLO".toLowerCase()"hello"

.trim()

Убирает пробелы по краям

" hi ".trim()"hi"

.replace(old, new)

Замена подстроки

"Java".replace("J", "L")"Lava"

.contains(String s)

Содержит ли подстроку

"Hello".contains("ell")true

.indexOf(String s)

Индекс первого вхождения (-1, если нет)

"Hello".indexOf("l")2

.isEmpty()

Пустая ли строка (длина 0)

"".isEmpty()true

.equals(Object o)

Сравнение содержимогокак показано выше

.equalsIgnoreCase(String s)

Сравнение без учёта регистра

"Java".equalsIgnoreCase("JAVA")true

.split(String regex)

Разбивает строку на массив по разделителю

"a,b,c".split(",")["a","b","c"]

⚠️ Важная деталь: поскольку строки неизменяемы, все методы, которые "модифицируют" строку, на самом деле возвращают НОВУЮ строку, не трогая оригинал:

String original = "hello";
String upper = original.toUpperCase();

System.out.println(original);   // "hello" — не изменился!
System.out.println(upper);      // "HELLO" — новая строка

Частая ошибка новичков:

String text = "hello";
text.toUpperCase();              // ❌ результат нигде не сохранён — бесполезная строка
System.out.println(text);        // выведет "hello", а не "HELLO"!

text = text.toUpperCase();       // ✅ правильно — переприсваиваем переменной
System.out.println(text);        // теперь "HELLO"

8. Конкатенация строк: оператор +

Мы уже видели это в предыдущих уроках, но формализуем правило:

String first = "Hello";
String second = "World";
String result = first + " " + second;   // "Hello World"

При конкатенации строки с числом или другим типом, Java автоматически вызывает toString()-представление значения:

int age = 25;
String message = "Возраст: " + age;   // "Возраст: 25" — число превращено в строку

Порядок вычисления слева направо (напоминание из Урока 2):

System.out.println(1 + 2 + "3");    // "33" — сначала 1+2=3 (числа), потом +"3" (конкатенация)
System.out.println("1" + 2 + 3);    // "123" — сначала "1"+2="12" (конкатенация), потом +3="123"

9. null — специальное значение ссылочных типов

Только ссылочные типы могут иметь значение null — это означает "переменная ни на что не ссылается" (пустая ссылка):

String name = null;   // допустимо для ссылочного типа
int age = null;       // ❌ ОШИБКА! примитивы не могут быть null

Попытка вызвать метод на null-ссылке вызовет runtime-ошибку NullPointerException (знаменитый NPE — головная боль каждого Java-разработчика):

String s = null;
System.out.println(s.length());   // 💥 NullPointerException во время выполнения!

⚠️ Это не ошибка компиляции — код скомпилируется без проблем, но упадёт при запуске. Всегда проверяй переменные на null перед использованием, если есть вероятность, что они не были инициализированы.


💻 ПРИМЕРЫ КОДА

Пример 1: Базовые операции со строками

public class Main {
    public static void main(String[] args) {
        String firstName = "Иван";              // 1. Строковый литерал → String Pool
        String lastName = "Петров";              // 2. Ещё один литерал
        String fullName = firstName + " " + lastName;  // 3. Конкатенация → новый объект

        System.out.println("Полное имя: " + fullName);  // 4. Вывод
        System.out.println("Длина имени: " + fullName.length());  // 5. Метод length()
    }
}

Вывод в консоли:

Полное имя: Иван Петров
Длина имени: 11

Разбор: fullName.length() считает все символы, включая пробел: "Иван Петров" = 4+1+6 = 11 символов.


Пример 2: Демонстрация String Pool и == vs .equals()

public class Main {
    public static void main(String[] args) {
        String a = "Java";                // из String Pool
        String b = "Java";                // переиспользует тот же объект из пула
        String c = new String("Java");    // принудительно новый объект в куче

        System.out.println("a == b: " + (a == b));               // сравнение ссылок
        System.out.println("a == c: " + (a == c));               // сравнение ссылок
        System.out.println("a.equals(c): " + a.equals(c));       // сравнение содержимого
    }
}

Вывод в консоли:

a == b: true
a == c: false
a.equals(c): true

Что происходит в памяти:

СТЕК                    КУЧА
a ──────┐
        ├───────────> "Java"     (String Pool — один объект для a и b)
b ──────┘

c ─────────────────> "Java"     (отдельный объект вне пула, создан через new)

⚠️ Типичная ошибка новичка: написать if (a == c) для сравнения содержимого строк — код скомпилируется и даже иногда "случайно сработает правильно" (если обе строки из пула), но в общем случае даст неверный результат. Всегда используй .equals().


Пример 3: Неизменяемость строк — частая ошибка

public class Main {
    public static void main(String[] args) {
        String text = "hello world";

        text.toUpperCase();               // ❌ результат никуда не сохранён!
        System.out.println(text);         // всё ещё "hello world"

        String upperText = text.toUpperCase();  // ✅ сохраняем результат
        System.out.println(upperText);          // "HELLO WORLD"
        System.out.println(text);               // исходная строка НЕ изменилась
    }
}

Вывод в консоли:

hello world
HELLO WORLD
hello world

Объяснение: toUpperCase() не может изменить существующий объект text (строки неизменяемы!), он создаёт и возвращает новый объект. Если не сохранить этот результат в переменную — он просто "потеряется" (станет мусором для сборщика мусора).


Пример 4: Работа с индексами и substring

public class Main {
    public static void main(String[] args) {
        String phrase = "Programming in Java";
        //               0123456789...

        System.out.println("Символ по индексу 0: " + phrase.charAt(0));
        System.out.println("Символ по индексу 12: " + phrase.charAt(12));
        System.out.println("Подстрока с 16 символа: " + phrase.substring(16));
        System.out.println("Подстрока [0, 11): " + phrase.substring(0, 11));
        System.out.println("Индекс слова 'Java': " + phrase.indexOf("Java"));
    }
}

Вывод в консоли:

Символ по индексу 0: P
Символ по индексу 12: n
Подстрока с 16 символа: Java
Подстрока [0, 11): Programming
Индекс слова 'Java': 16

⚠️ Важнейший подводный камень: индексация начинается с 0! Первый символ строки — это charAt(0), а не charAt(1). Также substring(begin, end) — конец не включается (полуоткрытый интервал), это классическая ошибка "на единицу" (off-by-one error).

Что будет, если выйти за границы?

System.out.println(phrase.charAt(100));  // 💥 StringIndexOutOfBoundsException — ошибка выполнения!

Пример 5: NullPointerException — типичная runtime-ошибка

public class Main {
    public static void main(String[] args) {
        String city = null;                  // допустимо для ссылочного типа

        System.out.println("Начало программы");
        System.out.println("Длина города: " + city.length());  // 💥 упадёт здесь!
        System.out.println("Это не выполнится");
    }
}

Вывод в консоли:

Начало программы
Exception in thread "main" java.lang.NullPointerException: 
Cannot invoke "String.length()" because "city" is null
    at Main.main(Main.java:6)

Как избежать: всегда проверяй на null перед вызовом методов, если есть сомнения:

if (city != null) {
    System.out.println("Длина города: " + city.length());
} else {
    System.out.println("Город не указан");
}

(Начиная с Java 14, сообщения об ошибках NullPointerException стали значительно информативнее — точно указывают, какая именно переменная была null — это называется Helpful NullPointerExceptions.)


⌨️ ГОРЯЧИЕ КЛАВИШИ INTELLIJ IDEA

ДействиеWindows/LinuxmacOSКогда использовать
Автодополнение методов после точки

Ctrl + Space

Ctrl + Space

Набрал text. — IDE покажет ВСЕ доступные методы String
Показать сигнатуру метода/параметры

Ctrl + P

Cmd + P

Внутри скобок метода — узнать, какие параметры ожидаются
Быстрая документация метода

Ctrl + Q

F1

Навести на .substring() — узнать точное поведение без выхода в браузер
Surround with (обернуть код)

Ctrl + Alt + T

Cmd + Alt + T

Быстро обернуть строку в if (x != null) { ... } для проверки на null
Найти использование переменной

Alt + F7

Alt + F7

Найти все места в коде, где используется конкретная строковая переменная
Live Template sout

Tab после sout

Tab после sout

Как в Уроке 1 — быстрый вывод
Live Template psvm

Tab после psvm

Tab после psvm

Быстрое создание main-метода

💡 Совет: сегодня активно пользуйся Ctrl+Space после точки — это лучший способ ЗАПОМНИТЬ методы String, а не зубрить таблицу выше. IDE сама подскажет все доступные варианты.


ПРАКТИЧЕСКИЕ ЗАДАНИЯ

Задание 1 (5 мин) — Первые строки

Создай переменную с твоим полным именем (в одну строку), выведи её длину и первую букву (через charAt(0)).


Задание 2 (10 мин) — String Pool в действии

Напиши код, аналогичный Примеру 2, но с другими строками. Поэкспериментируй: создай третью переменную через new String(...), сравни все три переменные между собой через == и .equals(). Выведи результаты и прокомментируй в коде, почему получились именно такие значения.


Задание 3 (10 мин) — Форматирование текста

Дана строка " Java Programming Language " (с лишними пробелами по краям). Напиши код, который:

  1. Убирает пробелы по краям (.trim())

  2. Переводит в нижний регистр

  3. Заменяет пробелы на нижнее подчёркивание _ (.replace(" ", "_"))

  4. Выводит результат каждого шага отдельно

Ожидаемый финальный результат: java_programming_language


Задание 4 (15 мин) — Валидатор email (упрощённый)

Напиши программу, которая проверяет строку email на два простых условия:

  1. Содержит символ @ (.contains("@"))

  2. Не пустая (!.isEmpty())

Выведи "Email корректен" или "Email некорректен" в зависимости от результата (используй if/else — если ещё не проходили детально, просто попробуй по аналогии с примерами, if разберём подробно в Уроке 6, но синтаксис интуитивно понятен).

String email = "test@example.com";
if (email.contains("@") && !email.isEmpty()) {
    System.out.println("Email корректен");
} else {
    System.out.println("Email некорректен");
}

Задание 5 (15 мин) — Работа с индексами

Дана строка "Hello, Java World!". Используя .substring(), извлеки и выведи отдельно:

  1. Слово "Hello"

  2. Слово "Java"

  3. Слово "World"

Посчитай индексы вручную или используй .indexOf() для автоматизации.


Задание 6 (15 мин) — Демонстрация неизменяемости

Напиши код, который явно показывает, что после вызова 5 разных методов (toUpperCase(), trim(), replace(), substring(), toLowerCase()) на одной и той же исходной переменной без переприсваивания — исходная строка остаётся неизменной. Выведи исходную строку после всех вызовов, чтобы доказать это.


Задание 7 (15 мин) — Ловушка NullPointerException

Специально воспроизведи NullPointerException (как в Примере 5), скопируй полный текст ошибки из консоли. Затем перепиши код с проверкой на null, чтобы программа не падала, а выводила сообщение об отсутствии данных.

Критерий проверки: первая версия кода падает с NPE, вторая версия работает без ошибок в обоих случаях (когда переменная null и когда содержит значение).


ПРОВЕРОЧНЫЕ ВОПРОСЫ

1. В чём принципиальная разница между примитивным и ссылочным типом данных?

ОтветПримитивный тип хранит значение напрямую в стеке. Ссылочный тип хранит в стеке только ссылку (адрес), а сам объект физически находится в куче (Heap).

2. Что выведет код?

String a = "test";
String b = "test";
System.out.println(a == b);

Ответ`true` — обе переменные ссылаются на один и тот же объект в String Pool, т.к. созданы через литералы с одинаковым содержимым.

3. Что выведет код?

String a = "test";
String b = new String("test");
System.out.println(a == b);
System.out.println(a.equals(b));

Ответ`false` (разные объекты в памяти — `b` создан принудительно через `new`, минуя пул), затем `true` (содержимое одинаковое, `.equals()` сравнивает именно содержимое).

4. Почему строки в Java называют неизменяемыми (immutable)?

ОтветПотому что после создания объект String никогда не может быть модифицирован. Любая "изменяющая" операция (toUpperCase, replace и т.д.) создаёт НОВЫЙ объект в памяти, а не меняет существующий.

5. Что выведет код?

String s = "hello";
s.toUpperCase();
System.out.println(s);

Ответ`hello` — результат `toUpperCase()` не сохранён ни в какую переменную, исходная строка `s` не изменилась (строки неизменяемы).

6. Какое правило нужно соблюдать при сравнении содержимого двух строк?

ОтветВсегда использовать метод `.equals()`, а не оператор `==`. `==` сравнивает ссылки (адреса в памяти), а не содержимое объектов.

7. Что такое String Pool?

ОтветСпециальная область памяти, где JVM хранит строковые литералы, переиспользуя одинаковые строки вместо создания дубликатов — это оптимизация памяти.

8. Может ли примитивная переменная (например, int) иметь значение null?

ОтветНет. `null` — допустимое значение только для ссылочных типов. Попытка присвоить `null` примитиву вызовет ошибку компиляции.

9. Что произойдёт при выполнении этого кода?

String s = null;
System.out.println(s.length());

ОтветПрограмма скомпилируется без ошибок, но упадёт во время выполнения с исключением `NullPointerException`, т.к. попытка вызвать метод на `null`-ссылке недопустима.

10. Что выведет код?

String phrase = "Hello";
System.out.println(phrase.charAt(0));

Ответ`H` — индексация в строках начинается с 0, поэтому `charAt(0)` возвращает первый символ.

11. Что выведет код?

String s = "Programming";
System.out.println(s.substring(0, 4));

Ответ`Prog` — метод `substring(begin, end)` возвращает подстроку от индекса `begin` до `end` НЕ включительно (полуоткрытый интервал).

12. Что выведет код?

System.out.println("5" + 3 + 2);
System.out.println(5 + 3 + "2");

ОтветПервая строка: `"532"` (всё сразу конкатенация строк слева направо). Вторая строка: `"82"` (сначала 5+3=8 как числа, потом конкатенация с "2" → "82").

13. Как проверить, содержит ли строка подстроку?

ОтветМетод `.contains(String s)`, например `"Hello World".contains("World")` вернёт `true`.

14. В каком случае строка создаётся ВНЕ String Pool, даже если написана с тем же содержимым, что уже есть в пуле?

ОтветЕсли строка создана через оператор `new String("...")`, JVM принудительно создаёт новый объект в куче, минуя проверку и переиспользование из пула.

15. Почему разработчики Java сделали строки неизменяемыми — назови минимум 2 причины.

Ответ1) Безопасность (защита от изменения критичных строк — паролей, путей и т.д.); 2) потокобезопасность в многопоточной среде; 3) возможность оптимизации через String Pool (переиспользование одинаковых строк).


ДОМАШНЕЕ ЗАДАНИЕ

Задача 1: "Анализатор текста"

Напиши программу TextAnalyzer.java, которая для заданной строки (например, "Java is a powerful programming language") выводит:

  1. Общую длину строки

  2. Строку в верхнем регистре

  3. Строку в нижнем регистре

  4. Количество вхождений подстроки "a" (можешь считать вручную по логике, или использовать .split() — подумай, как это сделать, используя .length() до и после .replace())

  5. Первое слово (используя .substring() и .indexOf(" "))

  6. Проверку: содержит ли строка слово "Java" (.contains())

Пример вывода:

Исходный текст: Java is a powerful programming language
Длина: 40
ВЕРХНИЙ РЕГИСТР: JAVA IS A POWERFUL PROGRAMMING LANGUAGE
нижний регистр: java is a powerful programming language
Первое слово: Java
Содержит 'Java': true

Задача 2: "Полное досье сравнения строк"

Напиши программу, которая создаёт 5 строковых переменных различными способами (через литералы, через new String(), через конкатенацию) и составляет полную "таблицу сравнения" — выводит результаты == и .equals() для всех значимых пар, с комментариями, объясняющими каждый результат.

Обязательное условие: минимум один случай должен демонстрировать, что конкатенация двух литералов на этапе компиляции (constant folding) может дать результат в пуле, а конкатенация через переменные — уже нет:

String a = "Hel" + "lo";           // константное выражение — попадёт в пул
String prefix = "Hel";
String b = prefix + "lo";          // вычисляется в runtime — НЕ попадёт в пул автоматически
System.out.println(a == "Hello");  // что здесь будет? Проверь и объясни!
System.out.println(b == "Hello");  // а здесь?

Критерии оценки: все сравнения объяснены текстовыми комментариями, продемонстрировано понимание разницы compile-time и runtime конкатенации.


Задача 3: "Безопасный обработчик пользовательских данных" (комплексная, объединяет с Уроками 1-2)

Напиши программу, моделирующую обработку "пользовательского ввода" (пока без реального Scanner — это будет в Уроке 7, сейчас просто задай переменные вручную):

String username = "  ivan_petrov  ";   // с лишними пробелами, как бывает при реальном вводе
String email = null;                    // представим, что email не был указан
int age = 25;

Программа должна:

  1. Очистить username от лишних пробелов

  2. Привести username к нижнему регистру

  3. Безопасно проверить email на null перед использованием — если null, вывести "Email не указан", иначе вывести его длину

  4. Вывести итоговое "досье" пользователя, объединив имя (обработанное), возраст и статус email, используя конкатенацию строк с числом

Пример вывода:

=== Досье пользователя ===
Username (обработан): ivan_petrov
Возраст: 25
Email не указан

Критерии оценки: нет NullPointerException при выполнении, корректная обработка строки (trim + lowercase), грамотное использование конкатенации, применение проверки на null.


📚 ДОПОЛНИТЕЛЬНЫЕ МАТЕРИАЛЫ

Официальная документация:

На что обратить особое внимание:

  • Разница == vs .equals()вопрос №1 на собеседованиях junior-уровня, обязательно закрепи это железно

  • Неизменяемость строк и её практические последствия (при интенсивной конкатенации в циклах — используй StringBuilder, урок 23)

  • NullPointerException — самое частое runtime-исключение в Java-разработке в принципе, привыкай сразу думать о null-безопасности

Частые ошибки новичков:

  1. Сравнение строк через == вместо .equals()

  2. Забывают, что методы String не изменяют исходную строку, а возвращают новую (не сохраняют результат в переменную)

  3. Путают одинарные и двойные кавычки ('H' — char, "H" — String из одного символа, это РАЗНЫЕ типы!)

  4. Забывают, что индексация строк начинается с 0, и получают off-by-one ошибки в substring

  5. Не проверяют переменные на null перед вызовом методов, получая NullPointerException в неожиданный момент

📎 Файлы для скачивания

📄 УРОК_3_Строки_String_в_Java._Ссылочные_типы_данных.html Скачать
МВ
Материал подготовил
Михаил Владимирович С.
Преподаватель физики и информатики
Страница преподавателя →