Все, что Вы должны знать о переходе на Java 11

Немного о модульной системе
Наверняка вы слышали о Java Platform Module System (JPMS), которая появилась в Java 9. Т.к. она вызывает множество проблем совместимости, с которыми вы столкнетесь во время перехода с Java 8, то это определенно будет подталкивать вас к тому, чтобы понять основы JPMS. Однако имейте в виду, вам не обязательно создавать модули для запуска вашего кода на Java 9 или на более новых версиях!
Вам не нужны модули для работы на Java 9+
Переход с Java 8 на Java 11
Мы закончили подготовку, пора начинать переход! Давайте посмотрим, какие проблемы вас ожидают при переходе на Java 11 и как их исправить.
Удаление модулей Java EE
В Java SE было много кода, который фактически был связан с Java EE. Он присутствовал в шести модулях, которые были объявлены кандидатами на удаление в Java 9 и удалены в Java 11. Вот удаленные технологии и пакеты:
  • JavaBeans Activation Framework (JAF) в javax. activation
  • CORBA в пакетах javax. activity, javax. rmi, javax.rmi.CORBA и org.omg.*
  • Java Transaction API (JTA) из пакета javax. transaction
  • JAXB в пакетах javax.xml.bind.*
  • JAX-WS в пакетах javax. jws, javax.jws.soap, javax.xml.soap, и javax.xml.ws.*
  • Commons Annotation в пакете javax. annotation
Симптомы
Вот ошибки компиляции класса, используемого JAXBException из the java.xml.bindmodule:
Если же вы сразу запустите приложение на выполнение, забыв о компиляции, то вы получите NoClassDefFoundError
Решение
Добавьте сторонние зависимости, которые содержат нужные вам классы. Самый простой способ сделать это — использовать эталонные реализации зависимостей (заданные с помощью Maven, без указания версий — используйте самую последнюю):
Дополнительные сведения, ресурсы и другие рекомендации можно посмотреть в этом посте на StackOverflow.
Незаконный доступ к внутренним API
Одним из наибольших преимуществ модульных систем является строгая инкапсуляция. Она гарантирует, что непубличные классы, а также классы из внутренних пакетов недоступны извне модуля. Прежде всего, это, конечно, относится к модулям платформы, поставляемым с JDK, где полностью поддерживаются только пакеты java. * и javax. *. С другой стороны, большинство пакетов com.sun. * и sun.* являются внутренними и следовательно недоступны по умолчанию.
Хотя компилятор Java 11 на этапе компиляции ведет себя так, как вы ожидаете, и предотвращает незаконный доступ, то же самое не относится к этапу выполнения. Это предполагает гибкую обратную совместимость, облегчает переход и повышает шансы приложений, разработанных на Java 8, выполняться на Java 11. Если для доступа используется механизм reflection, то выдается предупреждение.
Симптомы
Во время компиляции на Java 11 вы можете увидеть ошибки, похожие на следующие:
Предупреждения об использовании механизма reflection выглядят следующим образом:
Решения
Наиболее очевидным решением проблемы зависимостей от внутренних API-интерфейсов является избавление от этих зависимостей. Замените их внешними API-интерфейсами, и вы уменьшите технические риски.
      Если это не может быть сделано по какой-либо причине, следующая лучшая вещь — признать существование таких зависимостей и сообщить системе, что вам нужно получить к ним доступ. Для этого вы можете использовать две опции командной строки:
      • Параметр --add-exports module / package = $readmodule экспортирует $package из $module в $readmodule. Таким образом, код из $readmodule получает доступ ко всем открытым типам в $package, но другие модули это сделать не смогут. При установке флага $readmodule ALL-UNNAMED, весь код, расположенный в classpath, может получить доступ к этому пакету. Во время перехода на Java 11 вы всегда будете использовать эту опцию (вам придется изменить ее при модуляции). Она доступна для команд java и javac.
      • Способ, описанный выше, предоставляет доступ к открытым членам public типов, но reflection может сделать больше: при использовании setAccessible (true) механизм reflection позволяет взаимодействовать с приватными классами, полями, конструкторами и методами (иногда это называется deep reflection). Параметр java --add-opens использует тот же синтаксис, что и -add-exports, но открывает пакет для deep reflection. Это означает, что все его типы и их члены доступны независимо от их модификаторов видимости.
      Очевидно, вам нужен -add-exports, на этапе компиляции, но совместное использование -add-exports и -add-opens на этапе выполнения будет иметь свои преимущества:
      1. Механизм получения прав доступа во время выполнения кода может измениться в будущих релизах Java, и потому вам надо это сделать в любом случае.
      2. --add-opens делает предупреждения об использовании механизма рефлексии.
      3. Через минуту вы сможете убедиться в том, что при этом никаких новых зависимостей не возникнет, и во время исполнения будет применяться строгая инкапсуляция.
      Идем дальше
      Компиляция в Java 11 помогает отслеживать зависимости от внутренних API в коде проекта. Но библиотеки и фреймворки, используемые вашим проектом, также могут вызвать проблемы.
      JDeps — идеальный инструмент для поиска зависимостей компиляции от JDK-внутренних API в вашем проекте и в ваших зависимостях. Вот как использовать его для решения задачи:
      
      jdeps --jdk-internals -R --class-path 'libs/*' $project
      
      Здесь libs — это папка, содержащая все ваши зависимости; $project — JAR-файл вашего проекта. Анализ результатов работы JDeps выходит за рамки этой статьи, но это не так сложно — вы справитесь.
        Отслеживание механизма reflection немного сложнее. Поведение по умолчанию во время выполнения должно предупредить вас о первом незаконном доступе к пакету, чего явно недостаточно. К счастью, существует опция --illegal-access=$value, где $value может принимать следующие значения:
        • permit: доступ ко всем внутренним API JDK разрешен для кода из classpath. При работе механизма reflection выдается одно предупреждение при первом таком доступе к каждому пакету (По умолчанию в Java 9, но будет исключено в последующих релизах.)
        • warn: поведение как при permit, но для каждого вызова reflection выдается предупреждение
        • debug: поведение как при warn, но в каждое предупреждение включена трассировка стека
        • deny: вариант для тех, кто верит в сильное инкапсулирование: запрещено использование незаконного доступа по умолчанию
        Удаление устаревших API и JavaFX
        Начиная с Java 9, аннотация @Deprecated получила атрибут Boolean: forRemoval. Если значение этого атрибута установлено в true, deprecated элемент будет удален в следующем major-релизе. Это немного шокирует — раньше @Deprecated просто подчеркивалось желтыми линиями.
        Удаленные классы и методы
        Вот некоторые из наиболее распространенных классов и методов, которые были удалены между Java 8 и Java 11:
          • sun.misc.Base64 (используйте java.util.Base64)
          • com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel (используйте javax.swing.plaf.nimbus.NimbusLookAndFeel)
          • java.util.LogManager, java.util.jar.Pack200.Packer/ Unpacker: методы addPropertyChangeListener и removePropertyChangeListener
          • в пакете java.lang.Runtime: методы getLocalizedInputStream и getLocalizedOutputStream
          • различные методы в SecurityManager
          Отслеживание предупреждений об устаревшем коде (deprecation)
          Чтобы поддержка предупреждений об устаревшем коде было легче поддерживать, (deprecation), рекомендую использовать инструменты командной строки jdeps и jdeprscan. Они работают с файлами классов и JAR-файлами. Их можно найти в папке bin вашей JDK. Первый представляет собой многоцелевой инструмент анализа зависимостей, в то время как последний фокусируется на предоставлении отчетности об устаревших API-интерфейсах, подсвечивая те, которые будут вскоре удалены.
          JavaFX
          JavaFX никогда не был частью Java SE и лишь несколько версий Open JDK поставлялись вместе с ним. Некоторое время Oracle, похоже, проталкивала JavaFX, и поэтому включала его в свой JDK, но те времена прошли, и теперь Oracle, выпускающий JDK вместе с Open JDK, больше не поставляет JavaFX. Фактически, начиная с Java 11, вам будет очень сложно найти JDK, с которым поставляется JavaFX.

          Однако, не волнуйтесь, будущее прекрасно. OpenJFX, проект JavaFX, вытащил всю инфраструктуру пользовательского интерфейса в свои артефакты, которые вы просто добавляете в качестве зависимости. Вы можете скачать файлы этого проекта из Gluon или даже из Maven Central.
          Доработка для URLClassLoader
          Долгое время существовал способ программного добавления JAR-файлов в classpath, который состоял в том, чтобы создать системный ClassLoader и привести его к URLClassLoader для вызова метода AddURL. Начиная с Java 9 это больше не работает, поэтому команды (URLClassLoader) getClass().getClassLoader() или (URLClassLoader) ClassLoader.getSystemClassLoader() не будут выполняться. Это может вызвать некоторые проблемы при переходе.
          Симптомы
          Они очевидны. Вы получите ClassCastException, жалующийся, что новый AppClassLoader не является URLClassLoader:
          
          Exception in thread "main" java.lang.ClassCastException:
                  java.base/jdk.internal.loader.ClassLoaders$AppClassLoader
                  cannot be cast to java.base/java.net.URLClassLoader
                          at monitor.Main.logClassPathContent(Main.java:46)
                          at monitor.Main.main(Main.java:28)
          
          Решение
          Вероятно, загрузчик классов используется для доступа к методам, специфичным для URLClassLoader. Если это так, то вам, возможно, придется столкнуться с серьезными изменениями.
          Если вы хотите получить доступ к классам из classpath, проверьте properties для java.class.path и проанализируйте их:
          
          String pathSeparator = System.getProperty("path.separator");
          String[] classPathEntries = System.getProperty("java.class.path").split(pathSeparator);
          
          Если вы использовали URLClassLoader для динамической загрузки пользовательского кода (например, как часть инфраструктуры плагина или тестового фреймворка) путем добавления его к classpath, то вам необходимо найти новый способ для этого, поскольку с помощью Java 11 вы это не сделаете.
          Вместо этого вы должны рассмотреть возможность создания для этого нового пользовательского загрузчика классов.
          Здесь ваши шансы на переход с небольшими изменениями незначительны. Единственными поддерживаемыми (и, следовательно, доступными) родительскими классами для нового AppClassLoader являются SecureClassLoader и ClassLoader, и только несколько методов были добавлены здесь в Java 9. Тем не менее, проверьте, смогут ли эти методы сделать то, что вам необходимо.
          Оцените статью, если она вам понравилась!