gadget59.ru практика
Баг-детектив · Java · 5 дел

Баг-детектив в Java

Вы — начинающий Java-разработчик в IT-компании. Вам поступило пять «дел» — реальных проблем, с которыми сталкиваются программисты. В каждом деле есть код с ошибкой или непонятное поведение. Ваша задача — диагностировать проблему и предложить решение.

Всё, что нужно для расследования, мы уже проходили: ООП, коллекции, Swing, события, биндинги, модули и работа с файлами.
Как работать:
  • Читайте описание дела — там код и симптомы.
  • Пишите свой диагноз и решение в поле для ответа.
  • Если застряли — нажмите «Подсказка».
  • В конце нажмите «Сохранить отчёт» — ответы скачаются в файл.
1 Дело № 1: «Список студентов теряет данные» Лёгкий

Коллега просит помочь: у него есть класс StudentGroup, который хранит студентов в ArrayList. Метод getStudents() возвращает этот список наружу. Приложение работает, но иногда студенты «исчезают» или появляются «лишние». Код:

public class StudentGroup { private final List<String> students = new ArrayList<>(); public List<String> getStudents() { return students; } public void add(String name) { students.add(name); } }

Ваша задача:

  1. Объясните, почему снаружи можно случайно испортить внутренний список.
  2. Предложите два способа это исправить.
Подсказка. Подумайте, что произойдёт, если снаружи вызвать group.getStudents().clear(). А если кто-то добавит «чужого» студента? Это нарушает инкапсуляцию. Два варианта решения:
1) Возвращать копию: return new ArrayList<>(students) — снаружи можно менять, но оригинал не тронется.
2) Возвращать неизменяемое представление: return Collections.unmodifiableList(students) — снаружи менять нельзя вовсе, но изменения в оригинале видны через обёртку.
Второй вариант строже, первый — удобнее, если клиенту нужно менять список локально.
2 Дело № 2: «Кнопка не реагирует на клик» Лёгкий

Разработчик сделал окно Swing с кнопкой «Сохранить». При нажатии ничего не происходит. Код:

JButton saveBtn = new JButton("Сохранить"); saveBtn.addActionListener(e -> { System.out.println("Кнопка нажата"); }); JPanel panel = new JPanel(); panel.add(saveBtn); JFrame frame = new JFrame("Тест"); frame.setSize(300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Ваша задача: найдите ошибку и объясните, что нужно добавить, чтобы окно появилось и кнопка работала.

Подсказка. Посмотрите, что происходит с panel и с frame. Панель не добавлена в окно, а окно не показано. Нужны строки frame.add(panel) и frame.setVisible(true).
3 Дело № 3: «Счётчик в игре сходит с ума» Средний

В игре «Поймай кота» счётчик очков увеличивается сразу на 2 при одном клике. Код:

JButton clickButton = new JButton("Клик"); // Слушатель №1 — ActionListener clickButton.addActionListener(e -> { score++; }); // Слушатель №2 — MouseListener на той же кнопке clickButton.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { score++; } });

Ваша задача: объясните, почему счётчик растёт дважды, и как это исправить.

Подсказка. На одну кнопку повешены два разных слушателя — ActionListener и MouseListener. При клике срабатывают оба: JButton генерирует ActionEvent (его ловит первый) и MouseEvent (его ловит второй). Значит, score++ выполняется дважды. Решение: оставить один слушатель — обычно ActionListener. События мыши на кнопке нужны редко (например, для правой кнопки или перетаскивания).
4 Дело № 4: «Биндинг не обновляет метку» Средний

В JavaFX-приложении разработчик связал метку с полем ввода, но при вводе текста метка не меняется. Код:

StringProperty name = new SimpleStringProperty("Иван"); Label nameLabel = new Label(); nameLabel.textProperty().bind(name); name.set("Мария"); // в консоли видно, что name = "Мария", а метка всё ещё "Иван" // Метка создана, но вот куда её добавили — вопрос VBox root = new VBox(); Scene scene = new Scene(root, 300, 200);

Ваша задача: объясните, почему метка не обновляется, и как это исправить.

Подсказка. Биндинг сам по себе работает. Проблема в том, что метка не добавлена в сцену — root.getChildren().add(nameLabel) нигде не вызывается. Биндинг есть, значение в модели меняется, но метки на экране нет. Ещё одна возможная причина в похожих ситуациях — изменение свойства не в FX Application Thread (например, из new Thread(...)): JavaFX не обновит UI из чужого потока.
5 Дело № 5: «Сборка падает с NoSuchMethodError» Сложный

Проект собирается, но при запуске падает с ошибкой:

Exception in thread "main" java.lang.NoSuchMethodError: com.example.utils.StringUtils.reverse(Ljava/lang/String;)Ljava/lang/String;

В коде вызывается StringUtils.reverse("hello"). Класс StringUtils лежит в отдельном JAR-файле utils-1.0.jar, который подключён к проекту. Разработчик уверен, что метод есть.

Ваша задача: объясните, почему возникает эта ошибка, и как её исправить.

Подсказка. NoSuchMethodError — ошибка во время выполнения, а не компиляции. Значит, компилятор видел метод, а JVM — нет. Это классическая проблема несовпадения версий: в проекте два JAR-файла с одинаковым классом, и загружается «старый», где метода нет. Решение: проверить classpath, убедиться, что используется одна версия JAR, пересобрать проект.
! Если что-то не получается важно

В реальной работе не бывает так, чтобы всё получалось с первого раза. Умение описать проблему — такой же навык, как умение писать код. Если вы застряли — не молчите и не бросайте задание.

Что делать, если решение не находится:
  • Опишите, что вы пробовали. «Я попробовал X, получил Y, ожидал Z» — это уже половина решения.
  • Опишите, где именно застряли. «Не понимаю, почему метка не обновляется» лучше, чем «не работает».
  • Запишите это в поле для ответа. Даже если финального решения нет — ваш ход мыслей ценнее, чем пустое поле.
  • Используйте подсказку. Она не даёт готовый ответ, но подталкивает к нему.
  • Обсудите с соседом или преподавателем. Иногда достаточно проговорить проблему вслух, чтобы увидеть решение.
Не списывайте. Если вы нашли решение в интернете или у соседа — напишите, как вы поняли, почему это работает. Иначе на следующем задании вы снова застрянете.

Помните: «не получилось, но я понял, где именно» — это нормальный результат практики. Хуже — когда непонятно, что именно не получилось.

★ Попробуйте решить другими способами дополнительно

Одно и то же можно решить по-разному. Чем больше способов вы попробуете — тем лучше поймёте тему. Вот идеи для каждого дела:

Дело Другие способы № 1 Можно вернуть List.copyOf(students) (неизменяемая копия, Java 10+). Или сделать поле final и вернуть только количество: getCount(). Или дать клиенту методы add/remove, а список не показывать вовсе. № 2 Можно добавить панель не в frame, а в frame.getContentPane(). Можно задать frame.setLocationRelativeTo(null) для центрирования. Можно сделать окно через JOptionPane.showMessageDialog() — без JFrame вообще. № 3 Можно оставить только MouseListener — если нужна реакция именно на мышь. Или добавить проверку e.getButton() == MouseEvent.BUTTON1, чтобы правая кнопка не считалась. Или разделить score++ на два счётчика — «клик кнопкой» и «клик мышью». № 4 Можно заменить bind() на bindBidirectional() — если метка должна ещё и влиять на модель. Можно добавить nameLabel.textProperty().addListener(...) для отладки: печатать в консоль, когда свойство меняется. № 5 Можно проверить classpath командой java -cp ... MyApp с явным указанием JAR. Можно собрать проект через javac -d out src/**/*.java и запустить через java -cp out:lib/utils-2.0.jar MyApp. Можно использовать jdeps — утилиту JDK для анализа зависимостей.
Бонус: если найдёте ещё один способ решения любого дела — напишите его в итоговом отчёте. Это засчитывается как дополнительная работа.

Итоговый отчёт

Вы прошли все пять дел. Теперь составьте краткий итог: какие ошибки вы нашли, какие приёмы использовали и что нового узнали.

Заполнено 0 из 6 полей