გამოვიდა Java 27
გამოვიდა Java 27 -ის საჯარო ვერსია. ამ რელიზში შევიდა დაახლოებით 2500 დახურული ამოცანა და 9 JEP-ი . Release Notes შეგიძლიათ იხილოთ აქ . API-ს ცვლილებების სრული სია – აქ . Java 27 არ წარმოადგენს LTS-რელიზს და მისი განახლებები გამოვა მხოლოდ ნახევარი წლის განმავლობაში (2027 წლის მარტამდე). JDK 27-ის…
გამოვიდა Java 27-ის საჯარო ვერსია. ამ რელიზში შევიდა დაახლოებით 2500 დახურული ამოცანა და 9 JEP-ი. Release Notes შეგიძლიათ იხილოთ აქ. API-ს ცვლილებების სრული სია – აქ.
Java 27 არ წარმოადგენს LTS-რელიზს და მისი განახლებები გამოვა მხოლოდ ნახევარი წლის განმავლობაში (2027 წლის მარტამდე).
JDK 27-ის ჩამოტვირთვა შესაძლებელია შემდეგი ბმულებიდან:
- Oracle JDK (ლიცენზია NFTC)
- OpenJDK (ლიცენზია GPLv2 with Classpath Exception)
განვიხილოთ ყველა JEP-ი, რომელიც Java 27-ში შევიდა.
Primitive Types in Patterns, instanceof, and switch (Fifth Preview) (JEP 532)
პრიმიტიული ტიპები პატერნებში, instanceof-სა და switch-ში, რომლებიც preview რეჟიმში იყო Java 23-ში, Java 24-ში, Java 25-სა და Java 26-ში, უცვლელად რჩება მეხუთე preview-ზე.
ახალი ფუნქციონალის არსი მდგომარეობს პრიმიტიული ტიპების მხარდაჭერაში პატერნებსა და instanceof / switch ოპერატორებში:
// --enable-preview --source 27
Object obj = 42;
if (obj instanceof int i) { // matches
System.out.println("int: " + i);
}
switch (obj) {
case int i -> System.out.println("int: " + i); // matches
case double d -> System.out.println("double: " + d);
default -> System.out.println("other");
}ასევე შესაძლებელია იმის შემოწმებაც, ხვდება თუ არა მნიშვნელობები ტიპის დიაპაზონში:
int i = 42;
if (i instanceof byte b) { // matches
System.out.println("byte: " + b);
}double d = 3.0;
switch (d) {
case int i -> System.out.println("int: " + i); // matches
case float f -> System.out.println("float: " + f);
default -> System.out.println("other");
}ზემოთ მოცემულ მაგალითებში 42 ხვდება byte-ის დიაპაზონში ([-128; 127]), ხოლო 3.0 სიზუსტის დაკარგვის გარეშე გარდაიქმნება int-ად. ამგვარად, ეს საშუალებას მოგვცემს უფრო უსაფრთხოდ გარდავქმნათ ერთი რიცხვითი ტიპი მეორეში დიაპაზონების ხელით შემოწმების გარეშე.
მსგავსი შემოწმებები შეიძლება სასარგებლო იყოს ჩანაწერების (records) პატერნებშიც:
record JsonNumber(double d) {}
var json = new JsonNumber(3.0);
if (json instanceof JsonNumber(int i)) { // matches
// ...
}თუ Java 23-27-მდე switch-ში სელექტორი-გამოსახულებების ტიპები შეიძლებოდა ყოფილიყო მხოლოდ int, short, byte და char და მათთვის მხოლოდ მუდმივი შტოები (case 3 და მისთ.) იყო მხარდაჭერილი, ახლა უკვე ყველა პრიმიტიული ტიპია მხარდაჭერილი და შტოები შეიძლება იყოს პატერნები:
float f = 1.0f;
switch (f) {
case 0f -> System.out.println("0");
case float x when x == 1f -> System.out.println("1"); // matches
case float x -> System.out.println("other");
}
boolean b = "hello".isEmpty();
switch (b) {
case true -> System.out.println("empty");
case false -> System.out.println("non-empty"); // matches
}Lazy Constants (Third Preview) (JEP 531)
ზარმაცი მუდმივების (Lazy Constants) API, რომელიც preview რეჟიმში იყო Java 25-სა და Java 26-ში, მესამედ რჩება preview-ში. ამ რელიზში განხორციელდა შემდეგი ცვლილებები:
- წაიშალა მეთოდები [
LazyConstant::isInitialized](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/LazyConstant.html#isInitialized\(\)) და [LazyConstant::orElse](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/LazyConstant.html#orElse\(T\)). - დაემატა სტატიკური მეთოდი [
Set::ofLazy](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/Set.html#ofLazy\(java.util.Set,java.util.function.Predicate\)).
გავიხსენოთ ახალი ფუნქციონალის არსი. დასაწყისისთვის გავიხსენოთ, თუ როგორ შეიძლება Java-ში გადადებული ინიციალიზაციის რეალიზება კლასიკური საშუალებებით:
class OrderController {
private Logger logger = null;
Logger getLogger() {
if (logger == null) {
logger = Logger.create(OrderController.class);
}
return logger;
}
void submitOrder(User user, List<Product> products) {
getLogger().info("order started");
...
getLogger().info("order submitted");
}
}ზემოთ მოცემულ მაგალითში logger ობიექტის ინიციალიზაცია ხდება პირველი მიმართვისას. ასეთ მიდგომას რამდენიმე პრობლემა აქვს:
- ველთან
loggerნებისმიერი წვდომა უნდა მოხდესgetLogger()მეთოდის გავლით. ეს შეიძლება ვინმეს დაავიწყდეს. - კოდი არ არის ნაკადურად უსაფრთხო (thread-safe):
loggerობიექტი შეიძლება რამდენჯერმე ინიციალიზდეს. - კომპილატორს არ შეუძლია constant folding-ის ოპტიმიზაციის გამოყენება, რადგან ველი
loggerარ არის final.
ზემოთ ჩამოთვლილი პრობლემების ნაწილობრივ არიდება შესაძლებელია სხვა, უფრო რთული იდიომების გამოყენებით, როგორიცაა double-checked locking ან class holder. თუმცა double-checked locking-ის დროს კოდი წარმოუდგენლად მოცულობითი და მყიფე ხდება (მაგალითად, შეიძლება დაგვავიწყდეს საკვანძო სიტყვა volatile-ის ჩასმა), ასევე არ გვაქვს constant folding-ი. class holder-ით კოდი მეტ-ნაკლებად მარტივი და საიმედო ხდება (და constant folding-იც მუშაობს), მაგრამ ამ იდიომას სერიოზული შეზღუდვები აქვს: ის მხოლოდ სტატიკური ველებისთვის გამოიყენება და თითოეული ველისთვის საკუთარი კლასის გამოცხადება გვიწევს. ასევე შეიძლება ConcurrentHashMap-ის გამოყენება, თუმცა მასაც აქვს ნაკლოვანებები: არ გააჩნია constant folding-ი და ჩნდება პრობლემები, თუ ფუნქცია აბრუნებს null-ს.
ახლა ვნახოთ, როგორ გამოიყურება კოდი ახალი ინტერფეისით LazyConstant:
// --enable-preview --source 27
class OrderController {
private final LazyConstant<Logger> logger
= LazyConstant.of(() -> Logger.create(OrderController.class));
void submitOrder(User user, List<Product> products) {
logger.get().info("order started");
...
logger.get().info("order submitted");
}
}ახლა ლოგერის მისაღებად გამოიყენება LazyConstant ობიექტი, რომელიც საკუთარ თავში ახდენს ლოგერის გამოთვლის ზარმაცი ლოგიკის ინკაფსულაციას. get() მეთოდის პირველი გამოძახებისას შიგთავსი გამოითვლება გადაცემული Supplier-ის გამოძახებით. ხოლო თუ შიგთავსი უკვე გამოთვლილია, ის უბრალოდ ბრუნდება. LazyConstant იძლევა გარანტიას, რომ Supplier ერთზე მეტჯერ არ გამოიძახება, რითაც უზრუნველყოფს ნაკადურ უსაფრთხოებას.
შიდა დონეზე LazyConstant რეალიზებულია ისე, რომ იყენებს JDK-ის შიდა ანოტაციას @Stable შიგთავსის შესანახად ველში, რომელიც არ არის final. ეს ანოტაცია ვირტუალურ მანქანას აძლევს სიგნალს, რომ ველი ერთზე მეტჯერ არ შეიცვლება, რაც იმას ნიშნავს, რომ მინიჭების შემდეგ ვირტუალურ მანქანას შეუძლია ის მუდმივ მნიშვნელობად ჩათვალოს, რაც ხსნის constant folding-ის შესაძლებლობას. ამგვარად, LazyConstant საშუალებას იძლევა მივაღწიოთ ინიციალიზაციის მოქნილობასაც და კარგ წარმადობასაც.
API ასევე იძლევა საშუალებას შეიქმნას არა მხოლოდ ერთეული შიგთავსის მქონე მნიშვნელობები, არამედ ზარმაცი სიები და ლექსიკონებიც. მოვიყვანოთ ზარმაცი სიის მაგალითი:
// --enable-preview --source 27
class Application {
private static final List<OrderController> ORDERS
= List.ofLazy(POOL_SIZE, _ -> new OrderController());
public static OrderController orders() {
long index = Thread.currentThread().threadId() % POOL_SIZE;
return ORDERS.get((int)index);
}
}ზემოთ მოყვანილ მაგალითში სია ORDERS არის სია, რომელიც თითოეული ინდექსისთვის მნიშვნელობას ითვლის მიმართვის მომენტში და არაუმეტეს ერთხელ. ამგვარად, LazyConstant ასევე კარგი ვარიანტია ქეშების დასაწერად.
Java 28-ში გამოვა ზარმაცი მუდმივების მეოთხე preview.
Structured Concurrency (Seventh Preview) (JEP 533)
Structured Concurrency, რომელიც preview რეჟიმში იყო Java 21-ში, Java 22-ში, Java 23-ში, Java 24-ში, Java 25-სა და Java 26-ში, preview რეჟიმში რჩება მეშვიდედ. ამ რელიზში შევიდა API-ს შემდეგი ცვლილებები:
StructuredTaskScopeდაJoinerინტერფეისებს დაემატა კიდევ ერთი პარამეტრიR_Xგამონაკლისის ტიპისთვის, რომელიც მეთოდ [join()](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#join\(\))-ს შეუძლია ისროლოს.- გაჩნდა სტატიკური მეთოდის [
StructuredTaskScope::open](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#open\(java.util.function.UnaryOperator\)) ახალი გადატვირთვა ერთი პარამეტრითUnaryOperator(მანამდე არსებობდა მეთოდის მხოლოდ ორპარამეტრიანი ვერსია:JoinerდაUnaryOperator). - მეთოდები [
Joiner::allSuccessfulOrThrow](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#allSuccessfulOrThrow\(\)), [Joiner::anySuccessfulOrThrow](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#anySuccessfulOrThrow\(\)) და [Joiner::awaitAllSuccessfulOrThrow](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#awaitAllSuccessfulOrThrow\(\)) ახლა ქმნიან ისეთJoiner-ებს, რომლებიც გამონაკლისის წარმოქმნის შემთხვევაში ისვრიანExecutionException-ს. ასევე მათ დაემატათ გადატვირთვები, რომლებიც იღებენFunction-ს, რაც გამონაკლისის სხვა ტიპის მითითების საშუალებას იძლევა. - სტატიკური მეთოდი [
Joiner::awaitAll](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#awaitAll\(\)) წაიშალა. voidმეთოდი [Joiner::onTimeout](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#onTimeout\(\)) შეიცვალა მეთოდით [Joiner:timeout](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#timeout\(\)), რომელიც ტაიმაუტის შემთხვევაში შედეგის მითითების საშუალებას იძლევა.
შეგახსენებთ, რომ Structured Concurrency მრავალნაკადიანი პროგრამირების მიდგომაა, რომელიც ერთნაკადიანი სტრუქტურული პროგრამირებიდან სესხულობს პრინციპებს. ასეთი მიდგომის მთავარი იდეა შემდეგში მდგომარეობს: თუ ამოცანა იყოფა რამდენიმე კონკურენტულ ქვეამოცანად, ეს ქვეამოცანები კვლავ ერთიანდება მთავარი ამოცანის კოდის ბლოკში. ყველა ქვეამოცანა ლოგიკურად დაჯგუფებული და ორგანიზებულია იერარქიაში. თითოეული ქვეამოცანის სიცოცხლის ხანგრძლივობა შემოფარგლულია მთავარი ამოცანის კოდის ბლოკის ხილვადობის არით.
ახალი API-ს ცენტრშია კლასი StructuredTaskScope, რომელსაც ორი ძირითადი მეთოდი გააჩნია:
- [
fork()](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#fork\(java.util.concurrent.Callable\)) – ქმნის ქვეამოცანას და უშვებს მას ახალ ვირტუალურ ნაკადში, - [
join()](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#join\(\)) – ელოდება ყველა ქვეამოცანის დასრულებას ან scope-ის [დახურვას](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#close\(\)).
StructuredTaskScope-ის გამოყენების მაგალითი, სადაც ნაჩვენებია ამოცანა, რომელიც პარალელურად უშვებს ორ ქვეამოცანას და ელოდება მათი შესრულების შედეგს:
// --enable-preview --source 27
try (var scope = StructuredTaskScope.open()) {
Subtask<String> user = scope.fork(() -> findUser());
Subtask<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // Join subtasks, propagating exceptions
// Both subtasks have succeeded, so compose their results
return new Response(user.get(), order.get());
}შესაძლოა მოგეჩვენოთ, რომ ზუსტად ანალოგიური კოდის დაწერა შეიძლებოდა კლასიკური ExecutorService-ისა და [submit()](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/ExecutorService.html#submit\(java.lang.Runnable\))-ის გამოყენებით, თუმცა StructuredTaskScope-ს გააჩნია რამდენიმე პრინციპული განსხვავება, რომლებიც კოდს უფრო უსაფრთხოს ხდის:
- ქვეამოცანების ყველა ნაკადის სიცოცხლის ხანგრძლივობა შეზღუდულია
try-with-resourcesბლოკის ხილვადობის არით. მეთოდი [close()](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#close\(\)) გარანტირებულად არ დასრულდება, სანამ ყველა ქვეამოცანა არ დასრულდება. - თუ
findUser()დაfetchOrder()ოპერაციებიდან ერთ-ერთი შეცდომით სრულდება, მეორე ოპერაცია ავტომატურად უქმდება, თუ ის ჯერ არ დასრულებულა (ნაგულისხმევიJoiner-ის, [awaitAllSuccessfulOrThrow()](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Joiner.html#awaitAllSuccessfulOrThrow\(\))-ის გამოყენების შემთხვევაში, თუმცა შესაძლებელია სხვების გამოყენებაც განსხვავებული ქცევით). - თუ მთავარი ნაკადი წყდება
join()-ის მოლოდინის პროცესში, ორივე ოპერაცია —findUser()დაfetchOrder()— უქმდება ბლოკიდან გამოსვლისას. - ნაკადების დამპში გამოჩნდება იერარქია: ნაკადები, რომლებიც ასრულებენ
findUser()-სა დაfetchOrder()-ს, გამოჩნდება მთავარი ნაკადის შვილობილ ნაკადებად.
Structured Concurrency-მ ნაცნობი სტრუქტურული მიდგომის წყალობით უნდა გააადვილოს უსაფრთხო მრავალნაკადიანი პროგრამების წერა.
Java 28-ში Structured Concurrency მუდმივი API გახდება.
Compact Object Headers by Default (JEP 534)
ობიექტების კომპაქტური სათაურები, რომლებიც გამოჩნდა Java 25-ში (Java 24-ში – ექსპერიმენტულ რეჟიმში), ახლა ნაგულისხმევად არის ჩართული. ამრიგად, პარამეტრი -XX:+UseCompactObjectHeaders აღარ არის საჭირო. ასევე კომპაქტური სათაურების გამორთვა შესაძლებელია Java-ს საპირისპირო პარამეტრით გაშვებით:
$ java -XX:-UseCompactObjectHeaders ...კომპაქტური სათაურები პროექტ Lilliput-ში მუშაობის შედეგია. ახლა JVM-ში ობიექტების სათაურების ზომა 64-ბიტიან პლატფორმებზე 96/128 ბიტიდან 64 ბიტამდე შემცირდა. კომპაქტური სათაურები არა მხოლოდ ამცირებს heap-ის ზომას, არამედ შეუძლია გააუმჯობესოს წარმადობაც ახალი ობიექტების გამოყოფის უფრო მაღალი სიჩქარის, GC-ზე დაბალი დატვირთვისა და მონაცემთა უკეთესი ლოკალურობის წყალობით.
სათაურების შეკუმშვა მიიღწევა mark-სიტყვის (64 ბიტი) და class-სიტყვის (64 ან 32 ბიტი, თუ ჩართულია შეკუმშული კლასის მაჩვენებლები) ერთ 64-ბიტიან სიტყვაში გაერთიანებით. ახალ სქემაში კლასის მაჩვენებლები ყოველთვის შეკუმშულია და მათთვის ბიტების რაოდენობა 32-დან 22-მდე მცირდება. Identity ჰეშ-კოდი უცვლელი რჩება: 31 ბიტი. თეგ-ბიტების რაოდენობა ერთით იზრდება (GC self forwarding-ისთვის). GC ასაკის ბიტები კვლავ 4 რჩება, როგორც იყო. ასევე 4 ბიტი რეზერვირებულია Valhalla-სთვის.
პროექტ Lilliput-ზე მუშაობა არ დასრულებულა. მომავალში შესაძლებელია სათაურების კიდევ უფრო მეტად — 32 ბიტამდე შეკუმშვა, რაც მეხსიერების მოხმარებას კიდევ უფრო შეამცირებს.
Make G1 the Default Garbage Collector in All Environments (JEP 523)
ნაგვის შემგროვებელი G1 ნაგულისხმევად ჩართული გახდა ყველა გარემოში. მანამდე გარემოებში ერთი პროცესორით ან მცირე მოცულობის ოპერატიული მეხსიერებით (< 1792 მბ) ავტომატურად ირთვებოდა Serial GC. ეს იმიტომ ხდებოდა, რომ ასეთ პირობებში Serial GC-ს უპირატესობა ჰქონდა გამტარუნარიანობასა და მოხმარებული მეხსიერების რაოდენობაში. თუმცა ბოლო წლებში G1-მა წარმადობის კარგ დონეს მიაღწია, ამიტომ ასეთ გადართვას პრაქტიკულად აღარ აქვს აზრი. თუ თქვენს კონკრეტულ შემთხვევაში ნაგვის შემგროვებელ Serial GC-ს კვლავ უკეთესი წარმადობა აქვს, მისი ჩართვა შესაძლებელია -XX:+UseSerialGC პარამეტრით.
JFR In-Process Data Redaction (JEP 536)
JDK Flight Recorder-ში გაჩნდა სენსიტიური მონაცემების რედაქტირების შესაძლებლობა, რათა ისინი ჩანაწერის საბოლოო ფაილებში ღია სახით არ მოხვდეს. ასეთ მონაცემებს განეკუთვნება პაროლები, ტოკენები, საიდუმლო გასაღებები და სხვა მსგავსი კონფიდენციალური მონაცემები. რედაქტირება შესაძლებელია სამი სახის ატრიბუტისთვის: ბრძანებათა სტრიქონის არგუმენტები, გარემოს ცვლადები და სისტემური თვისებები. რედაქტირებისთვის კონკრეტული ატრიბუტების მითითების მიზნით, ოფციას -XX:FlightRecorderOptions დაემატა ორი ახალი ქვეოფცია: redact-argument (ბრძანებათა სტრიქონის არგუმენტებისთვის) და redact-key (გარემოს ცვლადებისა და სისტემური თვისებებისთვის). მაგალითად, ასე შეიძლება გამოიყურებოდეს JFR-ის გაშვება, თუ საჭიროა confidential ან CONFIDENTIAL სახელის მქონე ცვლადებისა და თვისებების, ასევე მომხმარებლის სახელითა და პაროლით შედგენილი URL-ების დაფარვა:
$ export CONFIDENTIAL=SOME_SECRET
$ java -XX:FlightRecorderOptions:'redact-key=confidential,redact-argument=https://*:*@*' \
-XX:StartFlightRecording:filename=dump.jfr \
-Dconfidential=ANOTHER_SECRET \
-jar application.jar https://john:[email protected]/login --verboseგაშვების შემდეგ JFR ფაილში სენსიტიური მონაცემები ჩანაცვლდება [REDACTED]-ით, მაგალითად, ივენთი jdk.JVMInformation ასე გამოიყურება:
jdk.JVMInformation {
startTime = 17:39:02.196 (2026-02-15)
jvmVersion = "Java HotSpot(TM) 64-Bit Server VM"
jvmArguments = "-Dconfidential=[REDACTED]
-XX:FlightRecorderOptions:redact-key=confidential,redact-argument=[REDACTED]
-XX:StartFlightRecording:filename=dump.jfr"
jvmFlags = "N/A"
javaArguments = "-jar application.jar [REDACTED] --verbose"
jvmStartTime = 17:39:02.050 (2026-02-15)
pid = 43671
}თუ პირდაპირ არ მიუთითებთ redact-argument-ს ან redact-argument-ს, გამოიყენება ნაგულისხმევი ფილტრები. მათ სიაში შედის ისეთი გავრცელებული პატერნები, როგორიცაა *passwd*, *password*, *credential*, *secret*, *token* და სხვა.
PEM Encodings of Cryptographic Objects (Third Preview) (JEP 538)
კრიპტოგრაფიული ობიექტების PEM ფორმატში კოდირებისა და უკან დეკოდირების API, რომელიც preview რეჟიმში იყო Java 25-სა და Java 26-ში, მესამე preview-ზე რჩება. ამ რელიზში არის API-ს რამდენიმე ცვლილება:
PEMახლა ჩვეულებრივი კლასია და არა ჩანაწერი, და მას დაემატა კონსტრუქტორები, რომლებიც Base64-ს ბაიტების მასივის სახით იღებენ.- ინტერფეისს
DEREncodableშეეცვალა სახელი და გახდაBinaryEncodable. - მეთოდები [
EncryptedPrivateKeyInfo::getKey](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/javax/crypto/EncryptedPrivateKeyInfo.html#getKey\(java.security.Key\)) და [EncryptedPrivateKeyInfo::getKeyPair](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/javax/crypto/EncryptedPrivateKeyInfo.html#getKeyPair\(java.security.Key\)), რომლებიც მეორე არგუმენტადProvider-ს იღებდნენ, ახლა მხოლოდ ერთ არგუმენტს,Key-ს იღებენ. - მეთოდს [
PEMDecoder::withFactory](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/security/PEMDecoder.html#withFactory\(java.security.Provider\)) შეეცვალა სახელი და გახდა [PEMDecoder::withFactoriesOf](https://docs.oracle.com/en/java/javase/27/docs/api/java.base/java/security/PEMDecoder.html#withFactoriesOf\(java.security.Provider\)). - გაჩნდა გამონაკლისის ახალი ტიპი
CryptoException.
შეგახსენებთ, რომ PEM ფორმატში კოდირების ახალი API საშუალებას იძლევა დაკოდირდეს ყველაზე მრავალფეროვანი კრიპტოგრაფიული ობიექტები: ღია გასაღებები, დახურული გასაღებები, სერტიფიკატები და ა.შ.
აი, PEM ფორმატში დაკოდირებული ღია გასაღების მაგალითი:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u
cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY-----ასეთი გასაღების დეკოდირება შესაძლებელია ახალი კლასის, java.security.PEMDecoder-ის მეშვეობით:
// --enable-preview --source 27
PEMDecoder decoder = PEMDecoder.of();
PublicKey key = (PublicKey) decoder.decode(data);
IO.println(key);კოდირება ხდება java.security.PEMEncoder კლასის დახმარებით:
// --enable-preview --source 27
PEMEncoder encoder = PEMEncoder.of();
String data = encoder.encodeToString(key);
IO.println(data);ყველა იმ კრიპტოგრაფიული ობიექტის სია, რომელთა კოდირება/დეკოდირებაც შესაძლებელია, შემოიფარგლება ახალი sealed ინტერფეისის, java.security.BinaryEncodable-ის მემკვიდრეებით:
public sealed interface BinaryEncodable permits AsymmetricKey, KeyPair,
PKCS8EncodedKeySpec, X509EncodedKeySpec, EncryptedPrivateKeyInfo,
X509Certificate, X509CRL, PEM, InternalBinaryEncodable {
}მემკვიდრეებს შორის გამოირჩევა განსაკუთრებული კლასი java.security.PEM. ამ კლასს შეუძლია ნებისმიერი PEM მონაცემი შეიცავდეს. ის შეიძლება გამოგვადგეს მაშინ, როდესაც Java-ში კრიპტოგრაფიული ობიექტისთვის შესაბამისი API არ არსებობს (მაგალითად, PKCS #10 სერტიფიკატის მოთხოვნა):
public final class PEM implements BinaryEncodable {
String type(); // Cryptographic object type, from the header text
// (e.g., "PRIVATE KEY")
byte[] content(); // Base64-encoded PEM content
byte[] leadingData(); // Any content preceding the PEM header
byte[] decode(); // Decode Base64 content
...
}PEM pr = PEMDecoder.of().decode(pem, PEM.class);Java 28-ში PEM Encodings მუდმივი API გახდება.
Post-Quantum Hybrid Key Exchange for TLS 1.3 (JEP 527)
JDK-ის TLS 1.3-ის რეალიზაციაში გაჩნდა პოსტკვანტური გასაღებების გაცვლის ჰიბრიდული სქემების მხარდაჭერა. ეს სქემები აერთიანებს ML-KEM-სა და ტრადიციულ ECDHE-ს (Ephemeral Elliptic-Curve Diffie-Hellman). სულ გაჩნდა 3 ჰიბრიდული სქემა:
- X25519MLKEM768 (ECDHE-ს კომბინაცია X25519-სა და ML-KEM-768-თან)
- SecP256r1MLKEM768 (ECDHE-ს კომბინაცია secp256r1 წირსა და ML-KEM-768-თან)
- SecP384r1MLKEM1024 (ECDHE-ს კომბინაცია secp384r1 წირსა და ML-KEM-1024-თან)
ახალი სქემების ჩასართავად დეველოპერისგან არაფერია საჭირო: ხელის ჩამორთმევისას X25519MLKEM768-ს ექნება უმაღლესი პრიორიტეტი და მას გამოიყენებს TLS-კლიენტი, თუ სერვერს აქვს მისი მხარდაჭერა.
ახალი პოსტკვანტური ჰიბრიდული სქემების გამოჩენა მორიგი ნაბიჯია JDK-ში იმ ალგორითმების მხარდაჭერისკენ, რომლებიც მდგრადია კვანტური კომპიუტერების მეშვეობით მოსალოდნელი პოტენციური სამომავლო შეტევების მიმართ. მანამდე Java 24-ში გამოჩნდა ML-KEM-ისა და ML-DSA-ის მხარდაჭერა.
Vector API (Twelfth Incubator) (JEP 537)
ვექტორული API მოდულში jdk.incubator.vector, რომელიც ჯერ კიდევ Java 16-ში გამოჩნდა, მეთორმეტედ რჩება საინკუბაციო სტატუსში.
ვექტორული API ინკუბატორში ამდენ ხანს იმიტომ რჩება, რომ დამოკიდებულია პროექტ Valhalla-ს ზოგიერთ შესაძლებლობაზე (უპირველეს ყოვლისა, value-კლასებზე), რომელიც მხოლოდ Java 28-ში გამოჩნდება. ამიტომ, სავარაუდოდ, Java 28-ში ვექტორული API ინკუბატორიდან preview სტატუსში გადავა.
