Беше пусната публичната версия на Java 22. Тази версия включваше около 2300 затворени задачи и 12 JEP. Бележките по изданието могат да се видят тук. Пълният списък с промените в API е тук.
Java 22 не е LTS версия и ще има актуализации само за шест месеца (до септември 2024 г.).
Можете да изтеглите JDK 22 от тези връзки:
Нека да разгледаме всички JEP, които бяха включени в Java 22.
Ненаименуваните променливи и шаблони, които бяха въведени в режим на визуализация в Java 21, сега са постоянна езикова конструкция.
Неназована променлива е променлива, която е обозначена от автора като неизползвана и е обозначена със знака за долна черта (_).
Неизползваните променливи са доста често срещани на практика:
В горния пример фактът, че елементът съществува е важен, но самата променлива не е необходима. Затова за нея вместо име е избран знак за подчертаване. Друг пример:
Тук бяха необходими само координатите x и y, така че неназована променлива беше изрично избрана за третата координата, за да се покаже ясно, че тя не се използва.
Често срещан случай на необходимост от ненаименувани променливи са неуловени изключения в catch блок:
Важното тук е, че има изключение, но не и самото изключение.
опитайте с ресурс:
И, разбира се, неизползвани параметри на ламбда изрази:
Във всички примери по-горе използването на долната черта прави кода по-кратък и по-четлив, прави намерението на автора ясно и намалява възможността за грешка. Той също така помага на инструментите за статичен анализ, които могат да се оплакват от неизползвани променливи.
Не само променливите, но и моделите могат да бъдат безименни:
По същия начин можете да извлечете само цвета, ако имате нужда само от него, но не и координатите:
Възможно е също така да се декларират неименувани променливи на модел:
Кодът по-горе може да бъде съкратен чрез сливане на първите два клона на case в едно:
Обърнете внимание, че такова сливане не би било възможно без използването на ненаименувани шаблони, тъй като преди няколко модела в един клон бяха разрешени само ако в тях нямаше променливи шаблони.
По-сложен пример с вложени шаблони, където има както ненаименувани шаблони, така и променливи на ненаименован шаблон:
Като цяло съпоставянето на шаблони и неназованите шаблони заедно имат страхотна синергия и ви позволяват да пишете наистина мощни, компактни и изразителни дизайни.
Java стартерът вече може да изпълнява програми, състоящи се от множество изходни файлове на Java.
Нека си припомним, че по-рано в Java 11 стана възможно да се изпълняват програми, състоящи се от един файл, без необходимост от самокомпилация (JEP 330):
Такъв файл може да бъде стартиран просто като напишете:
И сега тази функция е разширена до произволен брой файлове:
Ако горната програма се изпълнява чрез java Prog.java, тогава Java ще компилира Prog класа в паметта и ще изпълни неговия основен метод. Тъй като класът Prog препраща към класа Helper, Java ще го намери във файла Helper.java и ще го компилира също. По този начин програма, разделена на два файла, ще работи точно както ако всички класове са поставени в един изходен файл. Този алгоритъм може да бъде разширен до произволен брой файлове. Например, ако Helper препраща към друг клас HelperAux, файлът HelperAux.java ще бъде намерен и компилиран.
Възможността за стартиране на програми, състоящи се от множество изходни файлове без отделна стъпка на компилация, може да бъде много полезна. Основно това може да бъде полезно за бързо създаване на прототипи или в ранните етапи на проекти, когато проектът все още не е придобил повече или по-малко стабилна форма. В такива случаи разработчикът има възможност да пропусне етапа на настройка на компилацията на проекта и веднага да започне да пише код, без да се ограничава до един изходен файл (което би било необходимо преди Java 22). За някои прости проекти тази конфигурация за стартиране без инструменти за изграждане може дори да остане постоянна.
Програми, които използват библиотеки под формата на вече компилирани jar файлове, също могат да бъдат стартирани директно. Например, ако има структура:
Тогава такава програма може да бъде стартирана с помощта на опцията --class-path (или -cp):
Шаблоните за низове, които се появиха в режим на преглед в Java 21, преминават към втория кръг на преглед без промени.
Шаблоните за низове са нова синтактична функция, която ви позволява да вграждате изрази в низове:
Тоест, това е интерполация на низове, която отдавна присъства в много други добре познати езици за програмиране.
Реализацията на низови шаблони в Java е различна от повечето реализации в други езици: в Java, низовият шаблон всъщност първо се превръща в обект java.lang.StringTemplate и след това процесор, изпълняващ java.lang.StringTemplate.Processor, преобразува този обект в низ или обект в друг клас (забележка: в момента има дискусии относно изоставянето на идеята за процесори и оставянето само на StringTemplate). В горния пример STR."..." не е нищо повече от съкратена версия на следния код:
STR е стандартният и най-често използван процесор, който извършва просто заместване на стойност в шаблон и връща конкатениран низ. STR се импортира имплицитно във всеки файл източник, така че може да се използва без импортиране.
RAW е процесор, който не прави нищо с StringTemplate и просто го връща. Обикновено не се използва, защото... на практика малко хора се нуждаят от необработени представяния на шаблони, а по-скоро резултати от интерполация под формата на готови обекти.
Процесорите бяха въведени, за да позволят персонализиране на процеса на интерполация. Например, друг стандартен FMT процесор поддържа форматиране с помощта на спецификатори, дефинирани в java.util.Formatter:
Не се изисква процесорите да връщат String. Ето общия подпис на метода process() на интерфейса на процесора:
Това означава, че е възможно да се внедри процесор, който да прави почти всичко и да връща всичко. Например, хипотетичен JSON процесор би създал JSON обекти директно (без междинен String обект) и все още поддържа екраниране на кавички:
Ако име, телефон или адрес съдържат кавички, те няма да повредят обекта, т.к Процесорът ще замени " с \".
Или, например, SQL процесорът ще създаде PreparedStatements, защитавайки срещу SQL Injection атаки:
По този начин моделите на низове са много по-мощен инструмент от обикновената интерполация на низове. Те решават не само проблема с лесното вграждане на изрази в низове и увеличават четливостта, но също така подобряват сигурността и гъвкавостта на програмите.
В режим на предварителен преглед вече е възможно да напишете кодови инструкции в конструктора, преди изрично да извикате конструктора (super() или this()):
Може да изглежда, че точно същият код може да бъде написан с помощта на класическия ExecutorService и submit(), но StructuredTaskScope има няколко основни разлики, които правят кода по-безопасен:
Structured Concurrency би трябвало да улесни писането на безопасни за нишки програми благодарение на познатия структуриран подход.
Стойностите с обхват, които станаха предварителен преглед в Java 21, като Structured Concurrency, преминават към втория преглед без промени. Преди това стойностите с обхват бяха инкубирани в Java 20.
Новият клас ScopedValue ви позволява да обменяте неизменни данни, без да ги предавате през аргументи на метода. Това е алтернатива на съществуващия клас ThreadLocal.
Класовете ThreadLocal и ScopedValue са сходни по това, че решават един и същ проблем: предават стойността на променлива в нишка (или дърво на нишка) от едно място на друго, без да използват изричен параметър. В случая на ThreadLocal това се прави чрез извикване на метода set(), който задава стойността на променливата за дадена нишка, а след това методът get() се извиква от някъде другаде, за да получи стойността на променливата. Този подход има редица недостатъци:
Тези проблеми се изострят от появата на виртуални нишки, които могат да бъдат създадени в много по-голям брой от обикновените нишки.
ScopedValue няма горните недостатъци. За разлика от ThreadLocal, ScopedValue няма метод set(). Стойността се свързва с обекта ScopedValue чрез извикване на друг метод where(). След това се извиква методът run(), по време на който тази стойност може да бъде получена (чрез метода get(), но не може да бъде променена. След като методът run() завърши изпълнението, стойността се освобождава от обекта ScopedValue. Тъй като стойността не се променя, проблемът със скъпото наследяване също е решен: дъщерните нишки не трябва да копират стойност, която остава постоянна през целия живот.
Пример за използване на ScopedValue:
Като цяло ScopedValue е предпочитан заместител на ThreadLocal, защото налага на разработчика сигурен еднопосочен модел на работа с неизменни данни. Този подход обаче не винаги е приложим за някои проблеми и за тях ThreadLocal може да е единственото възможно решение.
API за външни функции и памет, който дълго време беше в режим на предварителен преглед (а преди това още по-дълго в инкубатора), най-накрая се стабилизира.
Основната цел на FFM API е да замени наследения JNI, който е опасно и крехко средство за извикване на собствени библиотеки и обработка на собствени данни. API на FFM, от друга страна, е проектиран да бъде безопасно, удобно, четимо и ефективно средство за взаимодействие със среда, различна от Java.
FFM API е в пакета java.lang.foreign. Състои се от две части: API за достъп до външна памет (чужда памет) и API за извикване на външни функции (чужди функции).
API за достъп до външна памет предоставя класове и интерфейси, които ви позволяват да разпределяте, освобождавате и манипулирате външна памет: MemorySegment, Arena, SegmentAllocator, MemoryLayout. Той също така използва вече съществуващия клас VarHandle. API за извикване на външни функции предоставя класове и интерфейси
Линкер, SymbolLookup, FunctionDescriptor. За директни повиквания се използва обичайният MethodHandle.
Ето кратък пример за използване на FFM API, където кодът на Java получава MethodHandle за функцията radixsort, написана на C, и я извиква, за да сортира масив от 4 низа:
Този код е много по-чист и по-прозрачен от всяко решение, използващо JNI.
Повечето от API на FFM са защитени по подразбиране. Много задачи, които преди изискваха писане на естествен код, извикан чрез JNI, сега могат да бъдат решени чрез писане само на Java код. API на FFM обаче има ограничени методи (като MemorySegment::reinterpret), които по своята същност не са безопасни. При използването им могат да възникнат ужасни последствия, като JVM срив, който виртуалната машина не може да предотврати. Следователно, когато изпълнява ограничен метод, JVM издава предупреждение, например:
За да позволите на модул да използва ограничени методи без предупреждения, трябва да използвате опцията на командния ред --enable-native-access=M, където M е името на модула или разделен със запетаи списък с модули (можете да използвате ALL- НЕНАЗВАН за целия код в класния път). Въпреки това, всяко използване на ограничени методи извън списъка с модули ще хвърли IllegalCallerException.
Векторният API в модула jdk.incubator.vector, който се появи вече в Java 16, остава в състояние на инкубация за седми път. Тази версия съдържа само незначителни промени в API, корекции на грешки и подобрения в производителността.
Векторният API остава в инкубатора толкова дълго, защото зависи от някои характеристики на проекта Valhalla (главно стойностни класове), който все още е в процес на разработка. Веднага след като тези функции станат достъпни във форма за предварителен преглед, векторният API също незабавно ще остави инкубатора в състояние на предварителен преглед.
Колекторът за боклук G1 е внедрил закрепване на региона, което предотвратява деактивирането на събирането на боклук, докато JNI е в критичен регион.
Критичният регион е кодът, който се изпълнява между две събития: получаване на указател към Java обект и освобождаването му. През този период събирачът на боклук няма право да премества обекта на Java, за да не наруши собствения код, който разчита той да бъде на същия адрес през цялото време на заснемане.
Преди Java 22 G1 имаше проста стратегия: ако поне една от нишките беше в критичен регион, тя просто изключваше събирането на боклук. Това може да доведе до различни проблеми, вариращи от дълги паузи до липса на памет, когато всъщност има излишък от нея.
За да закачите критични обекти, не е необходимо напълно да деактивирате колектора за боклук; достатъчно е да закачите само областта на колектора, в която се намира обектът. Това беше приложено в JEP 423. Това беше направено чрез използване на брояч, който се увеличава, когато критичен обект е уловен и намалява, когато бъде освободен. Ако броячът е нула, тогава регионът се сглобява в нормален режим. Ако броячът е по-голям от нула, тогава областта на колектора е фиксирана. Това трябва да реши проблемите, описани по-горе.
bbabo.Net