Java: классы, интерфейсы и коллекции

Структура класса в Java, конструкторы и модификаторы доступа, интерфейсы и абстрактные классы, коллекции List, Set, Map, обобщения, исключения и работа с файлами.

Java остаётся основным языком курсовых по объектно-ориентированному программированию: строгая типизация не позволяет обойти принципы ООП, а значит работа получается показательной.

Класс и объект

public class Student {
    private String name;              // поля закрыты — инкапсуляция
    private int course;
    private double avgGrade;

    public Student(String name, int course) {   // конструктор
        this.name = name;
        this.course = course;
        this.avgGrade = 0.0;
    }

    public String getName() { return name; }

    public void setAvgGrade(double grade) {
        if (grade < 2 || grade > 5)
            throw new IllegalArgumentException("Оценка вне диапазона: " + grade);
        this.avgGrade = grade;
    }

    public boolean isHonors() { return avgGrade >= 4.5; }

    @Override
    public String toString() {
        return String.format("%s, %d курс, средний балл %.2f", name, course, avgGrade);
    }
}
МодификаторСвой классПакетНаследникВезде
privateДаНетНетНет
(без модификатора)ДаДаНетНет
protectedДаДаДаНет
publicДаДаДаДа
Сеттер с проверкой — главный аргумент в пользу инкапсуляции, который стоит показать в отчёте: открытое поле принимает любое значение, а метод не даст записать некорректное.

Наследование и полиморфизм

public abstract class Employee {
    protected String name;
    protected double baseSalary;

    public Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }

    public abstract double calculateSalary();   // обязателен к реализации

    public void printInfo() {
        System.out.printf("%s: %.2f руб.%n", name, calculateSalary());
    }
}

class Manager extends Employee {
    private double bonus;

    public Manager(String name, double base, double bonus) {
        super(name, base);
        this.bonus = bonus;
    }

    @Override
    public double calculateSalary() { return baseSalary + bonus; }
}

class Worker extends Employee {
    private int hours;
    private double rate;

    public Worker(String name, int hours, double rate) {
        super(name, 0);
        this.hours = hours;
        this.rate  = rate;
    }

    @Override
    public double calculateSalary() { return hours * rate; }
}

// Полиморфизм: один цикл обрабатывает разные типы
Employee[] staff = { new Manager("Иванов", 80000, 20000),
                     new Worker("Петров", 160, 500) };
for (Employee e : staff) e.printInfo();

Интерфейс или абстрактный класс

ИнтерфейсАбстрактный класс
Сколько можно наследоватьМногоТолько один
ПоляТолько константыЛюбые
КонструкторНетЕсть
Реализация методовТолько default и staticЛюбая
СмыслКонтракт: «умеет делать»Общая основа: «является»
public interface Printable {
    void print();
    default String header() { return "=== Отчёт ==="; }
}

public class Report implements Printable, Comparable<Report> {
    private int priority;

    @Override public void print() { System.out.println(header()); }

    @Override public int compareTo(Report other) {
        return Integer.compare(this.priority, other.priority);
    }
}

Коллекции

ИнтерфейсРеализацияОсобенностьСложность поиска
ListArrayListМассив, быстрый доступ по индексуO(n) по значению
ListLinkedListСвязный список, быстрые вставкиO(n)
SetHashSetБез дубликатов, порядок не сохраняетсяO(1)
SetTreeSetОтсортированO(log n)
MapHashMapПары ключ-значениеO(1)
MapTreeMapОтсортирован по ключуO(log n)
QueueArrayDequeОчередь и стек
import java.util.*;
import java.util.stream.*;

List<Student> students = new ArrayList<>();
students.add(new Student("Иванов", 3));
students.add(new Student("Петров", 2));

// Сортировка по нескольким критериям
students.sort(Comparator.comparing(Student::getCourse)
                        .thenComparing(Student::getName));

// Группировка через Stream API
Map<Integer, List<Student>> byCourse = students.stream()
    .collect(Collectors.groupingBy(Student::getCourse));

// Фильтрация и подсчёт
long honors = students.stream().filter(Student::isHonors).count();

// Обход HashMap
Map<String, Integer> grades = new HashMap<>();
grades.put("Математика", 5);
for (Map.Entry<String, Integer> e : grades.entrySet())
    System.out.println(e.getKey() + ": " + e.getValue());
Помещая свой класс в HashSet или используя его как ключ HashMap, обязательно переопределите equals и hashCode. Без этого два одинаковых по содержанию объекта считаются разными, и коллекция ведёт себя не так, как ожидается.

Исключения

// try-with-resources закрывает файл автоматически
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (FileNotFoundException e) {
    System.err.println("Файл не найден: " + e.getMessage());
} catch (IOException e) {
    System.err.println("Ошибка чтения: " + e.getMessage());
} finally {
    System.out.println("Обработка завершена");
}

// Своё исключение
class InvalidGradeException extends Exception {
    public InvalidGradeException(String msg) { super(msg); }
}

Что включить в отчёт

  1. Диаграмма классов UML — обязательное требование почти всех кафедр.
  2. Обоснование выбора коллекций: почему HashMap, а не ArrayList.
  3. Демонстрация всех трёх принципов ООП с указанием, где именно они применены.
  4. Обработка некорректного ввода — покажите, что программа не падает.
  5. Модульные тесты на JUnit, хотя бы на ключевые методы.

Частые вопросы

Чем ArrayList отличается от LinkedList?

ArrayList хранит элементы в массиве: доступ по индексу за O(1), но вставка в середину требует сдвига. LinkedList — связный список: вставка за O(1) при наличии ссылки, зато доступ по индексу за O(n). На практике ArrayList быстрее почти всегда.

Почему нельзя наследовать несколько классов?

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

Обязательно ли переопределять toString?

Формально нет, но без этого вывод объекта даёт что-то вроде Student@1b6d3586. Для отчёта и отладки осмысленный toString экономит массу времени.

Читайте также

Сделаем работу по этой теме

Опишите задачу — ответим в течение 15 минут в личных сообщениях ВКонтакте, назовём срок и цену. Предоплаты за оценку нет.

  • Оценка заявки бесплатно
  • Правки по замечаниям преподавателя
  • Работы по всем техническим и IT-дисциплинам

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