Java за 30 дней: Интенсивный курс с практикой

Этот курс разбит на 5 логических этапов, позволяющих освоить язык Java с нуля за месяц. Программа включает изучение синтаксиса, принципов ООП, коллекций и создание итогового проекта.

1. Неделя 1: Основы синтаксиса, переменные, типы данных и управляющие конструкции

Неделя 1: Основы синтаксиса, переменные, типы данных и управляющие конструкции

Добро пожаловать на курс «Java за 30 дней». Вы сделали отличный выбор. Java — это не просто язык программирования, это фундамент корпоративной разработки, мобильных приложений (Android) и серверных решений. Это язык со строгой статической типизацией, что означает, что он не позволит вам совершить многие глупые ошибки, которые возможны в других языках.

Наша цель на эту неделю — перестать бояться кода, понять, как «думает» Java, и написать свои первые работающие программы. Мы разберем анатомию приложения, узнаем, где хранятся данные и как управлять ходом выполнения программы.

Анатомия Java-программы

Любая программа на Java состоит из классов. Представьте, что класс — это чертеж или инструкция. Внутри классов живут методы (функции), которые содержат набор команд.

Самый главный метод, с которого начинается выполнение любой программы — это метод main.

Посмотрим на классический пример:

Разберем каждую строчку:

  • public class FirstApp — мы объявляем класс с именем FirstApp. В Java имя файла обязано совпадать с именем публичного класса.
  • public static void main(String[] args) — это точка входа. Без этого метода программа просто не запустится.
  • System.out.println(...) — команда вывода текста в консоль.
  • !На схеме изображен большой прямоугольник с подписью "Файл FirstApp.java". Внутри него прямоугольник поменьше "class FirstApp". Внутри класса находится блок "method main", а внутри метода — конкретные инструкции (команды). Стрелки показывают вложенность.

    Переменные и Типы данных

    Программа должна обрабатывать данные. Чтобы сохранить данные в памяти, мы используем переменные. Переменная — это именованная область памяти, «коробка», в которую мы кладем значение.

    Так как Java — язык со строгой типизацией, вы не можете положить слона в коробку из-под спичек. Вы должны заранее сказать, какой тип данных будет храниться в переменной.

    Примитивные типы данных

    В Java существует 8 примитивных типов данных. Они хранят простые значения и занимают фиксированное место в памяти. Мы сосредоточимся на самых используемых:

    | Тип | Что хранит | Пример | | :--- | :--- | :--- | | int | Целые числа | int age = 25; | | double | Дробные числа | double price = 19.99; | | boolean| Истина или ложь | boolean isJavaFun = true; | | char | Один символ | char grade = 'A'; |

    Остальные (byte, short, long, float) используются реже, в специфических ситуациях.

    Ссылочные типы данных

    Самый популярный ссылочный тип — это String (строка). Строка — это не примитив, это полноценный объект.

    > Важно: Примитивы пишутся с маленькой буквы (int), а ссылочные типы (классы) — с большой (String).

    Математические операции

    Программирование неразрывно связано с математикой. В Java доступны стандартные операторы: +, -, *, / (деление), % (остаток от деления).

    Рассмотрим пример вычисления индекса массы тела (ИМТ). Формула выглядит так:

    Где — индекс массы тела, — масса тела в килограммах, — рост в метрах.

    В коде это будет выглядеть так:

    Управляющие конструкции

    Код не всегда выполняется линейно сверху вниз. Иногда нам нужно принимать решения или повторять действия.

    Условный оператор if-else

    Этот оператор позволяет программе «думать». Если условие истинно — делаем одно, иначе — другое.

    !Схема начинается с ромба, в котором написано "Условие истинно?". От ромба идут две стрелки: "Да" и "Нет". Стрелка "Да" ведет к прямоугольнику "Выполнить блок if", стрелка "Нет" ведет к прямоугольнику "Выполнить блок else". Затем обе линии соединяются и идут вниз к продолжению программы.

    Логические операторы

    Часто нужно проверить несколько условий сразу. Для этого используются логические операторы:

    * && (И) — истина, только если оба условия верны. * || (ИЛИ) — истина, если хотя бы одно условие верно. * ! (НЕ) — меняет значение на противоположное.

    Пример логики:

    Где — результат (true/false), и — переменные, — логическое «И» (в Java это &&). Если и , то результат будет true.

    Циклы (Loops)

    Циклы позволяют выполнять блок кода многократно. Это спасает нас от дублирования кода.

    #### Цикл while

    Выполняется, пока условие истинно.

    #### Цикл for

    Самый популярный цикл. Идеален, когда мы знаем, сколько раз нужно повторить действие.

    Синтаксис for состоит из трех частей:

  • Инициализация (int i = 0): выполняется один раз перед началом.
  • Условие (i < 5): проверяется перед каждой итерацией.
  • Обновление (i++): выполняется после каждой итерации.
  • Практическое задание недели

    Чтобы закрепить материал, попробуйте написать простую программу «Калькулятор скидок» в вашей IDE.

  • Создайте переменную price (цена товара).
  • Создайте переменную isRegularCustomer (постоянный клиент, тип boolean).
  • Если клиент постоянный, дайте скидку 10%.
  • Если цена выше 1000, дайте дополнительную скидку 5%.
  • Выведите итоговую цену.
  • Заключение

    На этой неделе мы заложили фундамент. Вы узнали, что такое переменные, как Java работает с типами данных и как управлять потоком выполнения программы. Это база, без которой невозможно написать даже простейшее приложение.

    На следующей неделе мы углубимся в объектно-ориентированное программирование (ООП) — сердце Java. Мы узнаем, что такое объекты, наследование и полиморфизм.

    Готовы проверить свои знания? Переходите к заданиям ниже.

    2. Неделя 2: Объектно-ориентированное программирование: классы, наследование, полиморфизм и инкапсуляция

    Неделя 2: Объектно-ориентированное программирование: классы, наследование, полиморфизм и инкапсуляция

    Поздравляю с успешным завершением первой недели! Вы уже умеете объявлять переменные, писать циклы и принимать решения с помощью if-else. Но пока ваш код напоминал список покупок: просто последовательность действий. В реальной разработке, особенно в Enterprise-секторе (банковские системы, маркетплейсы), такой подход быстро превращается в хаос.

    На этой неделе мы переходим к сердцу Java — Объектно-Ориентированному Программированию (ООП). Это не просто набор правил, это способ мышления. Мы научимся моделировать реальный мир внутри компьютера.

    Классы и Объекты: Чертеж и Дом

    В процедурном программировании (то, что мы делали на первой неделе) данные и методы живут отдельно. В ООП они объединяются в сущности.

    Что такое класс?

    Класс — это шаблон, чертеж или описание того, каким должен быть объект. Сам по себе класс не занимает места в оперативной памяти (кроме метаданных), пока мы не создадим объект этого класса.

    Что такое объект?

    Объект (или экземпляр класса) — это конкретная реализация этого чертежа. По одному чертежу можно построить тысячи домов. По одному классу можно создать тысячи объектов.

    !Визуализация различия между абстрактным описанием (классом) и конкретными экземплярами (объектами).

    Рассмотрим пример. Допустим, мы создаем игру с машинами.

    Теперь создадим объекты в методе main:

    Обратите внимание на ключевое слово new. Именно оно выделяет память под новый объект.

    Инкапсуляция: Защита данных

    Представьте, что в нашем классе Car кто-то напишет: myCar.speed = -200;. С точки зрения синтаксиса это верно (int может быть отрицательным), но с точки зрения логики — абсурд. Машина не может ехать с отрицательной скоростью.

    Инкапсуляция — это механизм скрытия данных и предоставления доступа к ним только через публичные методы. Это защита от «дурака» и неправильного использования вашего кода.

    В Java для этого используются модификаторы доступа: * private: доступ только внутри этого же класса. * public: доступ из любой части программы.

    Перепишем наш класс правильно:

    Теперь, если кто-то попытается установить -200, сработает наша проверка.

    Наследование: Не повторяй себя

    Программисты ленивы, и это хорошо. Если у нас есть классы Dog (Собака), Cat (Кошка) и Cow (Корова), у них у всех есть общие черты: имя, возраст, умение есть и спать. Писать один и тот же код в трех файлах — плохая практика.

    Наследование позволяет создать родительский класс (например, Animal) и унаследовать от него свойства в дочерних классах. Используется ключевое слово extends.

    Теперь объект Dog умеет и гавкать (свой метод), и кушать (метод родителя).

    !Схема наследования, показывающая передачу общих свойств от родителя к потомкам.

    Полиморфизм: Один интерфейс, много форм

    Это самое сложное для понимания, но самое мощное понятие. Полиморфизм позволяет работать с объектами разных типов так, будто это один и тот же тип.

    Допустим, у всех животных есть метод makeSound() (издать звук). Но собака лает, а кошка мяукает.

    Мы можем переопределить (Override) метод родителя в дочерних классах.

    Теперь магия полиморфизма:

    Результат:

    Программа сама определяет, какой именно метод вызвать, в зависимости от того, какой объект был создан (new Dog() или new Cat()).

    Пример с математикой: Площадь фигур

    Полиморфизм часто используется в геометрических задачах. Допустим, нам нужно вычислить площадь круга. Вспомним формулу из геометрии:

    Где — это площадь круга, — математическая константа (приблизительно равна 3.14159), а — радиус круга.

    В коде мы можем создать общий класс Shape (Фигура) и наследника Circle (Круг):

    Конструкторы

    Вы могли заметить метод Circle(double radius) в примере выше. Это конструктор. Это специальный метод, который вызывается только один раз — в момент создания объекта (при использовании new).

    Правила конструктора:

  • Имя совпадает с именем класса.
  • У него нет возвращаемого типа (даже void).
  • Используется для начальной настройки объекта.
  • Ключевое слово static

    Иногда нам нужны методы или переменные, которые принадлежат не конкретному объекту (конкретной машине), а всему классу в целом (заводу).

    Пример: счетчик созданных машин.

    К статическим переменным обращаются через имя класса: Car.carCount, а не через объект.

    Заключение

    Сегодня вы сделали огромный шаг. Вы перешли от написания скриптов к проектированию архитектуры.

    * Класс — это чертеж. * Объект — это реализация. * Инкапсуляция защищает данные (private). * Наследование (extends) позволяет переиспользовать код. * Полиморфизм позволяет работать с разными объектами одинаково.

    На следующей неделе мы разберем коллекции (как хранить много объектов удобно) и исключения (как обрабатывать ошибки, чтобы программа не падала).

    А пока — выполните задания ниже, чтобы закрепить материал.

    3. Неделя 3: Обработка исключений, работа со строками и фреймворк коллекций

    Неделя 3: Обработка исключений, работа со строками и фреймворк коллекций

    Добро пожаловать на третью неделю курса «Java за 30 дней»! Вы уже проделали огромную работу: освоили синтаксис, переменные, циклы и погрузились в мир Объектно-Ориентированного Программирования (ООП). Ваши программы уже имеют структуру, классы и объекты.

    Но есть проблема. Ваши программы пока очень «хрупкие». Что произойдет, если пользователь введет буквы вместо цифр? Или если файл, который программа пытается открыть, не существует? Скорее всего, приложение просто «упадет» с красным текстом ошибки в консоли. В реальном мире (Enterprise-разработка) это недопустимо.

    На этой неделе мы сделаем ваш код надежным, гибким и эффективным. Мы разберем три кита профессиональной разработки:

  • Исключения (Exceptions): как грамотно обрабатывать ошибки.
  • Строки (Strings): как эффективно работать с текстом.
  • Коллекции (Collections): где хранить данные, когда массивов недостаточно.
  • Обработка исключений: Право на ошибку

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

    Когда происходит ошибка, Java «выбрасывает» (throws) исключение. Если его не поймать, программа аварийно завершится.

    Блок try-catch

    Чтобы программа не падала, мы используем конструкцию try-catch (попытаться — поймать). Мы «оборачиваем» опасный код в блок try, а в блоке catch пишем инструкцию, что делать в случае аварии.

    Рассмотрим классический пример деления на ноль:

    Если бы мы не использовали try-catch, программа остановилась бы на строке int result = a / b, и последняя фраза никогда бы не напечаталась.

    !Визуализация того, как поток управления переходит в блок catch при возникновении ошибки.

    Блок finally

    Иногда нужно выполнить код независимо от того, произошла ошибка или нет (например, закрыть соединение с базой данных или файлом). Для этого существует блок finally.

    Работа со строками: String и его секреты

    Вы уже использовали тип String. Кажется, что это просто текст. Но в Java строки — это особые объекты. Главное, что нужно запомнить: строки в Java неизменяемы (immutable).

    Это значит, что когда вы пишете:

    Java не изменяет старую строку «Привет». Она создает в памяти совершенно новый объект «Привет, Мир», а старый объект «Привет» остается висеть в памяти, пока его не удалит сборщик мусора. Если вы делаете тысячи таких операций в цикле, это может «убить» производительность.

    StringBuilder

    Для частых изменений текста используйте класс StringBuilder. Он работает как изменяемый буфер.

    Сравнение строк

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

    Фреймворк Коллекций (Collections Framework)

    На прошлой неделе мы создавали массив животных: Animal[] animals = new Animal[2];. У массивов есть фатальный недостаток: фиксированный размер. Вы должны знать заранее, сколько элементов вам нужно. Но что, если мы пишем список покупок и не знаем, сколько товаров добавит пользователь?

    Здесь на помощь приходят коллекции. Это «умные» контейнеры для данных, которые умеют динамически расширяться.

    1. List (Список)

    Самая популярная коллекция — ArrayList. Это «резиновый» массив. Он хранит элементы в порядке добавления и позволяет дубликаты.

    Обратите внимание на <String>. Это Generics (обобщения). Мы указываем Java, что в этом списке будут лежать только строки.

    2. Set (Множество)

    Если вам нужен список уникальных элементов (где повторы запрещены), используйте HashSet. Порядок элементов здесь не гарантируется.

    3. Map (Словарь или Карта)

    Это самая интересная структура. Она хранит данные парами: Ключ -> Значение. Как в реальном словаре: слово (ключ) -> перевод (значение). Ключи должны быть уникальны.

    Самая популярная реализация — HashMap.

    #### Как работает HashMap внутри? (Немного математики)

    Чтобы HashMap работала быстро (находила значение по ключу почти мгновенно), она использует вычисление хеш-кода. Когда вы кладете пару в карту, Java вычисляет индекс ячейки (bucket), куда положить данные, по формуле:

    Где: * — итоговый номер ячейки в массиве, где будет храниться запись. * — хеш-код ключа (целое число, генерируемое методом hashCode()). * — текущий размер внутреннего массива (емкость карты). * — операция взятия остатка от деления.

    Благодаря этой формуле, Java не перебирает весь список, чтобы найти «Алекса», а сразу вычисляет математически, в какой ячейке он лежит. Это делает поиск молниеносным.

    !Иллюстрация того, как ключ преобразуется в индекс массива для быстрого доступа.

    Итерация по коллекциям

    Как пройтись по всем элементам коллекции? Используйте цикл for-each, который мы изучили ранее. Он отлично работает с коллекциями.

    Заключение

    Сегодня вы получили мощнейшие инструменты. * Исключения делают ваш код устойчивым к сбоям. * StringBuilder экономит память при работе с текстом. * Коллекции (List, Set, Map) позволяют удобно хранить любые объемы данных.

    Теперь вы можете написать не просто калькулятор, а, например, записную книжку, систему учета студентов или простой чат-бот. Это уже уровень Junior-разработчика.

    На следующей неделе мы коснемся продвинутых тем: работы с файлами (ввод-вывод) и основ многопоточности. А пока — закрепите знания на практике!

    4. Дни 22-26: Продвинутые темы: потоки ввода-вывода (I/O) и введение в Stream API

    Дни 22-26: Продвинутые темы: потоки ввода-вывода (I/O) и введение в Stream API

    Приветствую вас, студенты! Мы выходим на финишную прямую нашего курса «Java за 30 дней». Вы уже научились моделировать объекты, обрабатывать ошибки и хранить данные в коллекциях. Но до сих пор ваши программы жили в «вакууме»: данные исчезали сразу после завершения работы программы, и вы не могли загрузить информацию извне.

    В ближайшие несколько дней мы разрушим эту стену. Мы научимся работать с файловой системой, читать и записывать данные (I/O), а также познакомимся с одним из самых мощных инструментов современной Java — Stream API, который превращает обработку данных в элегантный конвейер.

    Часть 1: Потоки ввода-вывода (Java I/O)

    В Java любое чтение или запись данных (будь то файл, сетевое соединение или консоль) происходит через потоки (Streams). Не путайте их с Stream API, о котором мы поговорим во второй части лекции. Здесь «поток» — это последовательность данных, текущая от источника к приемнику.

    !Визуализация потока данных как трубы, по которой байты перемещаются от источника в программу.

    Байтовые и Символьные потоки

    Пакет java.io делит все потоки на две большие группы:

  • Байтовые потоки (Byte Streams): Оперируют «сырыми» байтами. Используются для картинок, аудио, видео и любых бинарных данных. Базовые классы: InputStream и OutputStream.
  • Символьные потоки (Character Streams): Оперируют символами (char). Они «знают» о кодировках и идеально подходят для текстовых файлов. Базовые классы: Reader и Writer.
  • > Золотое правило: Если вы работаете с текстом — используйте Reader/Writer. Если с чем-то другим — InputStream/OutputStream.

    Чтение и запись файлов

    Давайте попробуем записать текст в файл. Для этого используем класс FileWriter.

    Обратите внимание на конструкцию try (...). Это try-with-resources, введенный в Java 7. Он автоматически закрывает поток после завершения работы, даже если произошла ошибка. Раньше нам приходилось писать блок finally и вызывать метод close() вручную, что часто приводило к утечкам памяти.

    Буферизация

    Чтение данных по одному байту или символу с жесткого диска — это очень медленно. Диск — механическое (или сложное электронное) устройство, и каждое обращение к нему стоит дорого по времени.

    Чтобы ускорить процесс, используют буферизацию. Мы считываем с диска сразу большой кусок данных в оперативную память (буфер), и программа берет данные уже оттуда.

    Для этого мы «оборачиваем» наши потоки в BufferedReader или BufferedWriter.

    Часть 2: Введение в Stream API (Java 8+)

    Теперь перейдем к теме, которая изменила стиль написания кода на Java навсегда. Stream API (пакет java.util.stream) — это инструмент для обработки коллекций данных в функциональном стиле.

    Представьте, что у вас есть список из 1000 товаров, и вам нужно найти все товары дороже 5000 рублей, отсортировать их по названию и получить список их имен. В старом стиле (императивном) вы бы написали огромный цикл for с кучей условий if. В новом стиле (декларативном) вы просто описываете, что хотите получить.

    Конвейер операций

    Работа со стримом похожа на заводской конвейер:

  • Источник (Source): Коллекция, массив или файл.
  • Промежуточные операции (Intermediate): Фильтрация, преобразование, сортировка. Они ленивы (lazy) — ничего не происходит, пока не будет вызвана терминальная операция.
  • Терминальная операция (Terminal): Получение результата (сбор в список, сумма, вывод на экран).
  • !Иллюстрация принципа работы Stream API как конвейера обработки данных.

    Основные методы Stream API

    Рассмотрим пример. У нас есть список имен: ["Анна", "Иван", "Алексей", "Ольга", "Александр"]. Мы хотим оставить только имена на букву «А», перевести их в верхний регистр и отсортировать.

    Разберем ключевые методы:

    * filter(Predicate): Пропускает дальше только те элементы, которые удовлетворяют условию. * map(Function): Преобразует каждый элемент в нечто другое (например, строку в число или объект в одно из его полей). * sorted(): Сортирует элементы. * collect(): Превращает стрим обратно в коллекцию.

    Математика в Stream API

    Stream API отлично подходит для вычислений. Допустим, нам нужно найти сумму квадратов всех четных чисел в списке. Математически это выглядит так:

    Где: * — искомая сумма. * — знак суммирования. * — условие, что число принадлежит множеству четных чисел списка. * — операция возведения числа в квадрат.

    В коде это реализуется буквально в одну строку:

    Лямбда-выражения

    Вы заметили странный синтаксис n -> n n? Это лямбда-выражения. Это короткий способ записи анонимных функций. Читается как: «берем n и возвращаем n n».

    Связь I/O и Stream API

    Эти две темы прекрасно работают вместе. Например, класс Files позволяет читать строки из файла сразу в виде стрима.

    Этот код откроет файл, будет читать его лениво (строка за строкой, не загружая весь файл в память) и выведет только те строки, где есть слово "Java". Это очень эффективно для обработки огромных лог-файлов.

    Заключение

    За эти дни вы получили инструменты профессионального уровня. java.io позволяет вашей программе общаться с внешним миром, а Stream API дает возможность обрабатывать данные лаконично и эффективно.

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

    Практикуйтесь, экспериментируйте с файлами и стримами. Удачи!

    5. Дни 27-30: Разработка финального консольного приложения и разбор частых ошибок

    Дни 27-30: Разработка финального консольного приложения и разбор частых ошибок

    Поздравляю! Вы добрались до финала. За прошедшие 26 дней вы прошли путь от «Hello World» до понимания потоков ввода-вывода и Stream API. Теперь пришло время собрать все знания воедино и создать что-то работающее, полезное и архитектурно верное.

    В эти последние дни мы напишем Консольный Менеджер Задач (Task Manager). Это классический проект для начинающих, который позволяет отработать навыки работы с ООП, коллекциями, файлами и обработкой исключений.

    Архитектура приложения

    Прежде чем писать код, нужно спроектировать структуру. Новички часто пишут весь код в одном классе Main. Это ошибка. Мы разделим приложение на три логические части:

  • Модель данных (Model): Класс, описывающий саму задачу.
  • Логика (Service/Controller): Класс, управляющий списком задач (добавление, удаление, сохранение).
  • Интерфейс (View): Класс, отвечающий за общение с пользователем через консоль.
  • !Архитектура нашего приложения: разделение на данные, управление и интерфейс.

    Шаг 1: Создаем модель (Task.java)

    Наша задача — это объект. У нее должны быть свойства: уникальный номер (id), описание и статус (выполнена или нет).

    Мы добавили implements Serializable, чтобы Java могла легко превратить этот объект в поток байтов для сохранения в файл.

    Шаг 2: Пишем логику (TaskManager.java)

    Этот класс будет хранить список задач и выполнять операции над ними. Здесь мы применим коллекции и Stream API.

    Математическая логика фильтрации

    В методе getActiveTasks мы использовали фильтр. С точки зрения математической логики, мы формируем подмножество из множества всех задач :

    Где: * — множество активных (невыполненных) задач. * — множество всех существующих задач в списке. * — конкретная задача (элемент множества). * — разделитель, читается как «таких, что». * — логическое отрицание свойства «выполнена» (то есть задача не выполнена).

    Шаг 3: Интерфейс пользователя (Main.java)

    Теперь создадим меню. Мы будем использовать бесконечный цикл while(true), который прерывается только по команде пользователя.

    !Цикл работы консольного меню: программа ждет команды, выполняет ее и снова ждет.

    Топ-5 ошибок новичков (Anti-patterns)

    Теперь, когда код написан, давайте разберем ошибки, которые часто допускают Junior-разработчики.

    1. Проглатывание исключений (Swallowing Exceptions)

    Плохо:

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

    2. Злоупотребление static

    Новички любят делать все методы и поля static, чтобы вызывать их из main без создания объекта. Это убивает ООП. Статика нужна только для утилитных методов (как Math.pow) или констант. Данные должны принадлежать объектам.

    3. Магические числа

    Плохо: if (status == 1) ... Что такое 1? Через неделю вы забудете. Используйте константы или Enum. Хорошо: if (status == Status.ACTIVE) ...

    4. NullPointerException (NPE)

    Это самая частая ошибка. Она возникает, когда вы пытаетесь вызвать метод у переменной, которая равна null.

    Где: * — ссылка на объект. * — попытка обращения. * — пустое значение (null). * — логическое следствие.

    Всегда проверяйте объекты на null перед использованием или используйте Optional.

    5. Нарушение конвенций именования

    В Java принят CamelCase. * Классы: MyTask (с большой буквы). * Методы и переменные: calculateSum, userName (с маленькой). * Константы: MAX_VALUE (верхний регистр).

    Что учить дальше?

    Этот курс — только начало. Чтобы стать профессионалом, вам предстоит изучить:

  • Системы сборки: Maven или Gradle (чтобы не подключать библиотеки вручную).
  • Базы данных: SQL, JDBC, Hibernate (хранить данные в файлах — это несерьезно).
  • Spring Framework: Стандарт де-факто в мире Java Enterprise.
  • Git: Система контроля версий для командной работы.
  • Спасибо, что были с нами эти 30 дней. Практикуйтесь, пишите код каждый день, и у вас все получится!

    > «Программирование — это не о том, что вы знаете. Это о том, что вы можете выяснить». — Крис Пайн

    Удачи в ваших проектах!