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);
}
}
Коллекции
| Интерфейс | Реализация | Особенность | Сложность поиска |
|---|---|---|---|
| List | ArrayList | Массив, быстрый доступ по индексу | O(n) по значению |
| List | LinkedList | Связный список, быстрые вставки | O(n) |
| Set | HashSet | Без дубликатов, порядок не сохраняется | O(1) |
| Set | TreeSet | Отсортирован | O(log n) |
| Map | HashMap | Пары ключ-значение | O(1) |
| Map | TreeMap | Отсортирован по ключу | O(log n) |
| Queue | ArrayDeque | Очередь и стек | — |
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());
Исключения
// 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); }
}
- Проверяемые исключения (checked) наследуются от Exception и требуют обработки или объявления throws — компилятор не пропустит иначе.
- Непроверяемые (RuntimeException) — ошибки программиста: выход за границу массива, обращение по null, деление на ноль.
- Пустой блок catch — худшее, что можно сделать: ошибка исчезает бесследно. Как минимум логируйте её.
- try-with-resources предпочтительнее finally: закрывает ресурс даже при исключении в самом закрытии.
Что включить в отчёт
- Диаграмма классов UML — обязательное требование почти всех кафедр.
- Обоснование выбора коллекций: почему HashMap, а не ArrayList.
- Демонстрация всех трёх принципов ООП с указанием, где именно они применены.
- Обработка некорректного ввода — покажите, что программа не падает.
- Модульные тесты на JUnit, хотя бы на ключевые методы.
Частые вопросы
Чем ArrayList отличается от LinkedList?
ArrayList хранит элементы в массиве: доступ по индексу за O(1), но вставка в середину требует сдвига. LinkedList — связный список: вставка за O(1) при наличии ссылки, зато доступ по индексу за O(n). На практике ArrayList быстрее почти всегда.
Почему нельзя наследовать несколько классов?
Из-за проблемы ромбовидного наследования: при двух родителях с одинаковым методом непонятно, чей вызывать. Java решает это интерфейсами — они не содержат состояния, поэтому конфликта данных не возникает.
Обязательно ли переопределять toString?
Формально нет, но без этого вывод объекта даёт что-то вроде Student@1b6d3586. Для отчёта и отладки осмысленный toString экономит массу времени.