Ну что, вы уже выставили глобальную переменную
JAVA_TOOL_OPTIONS=--illegal-access=permit?Помните недавно я сокрушался про Lombok, который не работает на Java 16? Вчера сразу после выхода Java GA появился патч, который чинит этот баг. Ну как чинит, там опять обходные пути и сплошная грязь 💩. Но давайте по порядку, т.к. там немного нетривиально.
Итак, Lombok использует внутренности модуля jdk.compiler, так как ему для своего функционирования необходимо представление абстрактного синтаксического дерева Java и ещё много чего. Большая часть пакетов jdk.compiler модулем не экспортируется, и начиная с Java 16 доступиться до этих пакетов стало нельзя без использования флагов
1. Метод Module.addOpens() является CallerSensitive, и его может дёрнуть только код из самого модуля jdk.compiler, иначе вылетит IllegalCallerException. Что делать? Если посмотреть на реализацию метода Module.addOpens(), то можно увидеть, что он делегирует работу другому внутреннему методу Module.implAddOpens(), который уже не CallerSensitive. Поэтому давайте будем вызывать его!
2. Но как вызвать внутренний метод, если теперь у нас внутренности JDK закрыты? Method.setAccessible(true) выбросит InacessibleObjectException. Что делать? Давайте попробуем сделать метод accessible через Unsafe! Сделать это можно так: в классе AccessibleObject (от которого отнаследован Method) есть поле boolean override. С помощью Unsafe это поле можно переключить с
3. Но не всё так просто. Чтобы вызвать putBooleanVolatile, нужно передать ему offset этого поля, получить который можно через другой метод Unsafe.objectFieldOffset.
4. Вроде справились? Ещё нет. Чтобы вызвать Unsafe.objectFieldOffset, нужно иметь на руках объект Field для поля override. Но тут вроде всё просто? Получить Field можно просто вызвав
5. Так что же делать? И вот тут самое веселье (такой хак мне ни за что не пришёл бы в голову). Нам ведь нужно просто передать offset поля, а это просто обычный long. Давайте как-нибудь посчитаем, какой offset имеет это поле в объекте Method. Как? Очень просто: объявим где-нибудь на нашей стороне класс с точно такими же полями, как и у Method (и на всякий случай в том же порядке), и найдём offset boolean поля для этого класса! Как тебе такое, Илон Маск? Ну а что: есть два класса с одинаковыми полями – значит JVM должна разложить эти поля по оффсетам в этих двух классах идентично.
Вот такой вот хак. И это даже работает.
А теперь посмотрим ещё раз, какие допущения использовались, и как легко это всё может развалиться:
1. В классе Module внутренний метод, отвечающий за логику открытия пакетов, называется implAddOpens. Если этот метод переименуют, добавят туда параметры или поменяют их порядок, Lombok сломается.
2. Класс AccessibleObject имеет внутреннее boolean поле, которое объявлено первым. Если это поле уберут, на первое место поставят другое поле, JVM начнёт компоновать поля как-нибудь по-другому или вообще логика AccessibleObject как-нибудь совсем поменяется – Lombok сломается.
3. А ещё, что есть Unsafe, который сам может когда-нибудь исчезнуть.
И ещё кстати это всё написано на Java 6, поэтому логика нахождения модулей, которая требует наличие таких классов как Optional, Module, ModuleLayer, реализована полностью через reflection.
#lombok #java16 #unsafe
Итак, Lombok использует внутренности модуля jdk.compiler, так как ему для своего функционирования необходимо представление абстрактного синтаксического дерева Java и ещё много чего. Большая часть пакетов jdk.compiler модулем не экспортируется, и начиная с Java 16 доступиться до этих пакетов стало нельзя без использования флагов
--illegal-access=permit или --add-opens. Можно было бы заставить пользователей руками передавать флаг javac -J--illegal-access=permit или -J--add-opens с перечислением 9 пакетов (com.sun.tools.javac.code, com.sun.tools.javac.comp…), но понятно, что такое решение максимум тянет на временный workaround. Поэтому был выбран другой путь: через reflection дёрнуть метод Module.addOpens(), передав туда наш модуль и имя пакета. Однако возникают следующие проблемы:1. Метод Module.addOpens() является CallerSensitive, и его может дёрнуть только код из самого модуля jdk.compiler, иначе вылетит IllegalCallerException. Что делать? Если посмотреть на реализацию метода Module.addOpens(), то можно увидеть, что он делегирует работу другому внутреннему методу Module.implAddOpens(), который уже не CallerSensitive. Поэтому давайте будем вызывать его!
2. Но как вызвать внутренний метод, если теперь у нас внутренности JDK закрыты? Method.setAccessible(true) выбросит InacessibleObjectException. Что делать? Давайте попробуем сделать метод accessible через Unsafe! Сделать это можно так: в классе AccessibleObject (от которого отнаследован Method) есть поле boolean override. С помощью Unsafe это поле можно переключить с
false на true, используя метод Unsafe.putBooleanVolatile. Давайте так и сделаем.3. Но не всё так просто. Чтобы вызвать putBooleanVolatile, нужно передать ему offset этого поля, получить который можно через другой метод Unsafe.objectFieldOffset.
4. Вроде справились? Ещё нет. Чтобы вызвать Unsafe.objectFieldOffset, нужно иметь на руках объект Field для поля override. Но тут вроде всё просто? Получить Field можно просто вызвав
AccessibleObject.class.getDeclaredField("override"). Однако это не сработает: эту лавочку прикрыли в Java 12 в целях безопасности, и поле override стало невидимым для reflection (чтобы кто попало не мог своими грязными ручонками вот так делать setAccessible, доступаясь до чего угодно: мы видите ли тут всё гайки закручиваем, а эти гады продолжают обходить и обходить).5. Так что же делать? И вот тут самое веселье (такой хак мне ни за что не пришёл бы в голову). Нам ведь нужно просто передать offset поля, а это просто обычный long. Давайте как-нибудь посчитаем, какой offset имеет это поле в объекте Method. Как? Очень просто: объявим где-нибудь на нашей стороне класс с точно такими же полями, как и у Method (и на всякий случай в том же порядке), и найдём offset boolean поля для этого класса! Как тебе такое, Илон Маск? Ну а что: есть два класса с одинаковыми полями – значит JVM должна разложить эти поля по оффсетам в этих двух классах идентично.
Вот такой вот хак. И это даже работает.
А теперь посмотрим ещё раз, какие допущения использовались, и как легко это всё может развалиться:
1. В классе Module внутренний метод, отвечающий за логику открытия пакетов, называется implAddOpens. Если этот метод переименуют, добавят туда параметры или поменяют их порядок, Lombok сломается.
2. Класс AccessibleObject имеет внутреннее boolean поле, которое объявлено первым. Если это поле уберут, на первое место поставят другое поле, JVM начнёт компоновать поля как-нибудь по-другому или вообще логика AccessibleObject как-нибудь совсем поменяется – Lombok сломается.
3. А ещё, что есть Unsafe, который сам может когда-нибудь исчезнуть.
И ещё кстати это всё написано на Java 6, поэтому логика нахождения модулей, которая требует наличие таких классов как Optional, Module, ModuleLayer, реализована полностью через reflection.
#lombok #java16 #unsafe
Почему-то у нас только один Tonsky говорит про UX. Я тоже хочу.
Какой вот, например, сакральный смысл дублировать breadcrumbs в Идее? Причём второй breadcrumb какой-то урезанный, и, ИМХО, бесполезный. Можно убрать этот второй урезанный breadcrumbs и на его место перенести главный. В Eclipse именно так и сделано (второй скрин).
Результат: будет меньше визуальных элементов и будет сэкономлено драгоценное вертикальное пространство (для меня это важно - я работаю на маленьком ультрабуке).
#IntelliJIDEA #Eclipse #UX
Какой вот, например, сакральный смысл дублировать breadcrumbs в Идее? Причём второй breadcrumb какой-то урезанный, и, ИМХО, бесполезный. Можно убрать этот второй урезанный breadcrumbs и на его место перенести главный. В Eclipse именно так и сделано (второй скрин).
Результат: будет меньше визуальных элементов и будет сэкономлено драгоценное вертикальное пространство (для меня это важно - я работаю на маленьком ультрабуке).
#IntelliJIDEA #Eclipse #UX
Если бы у меня спросили назвать 10 главных классных фишек Идеи, то одной из них я бы однозначно назвал умение масштабировать свой UI на лету.
Проверить это очень просто: заходите в настройки дисплея в своей операционной системе, меняете масштаб со 100%, скажем, на 150%, и вуаля – Идея тут же подхватила изменения, и все шрифты, контролы, иконки стали выглядеть в 1.5 раза больше. А ещё более эффектно это можно продемонстрировать так: подключаете к компьютеру второй монитор с другим DPI (скажем, один монитор 4k, а другой FullHD) и начинаете перетаскивать мышью окошко Идеи туда-сюда с одного дисплея на другой и обратно. И Идея будет переключать свой scale прямо на глазах.
Но почему это для меня так важно, спросите вы? Я что, постоянно лажу в настройки и меняю туда-сюда масштаб? Да, только я не специально это делаю, а потому что у меня есть три разных компьютера, с которых я работаю. Главный компьютер стоит на работе, и там 4k монитор с 200%. Другие два – это домашний комп со 100% и ультрабук со 150%, которые подключены через RDP к рабочему. И вот я постоянно между ними переключаюсь: то лёжа работаю с маленьким ноутом, то стоя за компом, а иногда и на работу прихожу и там сижу за основным. И при каждом таком переключении Идея подхватывает DPI и переходит на правильный масштаб. Без рестарта. Казалось бы, это какие-то минуты в день, но всё равно приятно.
Впрочем, это не столько заслуга Идеи, сколько Swing'а, на котором она написана. Напомню, что в отличие от AWT и SWT, которые переиспользуют нативные контролы, в Swing все элементы рисуются поверх окошка программным образом. И я считаю, что это очень удачное решение. Точно так же, например, работает весь веб – браузеры рисуют содержимое страниц поверх окна, и поэтому тоже хорошо переключают масштаб. А вот 80% остальных приложений системы работают плохо, потому что используют нативный UI. Например, Telegram Desktop становится крошечным, и его приходится перезапускать. У Teams едет верхняя панель. JetBrains ToolBox становится мыльным. Ну и, конечно же, всё плохо с Eclipse, который является моей основной IDE.
Какой из этого вывод? Swing написан в конце 90-х, и там про слово HiDPI даже не слышали. Но они заложили фундамент – абстрагировались от натива и стали рисовать UI руками. И когда этот HiDPI появился, то его поддержку добавили относительно небольшими усилиями. Поэтому если вы просто запускаете старое Swing приложение на Java 9+ на HiDPI-дисплее, то автоматом получается красиво. Ну или почти автоматом. Иконки он, естественно, не перерендерит за вас. Swing их увеличит, и они станут мыльными. Поэтому вам придётся предоставлять HiDPI-версии иконок. Идея, кстати, использует SVG-иконки, поэтому её масштабировать можно как угодно, даже в некруглые масштабы типа 175% или 225%, и она во всех случаях будет выглядеть хорошо.
А что касается нативного Look and Feel, то я считаю, что он переоценён. Да, натив, это конечно, привычно и удобно для юзера, но в наше время большого количества девайсов гораздо важнее переносимость и кроссплатформенность. Да и нативный Look всегда можно сымитировать программно, если хочется. Вон, Swing на macOS выглядит вообще неотличимо от натива.
Впрочем, это ещё не ставит окончательный крест на SWT. Всё-таки это тоже абстракция, и нигде в спецификации не написано, что он обязан использовать нативные контролы. Приложив определённые усилия, можно реализовать отрисовку SWT-контролов аналогично Swing'у. Но нужен тот, кто готов вложиться в такой масштабный проект.
Проверить это очень просто: заходите в настройки дисплея в своей операционной системе, меняете масштаб со 100%, скажем, на 150%, и вуаля – Идея тут же подхватила изменения, и все шрифты, контролы, иконки стали выглядеть в 1.5 раза больше. А ещё более эффектно это можно продемонстрировать так: подключаете к компьютеру второй монитор с другим DPI (скажем, один монитор 4k, а другой FullHD) и начинаете перетаскивать мышью окошко Идеи туда-сюда с одного дисплея на другой и обратно. И Идея будет переключать свой scale прямо на глазах.
Но почему это для меня так важно, спросите вы? Я что, постоянно лажу в настройки и меняю туда-сюда масштаб? Да, только я не специально это делаю, а потому что у меня есть три разных компьютера, с которых я работаю. Главный компьютер стоит на работе, и там 4k монитор с 200%. Другие два – это домашний комп со 100% и ультрабук со 150%, которые подключены через RDP к рабочему. И вот я постоянно между ними переключаюсь: то лёжа работаю с маленьким ноутом, то стоя за компом, а иногда и на работу прихожу и там сижу за основным. И при каждом таком переключении Идея подхватывает DPI и переходит на правильный масштаб. Без рестарта. Казалось бы, это какие-то минуты в день, но всё равно приятно.
Впрочем, это не столько заслуга Идеи, сколько Swing'а, на котором она написана. Напомню, что в отличие от AWT и SWT, которые переиспользуют нативные контролы, в Swing все элементы рисуются поверх окошка программным образом. И я считаю, что это очень удачное решение. Точно так же, например, работает весь веб – браузеры рисуют содержимое страниц поверх окна, и поэтому тоже хорошо переключают масштаб. А вот 80% остальных приложений системы работают плохо, потому что используют нативный UI. Например, Telegram Desktop становится крошечным, и его приходится перезапускать. У Teams едет верхняя панель. JetBrains ToolBox становится мыльным. Ну и, конечно же, всё плохо с Eclipse, который является моей основной IDE.
Какой из этого вывод? Swing написан в конце 90-х, и там про слово HiDPI даже не слышали. Но они заложили фундамент – абстрагировались от натива и стали рисовать UI руками. И когда этот HiDPI появился, то его поддержку добавили относительно небольшими усилиями. Поэтому если вы просто запускаете старое Swing приложение на Java 9+ на HiDPI-дисплее, то автоматом получается красиво. Ну или почти автоматом. Иконки он, естественно, не перерендерит за вас. Swing их увеличит, и они станут мыльными. Поэтому вам придётся предоставлять HiDPI-версии иконок. Идея, кстати, использует SVG-иконки, поэтому её масштабировать можно как угодно, даже в некруглые масштабы типа 175% или 225%, и она во всех случаях будет выглядеть хорошо.
А что касается нативного Look and Feel, то я считаю, что он переоценён. Да, натив, это конечно, привычно и удобно для юзера, но в наше время большого количества девайсов гораздо важнее переносимость и кроссплатформенность. Да и нативный Look всегда можно сымитировать программно, если хочется. Вон, Swing на macOS выглядит вообще неотличимо от натива.
Впрочем, это ещё не ставит окончательный крест на SWT. Всё-таки это тоже абстракция, и нигде в спецификации не написано, что он обязан использовать нативные контролы. Приложив определённые усилия, можно реализовать отрисовку SWT-контролов аналогично Swing'у. Но нужен тот, кто готов вложиться в такой масштабный проект.
Теперь в Java у классов может быть 5 модификаторов. До sealed классов могло быть максимум 4.
#БесполезныйФактОJava
#БесполезныйФактОJava
Тут хотят добавить в JDK простой веб-сервер, с помощью которого можно будет одной простой командой статически хостить папку с файлами. Это очень круто, так как может быть очень удобно для простых случаев вроде прототипирования, тестирования или обучения.
Однако самый прикол в том, что встроенный веб-сервер уже давно существовал в JDK (ещё с шестёрки) и был частью публичного API! Смотрите сами:
• Веб-сервер находится в модуле jdk.httpserver.
• Там экспортированы два пакета com.sun.net.httpserver и com.sun.net.httpserver.spi.
• Есть джавадоки, в которых чётко задокументированы публичные классы и не написано ничего про то, что этот модуль закрыт или проприетарен.
• Наконец, в JEP 408 написано, что это public APIs. А в Java 8 стоит аннотация jdk.Exported.
Так что можно смело пользоваться хоть сейчас. Правда существующий API является низкоуровневым, и чтобы поднять сервер, придётся писать свои HTTP-хендлеры. В JEP 408 это всё упростят и сделают возможность поднимать сервер одной командой.
Однако самый прикол в том, что встроенный веб-сервер уже давно существовал в JDK (ещё с шестёрки) и был частью публичного API! Смотрите сами:
• Веб-сервер находится в модуле jdk.httpserver.
• Там экспортированы два пакета com.sun.net.httpserver и com.sun.net.httpserver.spi.
• Есть джавадоки, в которых чётко задокументированы публичные классы и не написано ничего про то, что этот модуль закрыт или проприетарен.
• Наконец, в JEP 408 написано, что это public APIs. А в Java 8 стоит аннотация jdk.Exported.
Так что можно смело пользоваться хоть сейчас. Правда существующий API является низкоуровневым, и чтобы поднять сервер, придётся писать свои HTTP-хендлеры. В JEP 408 это всё упростят и сделают возможность поднимать сервер одной командой.
Нарисовал с помощью LJV, как внутри устроен SpinedBuffer. Для тех кто не в курсе, это библиотека, которая с помощью reflection строит граф объекта и конвертирует его в Graphviz.
В графе показан spined buffer после добавления в него 99 элементов. Граф немного подшаманен вручную для компактности.
Spined buffer - это структура, которая используется в таких операциях как
Так что если переходите на Java 16, меняйте все
#java16 #stream #ljv
В графе показан spined buffer после добавления в него 99 элементов. Граф немного подшаманен вручную для компактности.
Spined buffer - это структура, которая используется в таких операциях как
Stream.toArray() и Stream.toList(). Такая структура накапливает элементы намного эффективнее, чем ArrayList, потому что вместо постоянной переаллокации одного и того же массива использует массив массивов, где каждый следующий chunk в два раза длиннее предыдущего. Поэтому Stream.toList() работает намного эффективнее, чем collect(Collectors.toList()) и даже чем ручное наполнение ArrayList без стримов.Так что если переходите на Java 16, меняйте все
collect(Collectors.toList()) на toList() – получите большой выигрыш. Если сидите на старых версиях, то можно использовать StreamEx.toList() – он тоже использует SpinedBuffer.#java16 #stream #ljv
This media is not supported in your browser
VIEW IN TELEGRAM
IDEA, я ведь всего лишь написал коротенькую строчку...
Помните суд между Oracle и Google, который длился более 10 лет? Так вот Google выиграл его, окончательно. Верховный суд США поддержал Google, что Java API не может являться объектом защиты авторского права.
Что вы обычно понимаете под термином «технический долг»? Наверное, всякие проблемы в архитектуре, низкое качество кода, плохое покрытие тестами и т.д. И тут как с кредитом – если его не гасить ежемесячно, то будут начисляться штрафы, потом у вас отнимут машину и квартиру, и в конце вы станете банкротом. Причём даже одна одноразовая несвоевременная уплата процента – уже неприятная проблема (кто сталкивался с таким в жизни, хорошо это представляет). Точно так же и с программным проектом – если не гасить техдолг непрерывно, каждый день, с каждым коммитом, то ваш проект будет медленно тонуть и в конце концов помрёт. Да, я убеждён, что гасить долг нужно именно непрерывно. Другие стратегии обречены на провал. Например, «давайте 4 месяца кодить, а потом месяц гасить техдолг» – так не получится, что подтверждено многолетним опытом индустрии.
С архитектурными проблемами и плохим кодом в целом всё понятно. Видите косяк в коде – исправляете. Видите архитектурную проблему – рефакторите. Мало тестов – дописываете новые. Все эти проблемы плавают на поверхности. Но вот есть одна проблема, которая есть, но она далеко не очевидна (по крайней мере не всем). Это то, что вы используете устаревший стек технологий. Это версии библиотек, фреймворков, JDK, OS и т.д. Они очень быстро устаревают. Ну как быстро: если вы зафиксировали версии в pom.xml, и не обновляли их год, то обновиться вы сможете более-менее безболезненно. Если не обновляете два года, то уже придётся попотеть. Три года и более – придётся очень серьёзно так поработать. Вот недавно мы обновили наш стек технологий до последнего, который не обновляли 4 года. Так эту задачу делал я целых 3 месяца!
Старые версии – это точно такой же техдолг, как и плохой код. И я утверждаю, что обновлять версии тоже нужно постоянно и непрерывно. Почему – потому что при обновлении библиотек в любом случае будет что-то ломаться. Что лучше: обновить одну библиотеку и починить только то, что сломалось в ней (исчезло, стало deprecated) или обновить 100 библиотек единовременно, и засесть на месяц, чтобы исправить всё? Ответ очевиден.
Объясню на примере Java. Всё-таки блог про неё.
Вот сидите вы на Java 8 и чувствуете себя превосходно. А тем временем выходит Java 9, 10, 11, 12, 13, 14, 15, 16 и вот уже на подходе 17. Вы думаете: «ну вот 17 выйдет, и тогда, наверное, уже пора обновляться». Поверьте, вы задолбаетесь. Кто переходил с Java 8 на 11, тот меня прекрасно понимает. А вот 8 → 17 это будет прям сущий ад. Вам придётся исправить просто тонну всего. Вот смотрите, что крупного изменилось в версиях Java:
• Java 9: модули, поменялась схема версионирования, исчезли многие проприетарные классы (sun.*), а на те, что остались, теперь нельзя ссылаться в compile-time.
• Java 11: нельзя запускать аплеты, Web Start, удален JavaFX, JAX-WS, JAXB, JAF, CORBA, JTA.
• Java 12: больше нельзя компилировать в Java 6.
• Java 14: удалён CMS, удалены инструменты pack200 и unpack200.
• Java 15: удалён Nashorn.
• Java 16: внутренности JDK стали закрытыми по умолчанию.
• Java 17: внутренности JDK теперь нельзя открыть одним флагом.
А сколько там мелких изменений? Тысячи. Новые предупреждения, куча deprecated API, поведения некоторых методов, кодировки, многих флагов JVM нет или изменились на другие дефолтные значения. И про новые баги в JDK тоже не стоит забывать. И т.д. и т.п.
А ещё при обновлении JDK, скорее всего, придётся обновить и версии библиотек/фреймворков, потому что многие из них просто перестанут работать при апгрейде. Также придётся обновить системы сборки, IDE. Возможно придётся обновить application сервера, докеры и кубернетесы. А может быть даже операционную систему. А потом ещё в течение нескольких месяцев будут постоянно всплывать баги, которые возникли из-за обновления, потому что вы в любом случае не сможете протестировать абсолютно всё. Представили себе весь этот ужас? В общем, работа на несколько месяцев вам гарантированно обеспечена.
#Java11 #Java17
С архитектурными проблемами и плохим кодом в целом всё понятно. Видите косяк в коде – исправляете. Видите архитектурную проблему – рефакторите. Мало тестов – дописываете новые. Все эти проблемы плавают на поверхности. Но вот есть одна проблема, которая есть, но она далеко не очевидна (по крайней мере не всем). Это то, что вы используете устаревший стек технологий. Это версии библиотек, фреймворков, JDK, OS и т.д. Они очень быстро устаревают. Ну как быстро: если вы зафиксировали версии в pom.xml, и не обновляли их год, то обновиться вы сможете более-менее безболезненно. Если не обновляете два года, то уже придётся попотеть. Три года и более – придётся очень серьёзно так поработать. Вот недавно мы обновили наш стек технологий до последнего, который не обновляли 4 года. Так эту задачу делал я целых 3 месяца!
Старые версии – это точно такой же техдолг, как и плохой код. И я утверждаю, что обновлять версии тоже нужно постоянно и непрерывно. Почему – потому что при обновлении библиотек в любом случае будет что-то ломаться. Что лучше: обновить одну библиотеку и починить только то, что сломалось в ней (исчезло, стало deprecated) или обновить 100 библиотек единовременно, и засесть на месяц, чтобы исправить всё? Ответ очевиден.
Объясню на примере Java. Всё-таки блог про неё.
Вот сидите вы на Java 8 и чувствуете себя превосходно. А тем временем выходит Java 9, 10, 11, 12, 13, 14, 15, 16 и вот уже на подходе 17. Вы думаете: «ну вот 17 выйдет, и тогда, наверное, уже пора обновляться». Поверьте, вы задолбаетесь. Кто переходил с Java 8 на 11, тот меня прекрасно понимает. А вот 8 → 17 это будет прям сущий ад. Вам придётся исправить просто тонну всего. Вот смотрите, что крупного изменилось в версиях Java:
• Java 9: модули, поменялась схема версионирования, исчезли многие проприетарные классы (sun.*), а на те, что остались, теперь нельзя ссылаться в compile-time.
• Java 11: нельзя запускать аплеты, Web Start, удален JavaFX, JAX-WS, JAXB, JAF, CORBA, JTA.
• Java 12: больше нельзя компилировать в Java 6.
• Java 14: удалён CMS, удалены инструменты pack200 и unpack200.
• Java 15: удалён Nashorn.
• Java 16: внутренности JDK стали закрытыми по умолчанию.
• Java 17: внутренности JDK теперь нельзя открыть одним флагом.
А сколько там мелких изменений? Тысячи. Новые предупреждения, куча deprecated API, поведения некоторых методов, кодировки, многих флагов JVM нет или изменились на другие дефолтные значения. И про новые баги в JDK тоже не стоит забывать. И т.д. и т.п.
А ещё при обновлении JDK, скорее всего, придётся обновить и версии библиотек/фреймворков, потому что многие из них просто перестанут работать при апгрейде. Также придётся обновить системы сборки, IDE. Возможно придётся обновить application сервера, докеры и кубернетесы. А может быть даже операционную систему. А потом ещё в течение нескольких месяцев будут постоянно всплывать баги, которые возникли из-за обновления, потому что вы в любом случае не сможете протестировать абсолютно всё. Представили себе весь этот ужас? В общем, работа на несколько месяцев вам гарантированно обеспечена.
#Java11 #Java17
Опрос только для работающих программистов из России. Какая у вас зарплата? Чистыми, на руки, в месяц, в рублях.
Final Results
0%
0-29 999
2%
30 000-49 999
2%
50 000-69 999
4%
70 000-99 999
8%
100 000-129 999
11%
130 000-169 999
11%
170 000-209 999
8%
210 000-249 999
12%
250 000+
43%
Не из России / не работаю
| Welcome to JShell -- Version 16
| For an introduction type: /help intro
jshell> import java.lang.reflect.Field;
jshell> import sun.misc.Unsafe;
jshell> // Поля в record являются truly final
jshell> record Point(int x) {}
| created record Point
jshell> Point p = new Point(1);
p ==> Point[x=1]
jshell> // Попробуем заменить p.x на 42
jshell> Field field = Point.class.getDeclaredField("x");
field ==> private final int Point.x
jshell> field.setAccessible(true);
jshell> field.set(p, 42);
| Exception java.lang.IllegalAccessException: Can not set final int field REPL.$JShell$13$Point.x to java.lang.Integer
| at UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException (UnsafeFieldAccessorImpl.java:76)
| at UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException (UnsafeFieldAccessorImpl.java:80)
| at UnsafeQualifiedIntegerFieldAccessorImpl.set (UnsafeQualifiedIntegerFieldAccessorImpl.java:79)
| at Field.set (Field.java:793)
| at (#7:1)
jshell> // Не сработало. Может, можно заменить через Unsafe?
jshell> Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe ==> private static final sun.misc.Unsafe sun.misc.Unsafe.theUnsafe
jshell> theUnsafe.setAccessible(true);
jshell> Unsafe unsafe = (Unsafe) theUnsafe.get(null);
unsafe ==> sun.misc.Unsafe@6073f712
jshell> // Сначала нужно узнать offset поля Point.x
jshell> long offset = unsafe.objectFieldOffset(field);
| Exception java.lang.UnsupportedOperationException: can't get field offset on a record class: private final int REPL.$JShell$13$Point.x
| at Unsafe.objectFieldOffset (Unsafe.java:648)
| at (#11:1)
jshell> // Тоже не сработало. Это тоже учли. Но зачем нам offset, ведь мы и так знаем, что он равен 12?
jshell> unsafe.putIntVolatile(p, 12, 42);
jshell> p
p ==> Point[x=42]
jshell> // Сработало!👍1
Как-то незаметно вышла JDK 16.0.1. Почти никто не написал. Вот, исправляю:
https://www.oracle.com/java/technologies/javase/16-0-1-relnotes.html
http://jdk.java.net/16/
#java16
https://www.oracle.com/java/technologies/javase/16-0-1-relnotes.html
http://jdk.java.net/16/
#java16
Сколько у вас репутации на StackOverflow?
Final Results
43%
Нет аккаунта
29%
< 10
8%
[10, 100)
9%
[100, 500)
6%
[500, 2k)
4%
[2k, 10k)
0%
[10k, 20k)
1%
[20k, 50k)
0%
≥ 50k