С момента первого выпуска Java в 1995 году многое изменилось как в программном, так и в аппаратном обеспечении. Мощные процессоры и более дешевая память позволяют нашим приложениям работать быстрее и обрабатывать большее количество информации. Язык программирования Java все эти годы тоже не стоял на месте и активно развивался, однако успел обрасти мифами. В этой статье мы рассмотрим ряд ложных и устаревших представлений о Java.

Миф 1: Java старая и не развивается.

Бытует мнение что Java это устаревший язык программирования, хотя PHP такой же старый, а тот же Python ещё на четыре года старше.

Что касается развития языка, то у Java до 9-й версии не было установленного графика выпуска. Новая версия выпускалась, когда реализовывался предопределенный список новых функций. Но с 2018 года у Java появился шестимесячный цикл релизов с фиксированными датами, и каждая новая версия содержит функции, которые полностью завершены и протестированы сообществом. Также в релизе присутствуют и экспериментальные функции, которые все еще находятся в разработке, однако они доступны только в том случае, если приложение собрано и запущено с дополнительными флагами.

Благодаря этому новому подходу каждая новая версия приносит улучшения как в плане производительности, так и в плане написания кода. А поскольку с обратной совместимостью в OpenJDK всё хорошо, то в большинстве случаев можно без проблем запускать существующие приложения с более новыми версиями JDK не меняя кода, при этом получая выгоду от меньшего времени запуска, меньшего использования памяти и улучшенной сборкой мусора.

Вот очень краткий обзор ряда изменений в версиях OpenJDK, но имейте в виду, что это лишь малая часть новых функций и улучшений. Некоторые функции были впервые представлены как экспериментальные (превью) и доработаны только в более поздней версии:

Версия

Изменения

OpenJDK 19

Виртуальные потоки (превью)

OpenJDK 18

Pattern Matching для switch (второй превью)

OpenJDK 17

Sealed типы

OpenJDK 16

Новый тип объявления классов - record, Pattern matching для instanceof

OpenJDK 15

Текстовые блоки

OpenJDK 14

Helpful NPE, стримминг из запущенного приложения в JFR, Java Packager (jpackage), Pattern matching

OpenJDK 13

Улучшенные версии превью-фич

OpenJDK 12

Первое превью текстовых блоков

OpenJDK 11

Упрощённый запуск программ из одного файла, Java Flight Recorder (JFR)

OpenJDK 10

Local Variable Type Inference (LVTI) - он же тип var

OpenJDK 9

Модульность, Java Dependency Analyser (jdeps), Java Shell (jshell)

OpenJDK 8

Лямбда-выражения и Stream API, default-методы в интерфейсах. Всё это очень сильно повлияло на стиль программирования на Java.

Факт: 6-месячный цикл выпуска привнес много изменений в OpenJDK.

Миф 2: Нужно скомпилировать код, прежде чем его можно будет запустить

Действительно, Java — это компилируемый язык программирования, и для выполнения программы лучший вариант это предварительно скомпилировать код в class‑файл. Этот файл содержит байтовый код, который может выполняться виртуальной машиной Java (JVM) на любой платформе.

Но есть и другие способы выполнения Java‑кода!

В Java 9 появился jshell как часть Java Development Kit (JDK). Это интерфейс командной строки для выполнения кода Java, который можно использовать, например, для тестирования ваших кусков кода. Как описано в JEP 222, код все еще компилируется, но это происходит за кулисами.

% jshell
|  Welcome to JShell -- Version 18.0.1
|  For an introduction type: /help intro

jshell> String message = "Hello World";
message ==> "Hello World"

jshell> "Test: " + message
$2 ==> "Test: Hello World"

jshell> 8*9
$3 ==> 72

А в Java 11 появился новый способ — выполнения однофайлового кода. Он позволяет запускать программу, состоящую из одного файла, точно так же, как запускаются скрипты PHP или bash. Например:

java HelloWorld.java

Кроме этого, существует такой инструмент, как JBang, который позволяет выполнять код, использующий зависимости, без необходимости его компилировать или использовать инструменты сборки (Maven или Gradle). И если на компьютере не установлена Java, то JBang поставит её за вас!

Факт: под капотом действительно Java‑код должен быть скомпилирован, прежде чем его можно будет запустить. Но теперь доступны различные инструменты, которые позволяют «скрыть» этот процесс и упростить непосредственное выполнение Java‑кода.

Миф 3: Java медленная

C 1999 года в Java появилась динамическая (Just‑In‑Time) компиляция.

Текущий Hotspot JIT компилятор или например улучшенный компилятор Falcon, разработанный компанией Azul, позволяют приложениям на Java работать наравне с приложениями на C++. Так же Java намного быстрее, чем большинство популярных скриптовых языков, таких как Python или JavaScript.

Однако есть нюанс — приложения на Java запускаются медленнее, чем приложения на C++, и требуют специальной подготовки, пока JIT компилятор не начнет действовать. Из‑за данной особенности в начале 2000-х годов на персональных компьютерах запуск Java‑приложений происходил заметно медленнее, приложений на C++, из‑за чего создалось впечатление что Java работает медленно.

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

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

Миф 4: Java небезопасна

В конце 90-х и 2000-х простого HTML было явно недостаточно, поэтому были очень популярны браузерные плагины, которые позволяли запускать различные приложения. Наиболее известными в то время технологиями были Flash, ActiveX, Silverlight и Java‑апплеты. Все эти технологии, основанные на плагинах, устарели и полностью запрещены в корпоративной среде, так как с развёртыванием и безопасностью у них очень плохо.

Среди этих технологий Flash был одним из самых уязвимых, и хотя Java‑апплеты были лучше изолированы и безопаснее чем Flash — полностью проблема безопасности решена не была, что и способствовало распространению мифа о том, что Java небезопасна.

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

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

Факт: полностью безопасной системы не существует, но наличие рабочего процесса, который собирает, документирует и устраняет проблемы, гарантирует, что безопасность Java поддерживается на высоком уровне.

Миф 5: Сложно установить Java на свой компьютер, и не понятно что с лицензией

Поскольку OpenJDK — это проект с открытым исходным кодом, любой может создать свою реализацию JVM. Многие некоммерческие и коммерческие организации как раз взяли на себя эту роль.

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

Факт: большинство дистрибутивов OpenJDK имеют установщики для различных платформ, а такие инструменты, как SDKMAN, позволяют легко устанавливать и переключаться между версиями.

Миф 6: Для запуска Java-кода на ПК должна быть установлена среда выполнения Java

Традиционно среда выполнения Java (JRE) используется для выполнения программ на Java. Но, начиная с OpenJDK 11, имеется альтернатива — связать требуемую JRE с приложением для удобства развертывания и максимальной совместимости.

jlink (JEP 282) — это инструмент, который позволяет собирать модули и их зависимости в пользовательский образ Java Runtime. Можно сказать, что он создает минимально необходимую среду выполнения Java (JRE), специально поддерживающую ваше приложение.

Начиная с OpenJDK 14, был добавлен jpackage (JEP 343) для создания собственного исполняемого файла на основе вывода jlink. Так jpackage создает один исполняемый файл, который автоматически распаковывается без участия пользователя.

Есть и другие упаковщики, например, GluonFX Plugin for Maven может создавать приложения для Windows, macOS, Linux, Android и iOS.

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

Миф 7: Приложение приостанавливается во время очистки памяти

В некоторых ситуациях это именно то что нужно, особенно когда правильность результата важнее скорости выполнения, например для длительных пакетных операций. И в таком случае лучше всего подойдет так называемый сборщик мусора Stop‑The‑World.

Однако за прошедшие годы было реализовано множество других сборщиков мусора, и некоторые из них работают изолированно от потоков вашего приложения (параллельные сборщики мусора) и почти не влияют на выполнение кода, либо влияют минимально (минимальное время остановки). Из стандартных это Garbage First и Z Garbage Collector, а до это ещё был Concurrent Mark Sweep, но в данный момент он объявлен устаревшим. Так же имеются и сторонние реализации, например C4 GC (Continuously Concurrent Compacting Collector) от Azul.

Факт: в некоторых ситуациях сборщик мусора Stop‑The‑World — это то что нужно, а для других случаев можно использовать параллельные сборщики мусора.

Миф 8: Java не поддерживает множественного наследования

Изначально в Java не поддерживалось множественное наследование чтобы избежать так называемой «проблемы алмаза» (diamond problem), она же — ромбовидное наследование. Однако, с появлением JDK 8 появилась возможность множественного наследования поведения с решением данной проблемы.

Давайте рассмотрим пример с двумя интерфейсами ExampleA и ExampleB, имеющими дефолтный метод doSomething(). Если некий класс ExampleC имплементирует интерфейсы ExampleA и ExampleB, то вызов метода doSomething() приведет к двусмысленности, поскольку и ExampleA, и ExampleB предоставляют этот метод, и компилятор не знает какой из них использовать.

interface ExampleA {
    default void doSomething() {
        System.out.println("Output of A");
    }
}

interface ExampleB {
    default void doSomething() {
        System.out.println("Output of B");
    }
}

public class ExampleC implements ExampleA, ExampleB {

    @Override
    public void doSomething() {
        System.out.println("Output of C");
    }

    public static void main(String args[]){
        ExampleC example = new ExampleC();
        example.doSomething();
    }

}

Приведенный выше код без переопределённого метода doSomething() в ExampleC сгенерирует эту ошибку:

class ExampleC inherits unrelated defaults for doSomething() from types ExampleA and ExampleB

Но достаточно переопределить doSomething() в ExampleC и компилятор сразу поймёт что от него хотят:

Output of C

Факт: начиная с JDK 8 доступно множественное наследование поведения.

Миф 9: Интерфейсы не могут содержать методы с реализацией

Статические методы и default‑методы сняли это ограничение с Java, теперь можно легко поместить реализацию в интерфейс.

Как вы можете видеть в приведенном выше примере кода, оба интерфейса ExampleA и ExampleB содержат метод, выполняющий код.

Факт: это уже не так, и интерфейсы могут содержать методы с реализацией.

Заключение

С момента перехода на шестимесячный цикл релизов в 2018 году язык и инструменты Java претерпели множество изменений Одни фичи находятся «под капотом» и служат для повышения производительности и улучшения использования памяти, в то время как другие ориентированы на упрощение написания и поддержку кода. Так же не забываем и про prewiew‑фичи!

Список фич можно посмотреть тут. Особенно любопытен раздел “Draft and submitted JEPs”.

Комментарии (8)


  1. blik13
    05.04.2023 09:56
    +1

    В первом "мифе" некоторое смешение понятий, старый язык и устаревший язык это не одно и то же.


  1. panzerfaust
    05.04.2023 09:56
    +3

    Миф 9: Интерфейсы не могут содержать методы с реализацией

    Интересно, как много человек отказалось от изучения джавы, услышав это страшный миф?


  1. shai_hulud
    05.04.2023 09:56
    +1

    Миф 8: Java не поддерживает множественного наследования

    Поддержка множественного наследования, как мне кажется, это отрицательный фактор при выборе языка :)


    1. Alexey_Kutepov Автор
      05.04.2023 09:56
      -1

      Разные языки программирования используются для разных задач и в разных сферах, так что вряд ли наличие множественного наследования может повлиять на выбор языка, всё же основные критерии совсем другие. Например если это Data Science, то выбор падёт на Python, если это iOS разработка - то Swift, если Android - то Kotlin, если это бэк корпоративной системы, то могут быть варианты, но на текущий момент очень часто применяется именно Java, особенно в банках её любят.

      Что касается самого множественного наследования в Java, то как показано в примере, это не множественное наследование в классическом понимании, а скорее множественное наследование поведения + реализации с защитой от ромбовидного наследования. Так что страшного ничего нет :)


  1. matthew_shtyasek
    05.04.2023 09:56
    +2

    Java не поддерживает множественного наследования. Один класс в Java (как в вашем примере) может реализовать несколько интерфейсов, но никак не наследоваться от нескольких классов. Так что 8 миф — это не миф, а правда.


    1. Alexey_Kutepov Автор
      05.04.2023 09:56
      -1

      Так я и про наследование классов не писал (точнее не я, а автор оригинала). Тут подразумевается что изначально было только множественное наследование поведения, но с появлением default-методов в интерфейсах появилась возможность множественного наследования реализации, причём с защитой от ромбовидного наследования.

      Если требуется именно множественное наследование классов, то тут тоже есть обходной путь - цепочка классов, которые друг от друга наследуются (частый вопрос на собесах). Но да, это формально не множественное наследование в классическом понимании, а именно обходной путь.


  1. simenoff
    05.04.2023 09:56
    -1

    Миф 7: Приложение приостанавливается во время очистки памяти

    Почему тогда IDE отJetBrains периодически залипают?


    1. Alexey_Kutepov Автор
      05.04.2023 09:56
      -1

      Причины зависаний могут быть не только из-за GC, что не исключает возможность что они предпочли GC работающий по принципу stop-the-world. Но это больше вопрос к ним :)