Core Java. Лекция 1

Обзор истории, возможностей и особенностей экосистемы Java

Иван Пономарёв, КУРС/МФТИ

profile
  • Выпускник МФТИ 2003 г., к.ф.-м.н

  • ERP systems & Java background

  • Speaker at conferences

  • Heisenbug Program Committee Member

Как со мной связаться?

Весь лекционный материал здесь

Полезные книги

horstmann
horstmann2
urma
  • Cay S. Horstmann. Core Java vol 1 & 2

  • Raoul-Gabriel Urma et al. Modern Java in Action

Полезные книги*

bloch
goetz
  • Joshua Bloch Effective Java

  • Brian Goetz et al. Java Concurrency in Practice

Зачем нужны лекции по Java в 2026м?

  • Устаревший язык, на нём, наверное, уже новых проектов и не пишет никто…​

  • Языки вообще учить не надо: Claude всё напишет за нас…​

  • Что такого я узнаю на лекциях, чего нет онлайн?..

 — всё это правильные вопросы! Отвечу на них с конца…​

Чем отличаются эти лекции от типичного онлайн-курса?

  • Инженерия — искусство компромиссов (engineering trade-offs).

  • Осваивать это искусство хорошо на удачных примерах из прошлого.
    Java — такой пример.

  • Инженеру в наше время не так важно знать конкретный язык, как понимать, что компромисс неизбежен, и уметь выбирать наилучший.

Что такое Java вообще? (И что значит «выучить Java»?)

  • Язык + виртуальная машина: https://docs.oracle.com/javase/specs/index.html

    • Java Language Specification (872 стр)

    • Java Virtual Machine Specification (626 стр)

  • Экосистема

    • стандартная библиотека,

    • иные JVM-языки (Kotlin, Scala, Groovy), и реализации языков для JVM (JRuby)

    • Репозиторий библиотек Maven Central и огромное количество переиспользуемого кода

    • Тулинг (IDE, инструменты тестирования)

Немного истории: как всё начиналось

gosling lotr
James Gosling (JPoint 2020)
  • Язык для приставок кабельного телевидения

  • Вдохновлён C/C++: знакомый синтаксис if(…​) {}; while(…​){}; …​

  • WORA

  • Было и другое, но WORA — самое главное

Где Java пыталась найти свою нишу

  • Ранние годы Интернета: браузерные «апплеты» в статических HTML-страницах.

  • Built-in security: виртуальная машина работает в «песочнице»

  • Продвинутые возможности на фоне примитивных браузеров.

  • В итоге технология проиграла языку JavaScript, выполняющемуся непосредственно в браузере

  • Сотрудничеству Sun и Netscape мы обязаны слову "Java" в "JavaScript".

netscape

Где Java нашла свою нишу?

Повсюду: от чипов в банковских картах до серверов в дата-центрах.

card full
  • Desktop (cross-platform)

  • Embedded Systems

  • Server-side (backend)

  • Mobile Devices (Android)

server rack
minecraft

Java и Android

  • При разработке Android, Google использовали Java как язык, javac, стандартные API, байткод, но не виртуальную машину и не реализацию API.

  • 11 лет судов Google с Oracle (2010—​2021), пока решение не было вынесено в пользу Google.

  • Kotlin (язык, компилирующийся в тот же JVM байт-код) провозглашён основным языком Android.

  • Oracle не признаёт настоящей «Джавой» то, что работает на Android.

  • По некоторым оценкам, в мире около 3.9 млрд Android-устройств.

android

Синтаксис тот же, типизация строже

c

java logo2

int i = 2;
if (i = 4) {
    printf("head\n");
} else {
    printf("tail\n");
}

Напечатает head (i = 4 — выражение, равное 4, воспринимаемое как «истина»).

int i = 2;
if (i = 4) {
    println("head");
} else {
    println("tail");
}

Не скомпилируется (как и в C, i = 4 — выражение, возвращающее 4, но его тип не boolean).

Синтаксис тот же, семантика строже

c

java logo2

int rise_salary(int base) {
 if (base < 0) {
  return 0;
  /*bug: misplaced
           after return*/
  printf("Invalid salary: %d\n",
                           base);
 }
 return base + 100;
}

Язык допускает подобный код (компилятор, в зависимости от версии и настроек, может выдать warning).

int riseSalary(int base) {
 if (base < 0) {
  return 0;
  /*bug: misplaced
           after return*/
  println("Invalid salary: "
                     + base);
 }
 return base + 100;
}

Не скомпилируется (unreachable statement не допустим).

rtvscompiletime

Синтаксис тот же, семантика строже!

c

java logo2

int i = 2;
i = ++i + ++i + ++i;

Неопределённое поведение
(https://stackoverflow.com/questions/3879176/i-i-i-in-java-vs-c)

int i = 2;
i = ++i + ++i + ++i;

3 + 4 + 5 = 12

Синтаксис тот же, безопасности больше

c

java logo2

int array[2];
printf("%d\n", array[5]);

Неопределенное поведение (включая чтение чужих данных или segmentation fault).

int[] array = new int[2];
println(array[5]);

ArrayIndexOutOfBoundsException, который можно перехватить и обработать. Цена: проверка при каждом обращении к элементу массива!

Синтаксис (почти) тот же, безопасности больше

c

java logo2

Employee* e = NULL;
printf("%d\n", e->salary);

Неопределенное поведение (включая аварийное завершение программы).

Employee e = null;
println(e.salary);

NullPointerException, который можно перехватить и обработать.

Проблема несовместимости платформ

  • На разных типах компьютеров разные

    • Endianness…​

    • Размер регистров и ссылок (sizeof(int), sizeof(void *)…​

    • Floating point hardware & format…​

  • Приходилось разрабатывать разные версии программ для разных платформ (или писать спагетти из #ifdef)

JVM

  • Java bytecode — исполняемый код виртуальной машины. Виртуальные машины (написанные на C++) написаны для разных платформ, все они исполняют байт-код.

  • LLVM intermediate representation — схожая идея, появившаяся позднее.

  • Решается вопрос не только кроссплатформенности, но и безопасности.

Diagram
Diagram

Путь байткода: верификация, интерпретация и JIT-компиляция

Diagram

Путь байткода: верификация, интерпретация и JIT-компиляция

Diagram
  • JVM начинает исполнение с интерпретации байт-кода, накапливая статистику выполнения.

Путь байткода: верификация, интерпретация и JIT-компиляция

Diagram
  • По мере того, как накапливается статистика использования кода, код компилируется в оптимизированный машинный код с использованием допущений, накопленных по статистике выполнения.

  • Скомпилированный код хранится в Code Cache (специальном сегменте оперативной памяти).

Путь байткода: верификация, интерпретация и JIT-компиляция

Diagram
  • Если путь выполнения меняется, срабатывает "uncommon trap" и выполнение возвращается к интерпретатору.

В низкоуровневых языках (например, C) мы контролируем аллокацию данных в памяти

typedef struct {
    int year;
    int month;
    int day;
} Date;
/* "stack allocation" */
Date val_date =
  {.year = 2026, .month = 8, .day = 15};
/* "heap allocation" */
Date* ptr_date = malloc(sizeof(Date));

*ptr_date = val_date; // values copied
ptr_date->day++; // value on heap increased
print_date(val_date); // 2026-8-15
print_date(*ptr_date); // 2026-8-16
Diagram

В C мы контролируем передачу данных по ссылке или по значению

Date modify_months(Date date1, Date *date2) {
  date1.month++;
  date2->month++;
  return date1;
}

Date result =
  modify_months(
    *ptr_date, // value at pointer
    &val_date  // address of value
);

print_date(val_date); // 2026-9-15
print_date(*ptr_date); // 2026-8-16
print_date(result); //2026-9-16
Diagram

Массивы ссылок и массивы значений

Date val_dates[3];
val_dates[0] = val_date;
val_dates[1] = *ptr_date;
val_dates[2] = result;

Date* ptr_dates[3];
ptr_dates[0] = &val_dates[0];
ptr_dates[1] = &val_dates[1];
ptr_dates[2] = &val_dates[1];
Diagram

Проблемы C: use after free

void fillInterval(Date* from, Date* to) {
  *from = (Date){.year = 2026,
    .month = 8, .day = 15};
  Date dateTo = (Date){.year = 2026,
    .month = 8, .day = 16};
  to = &dateTo;
  free(from);
}

void main() {
  Date* from = malloc(sizeof(Date));
  Date* to = NULL;
  fillInterval(from, to);
  print_date(*from); //Access violation
  print_date(*to);
}
Diagram

Проблемы C: потерянный указатель / утечка памяти

Date* date = malloc(sizeof(Date));
*date =
  (Date){.year = 2026,
         .month = 8,
         .day = 15};
date = malloc(sizeof(Date));
*date =
  (Date){.year = 2026,
         .month = 8,
         .day = 16};
Diagram

Java: свобода и производительность в обмен на безопасность

  • Нет больше * и &, всё определено за нас.

  • «Объекты» и «примитивы/value-объекты» ведут себя по-разному.

@AllArgsConstructor @ToString
class Date {
  int day; int month; int year; }

int modifyMonths(int month, Date date) {
  month++; date.month++;
  return month; }

Date d = new Date(15, 8, 2026);
int month = 8;
int result = modifyMonths(month, d);
println(month);  // 8
println(d);      // Date(d=15, m=9, y=2026)
println(result); // 9
Diagram

Массивы объектов в Java (не value-объектов) — всегда массивы ссылок

Date[] dates = new Date[3];

dates[0] = new Date(1, 1, 2023);
dates[1] = new Date(2, 2, 2024);
dates[2] = dates[1];
Diagram

Потерянная ссылка — не проблема

dates = new Date[0];
Diagram

Обоснование решений из 1995-го года

  • Нет (явных) аллокаций на стеке

  • Нет ручной деаллокации

⇒ нет проблемы «висячих» ссылок

  • Garbage collection

⇒ нет проблемы «потерянных» ссылок

  • 8 предопределенных примитивных типов (byte, short, int, long, char, float, double, boolean), не являющихся частью иерархии типов объектов.

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

Конечно, подход Java к безопасности — 
не единственный возможный подход…​

c sharp logo 2023

C#: «соперник Java», на 4 года моложе Джавы

  • имеет GC, но также имеет struct для типов-значений, аллоцируемых «на стеке», и ref для передачи данных по ссылке.

  • контроль «утечек» ref производится компилятором: нельзя передать ref на локальную переменную туда, где ссылка сохранится после выхода из функции, создавшей локальную переменную.

  • List<int> не использует типы-обёртки.

Конечно, подход Java к безопасности — 
не единственный возможный подход…​

rust logo blk

Rust: «соперник C», примерно на 10 лет моложе Джавы

  • Имеет struct, иммутабельные (&) и мутабельные (&mut) ссылки.

  • Borrow checking/lifetime checking во время компиляции.

  • unsafe {…​} в ситуациях, где он нужен.

20 лет Java (наряду с C) оставалась лидером индекса TIOBE

Diagram

Критика Java в годы «застоя» 2004—​2014 (версии 5—​8)

  • Потребляет слишком много памяти

  • Медленно стартует

  • Многословна (слишком много "boilerplate code")

  • Язык консервативен, медленно развивается (этот аргумент, в частности, привёл к появлению новых JVM языков: Scala, Groovy, Xtend, Kotlin).

С 2018 года Oracle перешёл на 6-месячный цикл релизов.

6-месячный цикл Java и взлёт Python

Diagram
redmonk rankings 2026

https://redmonk.com/ — Альтернатива TIOBE с 2012 г., GitHub+StackOverflow. Проблема: StackOverflow, достигавший пика в 2016м, теперь практически мёртв.

Что нам говорят TIOBE и RedMonk?

  • Мир меняется, языки и платформы развиваются в условиях жёсткой конкуренции — Java не исключение.

  • Как ни измеряй, Java/JVM на протяжении 30 лет была и продолжает быть одной из ведущих экосистем.

Сервисы виртуальной машины и платформы (помимо исполнения байт-кода)

  • Garbage Collection

  • MultiThreading & Memory Model

  • Загрузка и исполнение кода в runtime

  • Рефлексия

  • Стандартная библиотека

Виртуальная машина

  • Достоинства

    • WORA

    • Safety/техническая безопасность: исключение фатальных сбоев на сервере

    • Security/информационная безопасность: защита от злонамеренных действия

  • Недостатки

    • Overhead по производительности и памяти

    • Отсутствие гибкости, предоставляемой низкоуровневыми языками

Garbage Collection

  • Разработчику не нужно заботиться о высвобождении памяти

  • Ссылок на объект нет? — garbage collection

  • Достоинства:

    • Меньше кода

    • Не бывает некорректных ссылок (на разрушенный объект)

    • Меньше утечек памяти

  • Недостатки:

    • Не контролируем время работы GC

    • STW-паузы (бывает, в неподходящий момент)

Типы коллекторов в современной Java

GC полностью изолирован от исполняемого кода.
Семантика не меняется при замене одного GC на другой!

Diagram
  • Serial (for single-threaded, small heaps)

  • Parallel (high throughput, potentially 10..100ms or more STW)

  • G1 (Garbage First — default one since Java 9, generational, balanced on medium-to-large heaps, 200ms is default soft target)

  • ZGC: latency over throughput (normally <1ms)

  • Shenandoah latency over throughput (normally <1ms)

  • Epsilon (no GC at all)

Diagram

Latency vs. Throughput tradeoff

Latency

seesaw

Throughput

Многопоточность и модель памяти

  • Многопоточность

    • Возможность запуска параллельных потоков выполнения встроена в язык с первой версии (Thread, Runnable etc)

  • Memory Model

    • Часть спецификации VM: модель того, как работает хранилище данных в условиях многопоточного доступа

    • Отвечает на вопрос: «какие значения может прочитать конкретный read в программе?»

Когда нужна memory model?

class DumbWayToFallAsleep implements Runnable {
  private boolean asleep;

  public void setAsleep(boolean asleep){
    this.asleep = asleep;
  }

  @Override
  public void run() {
    while (!asleep){
      //countSomeSheep
      //УСНЁМ ЛИ МЫ?
    }
  }
}

Когда нужна memory model?

class NotSoDumbWayToFallAsleep implements Runnable {
  private volatile boolean asleep;

  public void setAsleep(boolean asleep){
    this.asleep = asleep;
  }

  @Override
  public void run() {
    while (!asleep){
      //countSomeSheep
      //...
    }
  }
}

Загрузка и исполнение кода в runtime

  • Скомпилированный байт-код класса может быть доставлен на любую систему в runtime (чаще всего «подкладыванием файла» на файловую систему).

  • Пересборка/линковка не требуется.

Сериализация объектов, встроенная в язык

  • Идея, в начале казавшаяся очень хорошей!

Рефлексия

  • Исследование информации о классах, полях, методах и т. д. во время выполнения

  • Чтение/модификация полей (в том числе private!) и динамический вызов методов (в том числе private!)

  • Proxy и AOP

Постоянно совершенствуемая стандартная библиотека

  • Независимая от системы часть

    • Структуры данных

    • Математика

  • Зависимая от системы часть

    • Дата и время

    • Доступ к файловой системе

    • Доступ к сети

    • Пользовательский интерфейс

Обратная совместимость

  • Любой, как угодно давно скомпилированный .jar-файл может быть запущен на любой современной JVM

  • Дар или проклятие?

Развитие языка c 10й версии (6-months release cadence)

  • type inference (var),

  • switch expressions,

  • text blocks

  • sealed classes,

  • pattern matching (instanceof, switch),

  • records,

  • unnamed main classes,

  • virtual threads…​

Текущие проекты

  • Project Amber: productivity-oriented features (например: flexible constructors)

  • Project Loom: fibers & continuators

  • Project Valhalla: value objects

  • Panama: Java & native code connection

  • Leyden: improve startup time

  • etc…​

Разнообразие языков для Java-машины

  • Есть виртуальная машина — можно компилировать из любого языка!

  • https://en.wikipedia.org/wiki/List_of_JVM_languages

  • 20-30 языков

    • Специально созданные: Groovy, Scala, Kotlin, Xtend…​

    • Реимплементация для JVM: JRuby, Clojure (Lisp dialect), Nashorn/Graal.js…​

Важнейшие элементы экосистемы (за рамками стандартной библиотеки)

  • Библиотеки и фреймворки (в т. ч. для тестирования)

  • Среды разработки

  • Репозиторий Maven Central

  • Системы сборки

junit logo

Модульное тестирование и автоматизация тестов

springframework

DI container and application framework

Среды разработки

IntelliJ IDEA Logo

IDEA

(proprietary, JetBrains)

Apache NetBeans Logo

NetBeans

(ASF / Oracle)

Eclipse Luna Logo

Eclipse Foundation

vscode

VS Code

(Microsoft)

maven central logo
  • 22 миллиона пакетов, 2-3 млн новых пакетов каждый год (https://mvnrepository.com/repos/central)

  • Условие публикации — open source (хотя есть исключения), обязательно прикладывать исходники и электронную подпись.

  • В отличие от PyPI и npm, авторы имеют group id, основанный на домене организации (com.microsoft, org.atp-fivt), и производится процесс верификации домена.

Системы сборки

mavenlogo

XML configuration
Maven project layout
plugins
declarative

gradlelogo

Groovy DSL / Kotlin DSL
plugins
scripts
mixed declarative/imperative

Java community

  • Conferences (Joker/JPoint/SnowOne, DevoXX etc.)

  • JUGs

duke

Hello, world! — страница 1

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.release>25</maven.compiler.release>
    </properties>
</project>

Hello, world! — страница 2

src/main/java/org/example/Main.java

package org.example;

public class Main {
    static void main() {
        IO.println("Hello World!");
    }
}
mvn package
java -cp target/hello-world-1.0-SNAPSHOT.jar org.example.Main