Могу ли я использовать более новые API Java при запуске старых программ Java с более новой JRE?

У меня есть приложение Java 8, в котором я скомпилировал байт-код Java 8 с использованием Java 17. Таким образом, source и target являются Java 8. Я сделал это потому, что в Java 17 было много улучшений алгоритмов GC. Я провел небольшое исследование и Я знаю, что мне следует писать в синтаксисе Java 8 (или более ранней версии), если я хочу скомпилировать байт-код Java 8.

Затем я заметил, что не могу использовать функцию Map.of(), так как API помечен @since 9, и это меня смутило. Я не использую новый синтаксис Java. Почему я могу использовать алгоритмы GC, поставляемые с Java 17, но не могу использовать стандартные библиотеки, поставляемые вместе с ним?

Вот как я думаю, это должно работать:
JRE 17 (которая запускает мое приложение) уже содержит все стандартные библиотеки Java 17, скомпилированные в байт-коде Java 17. Поэтому, когда JVM встречает Map.of() (или любые другие методы, отмеченные @since 9+), она должна просто запустить стандартную библиотеку, как обычно.

Кроме того, в чем конкретно проблема с запуском Map.of()?

Этот метод не существует в версии 8 Java. Поэтому вы не можете использовать его с версией 8 Java. Предположим, вы построили новый дом. Предположим, что в новом доме есть новая печь. Почему ты не можешь использовать эту печь в своем старом доме? Потому что в новом доме.

Elliott Frisch 06.07.2023 13:01

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

ParSal 06.07.2023 13:03

Вы явно запросили класс, совместимый с Java 1.8, поэтому javac отказывается компилировать код с Map.of() в нем. Потому что этот файл .class не будет работать с 1.8 JRE.

tgdavies 06.07.2023 13:11

Потому что, даже если у вас есть JDK 17, вы просите компилятор использовать синтаксис JDK 8. Какая JRE установлена ​​​​в целевой системе, не имеет значения во время компиляции. Компиляция должна ограничивать использование API JDK 8 и более ранних версий, поскольку вы указали этот источник и цель. Когда на самом деле он развернут в JDK 17, он работает со всеми функциями 17 на уровне JVM, но ваш код по-прежнему скомпилирован с совместимостью с JDK 8. Он также может работать на JRE 8.

Ishan 06.07.2023 13:11

На самом деле я ошибся выше. Вы можете использовать javac -source 1.8 -target 1.8 для компиляции кода, использующего Map.of().

tgdavies 06.07.2023 13:13

Возможно, я ошибаюсь: вы используете инструменты из java17 и синтаксис кода из Java 8. jre из Java8 знает формат из байт-кода, но не имеет реализации для более новых инструментов.

Reporter 06.07.2023 13:13

Когда вы говорите: «Я заметил, что не могу использовать функцию Map.of()», что мешало вам ее использовать?

tgdavies 06.07.2023 13:14

@tgdavies Правда? но ваше объяснение звучит разумно. Если я нацелен на jre 1.8, он должен работать на 1.8, поэтому у него не должно быть Map.of. У меня просто написано "не удается найти символ"

ParSal 06.07.2023 13:16

@tgdavies — когда я смотрю исходный код RTL, я не могу найти маркер для методов или классов, который ограничивает этот код определенной версией. Есть только тег @since JavaDoc, и я сомневаюсь, что он будет каким-то образом переведен на что-то вроде if ( version > 1.8 ) или подобное. То, что будут работать чистые расширения классов вроде map.of(), меня не удивляет. Но var не должно работать, также не должно работать сопоставление с образцом при компиляции с -source 1.8. -target 1.8 управляет только бинарным выходом.

tquadrat 06.07.2023 13:23

В любом случае - вы не хотите этого делать. Ваша «старая Java-программа, которая использует Map.of()», не будет работать на JRE 1.8, потому что она не найдет этот метод, так зачем вообще ориентироваться на 1.8?

tgdavies 06.07.2023 13:46

Это большой старый проект, который работал на Java 8, мы изменили jre на 17, чтобы использовать алгоритмы GC. Изменение уровня языка до 17 — это еще один шаг, который мы можем сделать в будущем. Поскольку единственными людьми, запускающими нашу программу, являемся мы, я подумал, почему бы не использовать функции в java 17, если мы можем.

ParSal 06.07.2023 13:51

Map.of не существует в jdk 1.8, но он действителен. Вы можете нормально скомпилировать исходный/целевой набор для java 1.8. Однако, когда вы пытаетесь запустить его на jvm 1.8, он не может найти метод. Как, по-вашему, этот метод может быть доступен? Кроме того, если вы скомпилируете код, а затем запустите его на jvm 1.8, он будет использовать сборщик мусора 1.8. Как вы думаете, какие проблемы у вас возникнут, если вы измените уровень языка?

matt 06.07.2023 14:05

@matt Я работаю только на jre 17, который должен найти метод. Мой вопрос заключался в том, как он может использовать GC из java 17, но не Map.of из него?

ParSal 06.07.2023 14:25

"он просто говорит "не удается найти символ"" - что это?

tgdavies 06.07.2023 14:28

(и я до сих пор не понимаю, почему вы не компилируете с исходным кодом и целью 17)

tgdavies 06.07.2023 14:29

@ParSal Я не верю в это. Если вы используете JRE 8, то он будет использовать этот GC и не сможет найти символ для Map.of, их не существует. Они не включены в файл класса, который вы компилируете. Если вы используете JRE 17, то он будет использовать новый GC и найдет символ для Map.of.

matt 06.07.2023 14:38

Предположим, я написал инструкции по приготовлению рецепта с двухконфорочной плитой. У вас четырехконфорочная плита. Таким образом, вы можете выполнять несколько шагов одновременно (когда вы используете плиту с четырьмя конфорками), но если вы используете плиту с двумя конфорками, вы не можете. Это печка другая. Не настоящий рецепт. Когда вы запускаете байт-код Java 8 на Java 17 JVM, он может использовать больше устройств записи, он понимает рецепт (ваш байт-код). Чего вы не можете сделать, так это сказать, что этот рецепт предназначен для печи с двумя конфорками, начните с зажигания всех четырех конфорок.

Elliott Frisch 06.07.2023 14:39

Обновление: я ошибался: как сказал @tgdavies, код компилируется. Виновником в моем случае был <maven.compiler.release> , который делает именно это ! Судя по всему, ответ @matt ниже кажется правильным.

ParSal 06.07.2023 15:23

@tgdavies Может ли это внести какие-либо сложности в существующий код? Если у меня есть исходный код java 8, который скомпилирован в java 8 с использованием java 17 и всегда запускается java 17, я не могу придумать ничего, что могло бы пойти не так, если бы я изменил уровень языка на 17.

ParSal 06.07.2023 15:25

Верно, "ничего" не могло пойти не так. Если вы компилируете его с помощью jdk 17 с исходным и целевым наборами. Он по-прежнему будет компилироваться без исходного и целевого набора. По сути, объявление источника и цели заставляет jdk 17 работать с подмножеством кода, с которым он работает. Я процитировал «ничего», потому что всегда есть исключение, особенно с аудиторией, которую вы достигаете на SO.

matt 06.07.2023 16:03
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
20
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Байт-код меняется, но вы можете сделать совместимый байт-код, указав javac "нацелить" более старую версию байт-кода.

Кроме того, исходный код меняется. Например, лямбда-выражения. Их не было в java 1.6, поэтому вы не можете их использовать и ожидаете создания байт-кода, совместимого с 1.6. Вы можете указать javac, что хотите скомпилировать только исходный код, совместимый с java 1.6, и вы получите сообщение об ошибке о лямбда-выражениях.

Когда у вас есть совместимый исходный код и совместимый байт-код, у вас возникают проблемы с библиотеками. Это очень просто, скажем, у меня есть библиотека "mylibrary.jar" и класс "MyClass.class". Я могу скомпилировать код, предоставив библиотеку, если вы хотите запустить этот код. Вам все еще нужна библиотека.

Map.of является дополнением к стандартному jdk, поэтому его нет в 1.8 jdk. Все в нем совместимо с java 8, поэтому он проходит исходный и целевой тесты. Вам просто нужно предоставить его при запуске программы. Как и в любой другой библиотеке.

Другие вопросы по теме