Перейти к содержанию
Educora
Продвинутый22 мин12 / 18

Современная Java и проект: журнал успеваемости

Познакомься с record, var, лямбдами и потоками, а затем объедини всё изученное в курсе и создай журнал успеваемости.

Проверь себя
В этом уроке ты узнаешь
  • Описывать простой класс данных через record и уместно использовать var
  • Писать лямбда-выражения и ссылки на методы
  • Обрабатывать коллекции потоками (filter, map, sorted) и объединить всё в проекте

Java уже больше 30 лет, но она не устарела: за последние годы в язык добавили возможности, которые заметно сокращают код. Класс, занимавший 40 строк, превращается в однострочный record, а пятистрочный цикл — в однострочный поток. В этом уроке ты познакомишься с этими возможностями, а затем объединишь всё, что изучил в курсе, в небольшой, но настоящий проект — журнал успеваемости.

record и var

Record (запись, с Java 16) — краткая форма класса, который хранит неизменяемые данные. Если написать record Point(int x, int y), Java сама создаст private final-поля, конструктор, методы x() и y(), а также equals, hashCode и toString. В запись можно добавлять и свои методы. А var позволяет компилятору самому определить тип локальной переменной.

Java
public class Main {
    public static void main(String[] args) {
        Point a = new Point(3, 4);
        Point b = new Point(3, 4);
        System.out.println(a);
        System.out.println(a.x() + a.y());
        System.out.println(a.equals(b) + " " + (a == b));
        System.out.println(a.distanceToOrigin());
        var message = "Records save time";
        var count = message.length();
        System.out.println(message + " (" + count + " chars)");
    }
}

record Point(int x, int y) {
    double distanceToOrigin() {
        return Math.sqrt(x * x + y * y);
    }
}
Ожидаемый результат
Point[x=3, y=4]
7
true false
5.0
Records save time (17 chars)

a.equals(b) дал true: записи сравниваются по содержимому. А a == b — false, потому что это два разных объекта. Обрати внимание: в именах геттеров нет get — не getX(), а просто x().

Лямбды и ссылки на методы

Лямбда-выражение — короткая функция без имени: n -> n * n означает «возьми n, верни n * n». Лямбды используются вместо функциональных интерфейсов — интерфейсов с единственным абстрактным методом: Predicate (проверяет условие), Function (преобразует значение), Comparator (сравнивает два объекта). Если лямбда только вызывает готовый метод, её можно записать ещё короче — ссылкой на метод: System.out::println.

Java
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Function<Integer, Integer> square = n -> n * n;
        System.out.println(isEven.test(4) + " " + square.apply(7));

        List<String> names = new ArrayList<>(List.of("Murad", "Aysel", "Elvin", "Leyla"));
        names.sort((a, b) -> a.compareTo(b));
        names.removeIf(name -> name.startsWith("E"));
        names.forEach(name -> System.out.print(name + " "));
        System.out.println();
        names.forEach(System.out::println);
    }
}
Ожидаемый результат
true 49
Aysel Leyla Murad 
Aysel
Leyla
Murad

Потоки (Stream API)

Поток (stream) обрабатывает элементы коллекции как конвейер: filter оставляет подходящие под условие, map преобразует каждый элемент, sorted сортирует, а toList, count, sum или average собирают результат. Поток не меняет исходную коллекцию. Код отвечает на вопрос «что сделать», а не «как», поэтому легко читается.

Java
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Integer> scores = List.of(78, 95, 62, 88, 45, 91);
        List<Integer> passed = scores.stream()
                .filter(s -> s >= 60)
                .sorted()
                .toList();
        System.out.println(passed);
        int total = scores.stream().mapToInt(Integer::intValue).sum();
        double avg = scores.stream().mapToInt(Integer::intValue).average().orElse(0);
        System.out.println(total + " " + avg);
        String initials = List.of("Baku", "Ganja", "Shaki").stream()
                .map(c -> c.substring(0, 1))
                .collect(Collectors.joining("-"));
        System.out.println(initials);
        long count = scores.stream().filter(s -> s > 80).count();
        System.out.println(count);
    }
}
Ожидаемый результат
[62, 78, 88, 91, 95]
459 76.5
B-G-S
3

Проект: журнал успеваемости

  1. 1
    Опиши данные

    Ученик — record с именем и списком баллов. Его компактный конструктор отклоняет баллы вне диапазона 0–100 с помощью IllegalArgumentException.

  2. 2
    Добавь вычисления

    average() находит средний балл с помощью потока и округляет его до одного знака, а letter() выставляет буквенную оценку switch-выражением.

  3. 3
    Напиши класс журнала

    GradeBook хранит учеников в private ArrayList, печатает отсортированный отчёт и считает средний балл класса.

  4. 4
    Собери всё в main

    Добавь учеников, перехвати неверные данные через try/catch и выведи отчёт.

Java
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        GradeBook book = new GradeBook();
        book.add(new Student("Aysel", List.of(92, 88, 95)));
        book.add(new Student("Murad", List.of(85, 78, 84)));
        book.add(new Student("Leyla", List.of(58, 49, 61)));
        book.add(new Student("Nigar", List.of(70, 65, 72)));
        try {
            book.add(new Student("Elvin", List.of(90, 120)));
        } catch (IllegalArgumentException e) {
            System.out.println("Skipped: " + e.getMessage());
        }
        book.printReport();
        System.out.println("Class average: " + book.classAverage());
    }
}

record Student(String name, List<Integer> scores) {
    Student {
        for (int s : scores) {
            if (s < 0 || s > 100) throw new IllegalArgumentException(name + " has score " + s);
        }
    }

    double average() {
        double avg = scores.stream().mapToInt(Integer::intValue).average().orElse(0);
        return Math.round(avg * 10) / 10.0;
    }

    String letter() {
        return switch ((int) average() / 10) {
            case 10, 9 -> "A";
            case 8 -> "B";
            case 7 -> "C";
            case 6 -> "D";
            default -> "F";
        };
    }
}

class GradeBook {
    private final List<Student> students = new ArrayList<>();

    void add(Student s) { students.add(s); }

    double classAverage() {
        double avg = students.stream().mapToDouble(Student::average).average().orElse(0);
        return Math.round(avg * 10) / 10.0;
    }

    void printReport() {
        students.stream()
                .sorted(Comparator.comparingDouble(Student::average).reversed())
                .forEach(s -> System.out.println(s.name() + ": " + s.average() + " (" + s.letter() + ")"));
    }
}
Ожидаемый результат
Skipped: Elvin has score 120
Aysel: 91.7 (A)
Murad: 82.3 (B)
Nigar: 69.0 (D)
Leyla: 56.0 (F)
Class average: 74.8
Весь проект в одном файле — Main.java
В проектеИз какой темы
record Student, Student { ... }записи, конструкторы
throw new IllegalArgumentException, try/catchисключения
switch ((int) average() / 10)switch-выражения, приведение типов
private final List<Student>инкапсуляция, коллекции, дженерики
stream(), Student::average, s -> ...потоки, ссылки на методы, лямбды

Student { ... } — компактный конструктор записи: он позволяет проверить данные, не повторяя параметры, а поля заполняются автоматически после него. Балл 120 у Эльвина не принят, и он не попал в журнал, но программа продолжила работу — как в уроке об исключениях.

Главное

  • record одной строкой создаёт класс неизменяемых данных: поля, конструктор, геттеры, equals и toString появляются автоматически.
  • var поручает компилятору вывести тип локальной переменной, но тип по-прежнему неизменен.
  • Лямбда (x -> ...) — краткая реализация функционального интерфейса; Type::method — ссылка на метод.
  • Поток: stream() → filter / map / sorted → toList, count, sum, average; исходная коллекция не меняется.
  • Настоящий проект объединяет все части курса: классы, исключения, коллекции и современную Java.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Как прочитать координату x у record Point(int x, int y)?