Backend
Java 17 bilan Java 11 ni solishtirish: eng yangi fichalar va takomillashuvni aniqlash
Java 17 koʻplab takomillashgan fichalari orqali Java 11 ni ortda qoldira oladi. Yana ayrim rad qilib boʻlmas sabablar borki, ular tufayli sizda yangi versiyani tanlashdan boshqa iloj qolmaydi.
Java 17 Java dasturlash tilining eng oxirgi LTS versiyasi boʻlib, 2021-yil 14-sentyabrda ishlab chiqarilgan. Long Term Support — yangilanishlar, baglardan himoya va xavfsizlik dasturlari bilan uzoq muddatga taʼminlangan versiya. Agar siz Java 11 dan foydalanayotgan boʻlsangiz, yangi fichalar va takomillashuvlarni hisobga olgan holda Java 17 ga oʻtishning hozir ayni vaqtidir. Ushbu maqolada biz muhokama qiladigan fichalarning ayrimlari Java 11 dan Java 17 versiyasiga oʻtkazilgan. Shunga qaramay, Java 17 da nimalar yangi ekanini koʻrib chiqamiz.
Nima uchun Java 11 dan voz kechishimiz kerak?
Java 11 ham LTS versiyaligi va koʻplab dasturlarda ishlatilishiga qaramay, biz nima uchun Java 17 ni tanlashimiz kerakligining bir qancha asosiy sabablari bor.
1. Java 11 ni qoʻllab-quvvatlashning toʻxtatilishi. Java 11 2023-yil sentyabr oyigacha taʼminlanishi kerak edi. Kengaytirilgan taʼminot bilan esa 2026-yilning sentyabr oyigacha qoʻllab-quvvatlanishi moʻljallangan. Bundan kelib chiqadiki, qoʻllab-quvvatlash toʻxtatilganidan soʻng bizda himoya dasturlari boʻlmaydi (hattoki xavfsizlik uchun ham).
2. Spring 6. Spring freymvorkining eng soʻnggi versiyasi boʻlgan Spring 6 ishlashi uchun Java 17 talab qilinadi. Shuningdek, ular bilan ishlash uchun moʻljallangan koʻplab kutubxonalar ham Java 17 ga koʻchirilmoqda. Agar dasturlaringiz Spring freymvorki asosida ishlasa, unda Java 17 dan foydalanish haqida aniq oʻylab koʻrishingiz kerak.
3. Oracle JDKʼdan foydalanish Java 17 da tekin. Java 17 yangi NFTC (Oracleʼning toʻlovsizlik shart va sharoitlari) litsenziyasi bilan taʼminlangan. Shuning uchun ham Oracle JDK bepul versiyasidan loyihaviy va iqtisodiy maqsadlarda foydalanishga ruxsat berilgan (Java 11 da bunday xususiyat yoʻq edi).
Java 17 ga nimalar yangi kiritilgan?
Java 17 ga uzoq muddatli “support” bilan taʼminlangan bir qancha takomillashtiruvlar va yangi fichalar joriy qilingan.
Matn bloklari
Java string formatlashdan qochish va kod oʻqishga qulay boʻlishi uchun matn bloklarini joriy qildi. Endi matnni uchtalik qoʻshtirnoq orasiga olishimiz va uning ichida slash belgisi (/)dan foydalanmasdan juft qoʻshtirnoqli matnlarni joylashtirishimiz mumkin. Quyidagi misol bilan tanishing:
private static void jsonBlock() {
String text = """
{
"name": "John Doe",
"age": 45,
"address": "Doe Street, 23, Java Town"
}
""";
System.out.println(text);
}
Koʻrib turganingizdek, bu imkoniyat slashli belgilardan koʻp foydalanishni talab qiladigan Json va shunga oʻxshash string formatida yozishni juda ham osonlashtirib beradi.
Shuningdek, yakunlovchi uchtalik qoʻshtirnoq matn blokining boshlanishini yoki undagi boʻsh joyni koʻrsatadi. Yuqorida keltirilgan misolda matndagi har bir satr uchun 2 tadan boʻsh joy qoldiriladi. Chunki juft qoʻshtirnoqlarning oʻrni eng oxirgi belgidan ikki boʻsh joy orqada boʻladi.
Matn bloklarida qoʻllash uchun 2 ta yangi slashli belgilar: bitta boʻsh joy qoʻshish uchun “\s”, yangi qatorni oʻchirish uchun esa “\”lar joriy etildi. Bular, ayniqsa, uzun SQL instruksiyalarini yozishda foyda beradi.
private static void sqlStatement() {
String sql = """
SELECT id, firstName, lastName\s\
FROM Employee
WHERE departmentId = "IT" \
ORDER BY lastName, firstName""";
System.out.println(text);
}
Takomillashtirilgan Switch Statements fichasi
Switch Expressions fichasi sizga switch caseʼdan qiymatlarni qaytarib olish va topshiriqlarda qoʻllash imkonini beradi. Javada “qaytish” harakatini ifodalash uchun : (ikki nuqta) belgisi oʻrniga -> (strelka) operatoridan foydalanish mumkin. Ushbu konstruksiyada switch yordamida orqaga qaytish uchun break kalit soʻzi kerak boʻlmaydi. Lekin standart (default) holatda boʻlish talab qilinadi.
private static void improvedSwitch(Fruit fruit) {
String text = switch (fruit) {
case APPLE, PEAR -> {
System.out.println("the given fruit was: " + fruit);
yield "Common fruit";
}
case ORANGE, AVOCADO -> "Exotic fruit";
default -> "Undefined fruit";
};
System.out.println(sql);
}
Bir switch caseʼda turli xil amaliyotlar bajarilgan holatda case block olishimiz va yield keywordʼidan qaytish qiymati uchun foydalanishimiz mumkin. Bu yerda yield kontekstga bogʻliq kalit soʻz. Yaʼni siz funksiya ichida yieldʼni boshqacha nom bilan uchratishingiz mumkin.
“record” guruhi
record klaslari oʻzgarmas klassning maxsus turi boʻlib, maʼlumotlarni uzatish obyektlarini (DTO) almashtirish uchun moʻljallangan. Odatda biz klasslar yoki metodlarda POJOʼdan foydalanamiz. Bunda klassni barcha getter, setter, equals va hashcode funksiyalarini belgilab, tasdiqlashimiz kerak. Masalan, Fruit klasini boshqa joylarda qoʻllash uchun uni quyidagicha ifodalashimiz kerak boʻladi:
public class Fruit {
private String name;
private int price;
//getters, setters, equals and hashcode methods
}
Oʻzgartirishsiz takrorlanadigan kodlarning koʻpini lombok kabi kutubxonalar yordamida qisqartirishimiz mumkin. Shunga qaramay, recordʼlar koʻmagi bilan ularni yana ham qisqartira olamiz. Bir xil kod recordʼlar yordamida quyidagicha boʻladi:
public static void doSomething() {
record Fruit(String name, int price) {}
Fruit fruit = new Fruit("Apple", 100);
System.out.println(fruit.getPrice());
}
Koʻrib turganingizdek, biz hattoki local record obyektlarining metodlarini ham aniqlay olamiz. Recordʼlar obyekti oʻzining hamma maydonlari uchun getter, equals va hashcode metodlari bilan avtomatik ravishda taʼminlaydi.
Record maydonlarini oʻzgartirib boʻlmaydi. Recordʼni yuqorida koʻrsatilganidek tasdiqlashda berilgan dalillar yordamida aniqlash mumkin (lekin faqat statik oʻzgaruvchilarni). Shuningdek, maydonlarni tasdiqlaydigan maxsus konstruktor tasvirlanadi. Uning oʻzgarmasligiga taʼsir qilishi mumkin boʻlgan recordʼlarning getter va setter funksiyalariga eʼtiborsiz boʻlmaslik tavsiya qilinadi. Quyida bir nechta konstruktorlar, statik oʻzgaruvchilar va metodlarga ega boʻlgan recordʼga misol keltirilgan:
public record Employee(int id, String firstName,
String lastName)
{
static int empToken;
// Compact Constructor
public Employee
{
if (id < 100) {
throw new IllegalArgumentException(
"Employee Id cannot be below 100.");
}
if (firstName.length() < 2) {
throw new IllegalArgumentException(
"First name must be 2 characters or more.");
}
}
// Alternative Constructor
public Employee(int id, String firstName)
{
this(id, firstName, null);
}
// Instance methods
public void getFullName()
{
if (lastName == null)
System.out.println(firstName());
else
System.out.println(firstName() + " "
+ lastName());
}
// Static methods
public static int generateEmployeeToken()
{
return ++empToken;
}
}
Record turlarining baʼzi xossalari:
Siz bir recordʼda ichma-ich sinflar va interfeyslarni qoʻllashingiz mumkin.
Shuningdek, mutlaqo statik boʻlgan ichma-ich recordʼlarga ham ega boʻlasiz.
Java dasturlash tilida recordʼlar interfeyslarni taʼminlay oladi.
Umumiy record sinflarini ham yaratishingiz mumkin.
Recordʼlar seriyalashtirilgan boʻladi.
Bu yerdan recordʼlar haqida koʻproq maʼlumot olishingiz mumkin
Java mutaxassisi boʻling
Mohirdev platformasida Full Stack Java kursi mavjud. Ushbu kurs yakunida siz 3 ta loyihani tamomlagan boʻlasiz. Kurs davomida Javani videodarslar, yopiq guruhdagi muhokamalar, mentorlar yordami va onlayn uchrashuvlar orqali oʻrganasiz.
Muhrlangan (sealed) klasslar
Muhrlangan klasslarning vazifasi nima? U klasslarimizni kengaytirishga ruxsat berilgan klasslar ustidan koʻproq nazorat qilish huquqini beradi. Java 11 da 2 turdagi: final (kengaymaydigan) yoki kengayuvchi klass boʻladi. Agar super klassni qaysi klasslar kengaytira olishini nazorat qilmoqchi boʻlsak, nima qilish kerak? Buning uchun hamma klasslarni bitta toʻplamga joylashingiz mumkin. Super klass toʻplamini esa alohida qilsangiz ham boʻladi. Biroq endi toʻplam tashqarisidan super klassga kirish mumkin boʻlmay qoladi. Misol uchun quyidagi kodga qarang:
public abstract class Fruit {
}
public final class Apple extends Fruit {
}
public final class Pear extends Fruit {
}
private static void problemSpace() {
Apple apple = new Apple();
Pear pear = new Pear();
Fruit fruit = apple;
class Avocado extends Fruit {};
}
Bu yerda biz avakadoni Fruit klasini kengaytirishiga toʻsqinlik qilolmaymiz. Agar Fruit klasini standart holatga keltirib qoʻysak, nima boʻladi? Keyin olma vazifasi meva obyektiga oʻtmaydi. Shunga qaramay, hozir super klasimizni kengaytirish uchun bir yoʻl bor. Faqat maxsus klasslarga ruxsat berish uchun muhrlangan klasslardan foydalanishimiz mumkin. Quyida bunga misol keltirilgan:
public abstract sealed class FruitSealed permits AppleSealed, PearSealed {
}
public non-sealed class AppleSealed extends FruitSealed {
}
public final class PearSealed extends FruitSealed {
}
Yuqoridan maʼlumki, bu muhrlangan klassligini bildirish uchun yangi keyword “sealed”ni qoʻlladik. “permits” kalit soʻzi yordamida kengaytirilishi mumkin boʻlgan klasslarni aniqlaymiz. Muhrlangan klassni kengaytiradigan har qanday klass PearSealed kabi kengaymaydigan (final) boʻlishi mumkin. Yoki klassni AppleSealed bilan tasdiqlashda “non-sealed” kalit soʻzi yordamida boshqa klasslar tomonidan kengaytirish mumkin.
Ushbu amaliyot AppleSealedʼni Fruitsealed klasiga tayinlash imkonini beradi. Lekin “permits” kalit soʻzi bilan belgilanmagan boshqa klasslarga Fruitsealed klasini kengaytirishga ruxsat yoʻq.
“Instance of” keywordi bilan namunalarni moslash
Java 11 da odatda obyektning maʼlum bir klassga tegishli ekanligini aniqlash uchun operatorning instance ofʼidan foydalanamiz. Agar biror amaliyot bajarishimiz kerak boʻlsa, instance of uning toʻgʻriligini tekshiradi. Keyin biz obyektni oʻsha maxsus klassga yuborishimiz kerak boʻladi. Quyida bunga misol keltirilgan:
private static void oldStyle() {
Object o = new Grape(Color.BLUE, 2);
if (o instanceof Grape) {
Grape grape = (Grape) o;
System.out.println("This grape has " + grape.getPits() + " pits.");
}
}
Misolda Grapeʼni yozish uchun obyektni aniq yuborish, keyin esa “pits” sonini aniqlashtirish kerak. Java 17 bilan uni quyidagicha oʻzgartirishimiz mumkin:
private static void patternMatchingInJava17() {
Object o = new Grape(Color.BLUE, 2);
if (o instanceof Grape grape) {
System.out.println("This grape has " + grape.getPits() + " pits.");
}
}
Instance of check kalit soʻzini && (va) shakli bilan bogʻlashimiz mumkin, || (yoki) bilan emas. Chunki “yoki” qoʻllangan taqdirda instance of check kalit soʻzi notoʻgʻri boʻlsa-da, operator boshqa shaklni ishlata oladi.
Agar instance of toʻgʻri boʻlsa, oʻzgaruvchi Grape koʻlami hatto yanayam kengayishi mumkin. Quyidagi misolda obyekt Grape turiga mansub boʻlmasa, Runtime Exception olib tashlanadi. Biroq compiler dasturi Grape obyekt chop qilish uchun tasdiqlanganda Grape obyekt mavjud boʻlishini aniq biladi. Instance ofʼ bilan namunalarni moslash haqida bu yerdan koʻproq maʼlumot olishingiz mumkin.
private static void patternMatchingScopeException() {
Object o = new Grape(Color.BLUE, 2);
if (!(o instanceof Grape grape)) {
throw new RuntimeException();
}
System.out.println("This grape has " + grape.getPits() + " pits.");
}
Foydali NullPointerException
Java 11 da NullPointerExceptionʼda faqatgina istisno holati mavjud qator raqamini olamiz. Bekor boʻlgan usul yoki oʻzgaruvchini emas. Java 17 da xabarlar almashish yaxshilandi. Chunki NullPointerException xabari NullPointerExceptionga sabab boʻlgan aniq soʻrov metodini maʼlum qildi.
public static void main(String[] args) {
HashMap<String, Grape> grapes = new HashMap<>();
grapes.put("grape1", new GrapeClass(Color.BLUE, 2));
grapes.put("grape2", new GrapeClass(Color.white, 4));
grapes.put("grape3", null);
var color = ((Grape) grapes.get("grape3")).getColor();
}
Misolda koʻrinib turganidek, biz obyekti bekor boʻlgan “grape3”ning rangini yaratishga urinyapmiz. Java 11 va Java 17 da olingan xatolik xabarini solishtirganimizda ulardagi farqni koʻramiz. Chunki endi biz xaritada mavjud boʻlgan bekor boʻlgan obyektga “get” metodini jalb qilish istisnoga sabab boʻlganini aniq bilamiz.
// Java 11
Exception in thread "main" java.lang.NullPointerException
at com.rg.java17.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)
// Java 17
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.rg.java17.Grape.getColor()" because the return value of "java.util.HashMap.get(Object)" is null
at com.rg.java17.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)
Koʻproq yutuqlar
Ixcham raqamlarni formatlashni qoʻllab-quvvatlash
Unicode standarti boʻyicha raqamlarni ixcham, odam oʻqiy oladigan shaklda formatlash uchun NumberFormat klasiga Factory metodi qoʻshildi. Uning SHORT va LONG formatlari mavjud, quyidagi misolga qarang:
NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
System.out.println(shortFormat.format(1000))
NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
System.out.println(shortFormat.format(1000))
// Output
1K
1 thousand
Kun vaqtini taʼminlovchi qoʻshildi.
Kunning vaqtini aniqlashtirishga imkon beruvchi yangi “B” namunasi Date Time namunasiga birlashtirildi.
DateTimeFormatter timeOfDayFomatter = DateTimeFormatter.ofPattern("B");
System.out.println(timeOfDayFomatter.format(LocalTime.of(8, 0)));
System.out.println(timeOfDayFomatter.format(LocalTime.of(13, 0)));
System.out.println(timeOfDayFomatter.format(LocalTime.of(20, 0)));
System.out.println(timeOfDayFomatter.format(LocalTime.of(23, 0)));
System.out.println(timeOfDayFomatter.format(LocalTime.of(0, 0)));
// Output
in the morning
in the afternoon
in the evening
at night
midnight
Sinov tajribalari oʻtkazish
Java 17, shuningdek, xotiradan foydalanish va vaqt murakkabligi boʻyicha ham Java 11 dan koʻra yaxshiroq natijalar koʻrsatgan. Ana shunday tajribalardan biri, ikkita versiyada yozilgan kodlarga bir qator topshiriqlar berib, sinab koʻrish orqali amalga oshirildi. Natijalar va topshiriqlarning toʻliq tavsifini bu yerdan topishingiz mumkin.
Qayd etilgan ayrim umumiy natijalar:
Java 17 Java 11 ga qaraganda 8,66 foiz tezroq ishlaydi. G1GC (standart chiqindi yigʻuvchi) uchun Java 16 ga qaraganda tezligi 2,41 foiz oshgan.
Java 17 Java 11 dan koʻra 6,54 foiz va Parallel GC (parallel chiqindi yigʻuvchi) uchun Java 16 dan 0,37 foiz tezroq ishlaydi.
Parallel chiqindi yigʻuvchi (Java 17 da bor) G1GCʼga qaraganda (Java 11 da bor) 16,39 foiz tezroq.
Java 11 dan Java 17 ga koʻchish koʻplab afzalliklar keltirishi mumkin. Jumladan, yangi fichalar va takomillashtirilgan ish faoliyati yaxshi natija beradi. Biroq shuni yodda tutish kerakki, migratsiya jarayonida nosozliklar paydo boʻlish ehtimoli bor. Koʻplab kutubxonalar Java 17 ni qoʻllab-quvvatlash uchun yangiroq versiyalarga almashtirilyapti. Shuning uchun loyihalarimizda tashqi kutubxonalardan foydalanayotgan boʻlsak, juda ehtiyot boʻlishimiz kerak. Ushbu ehtimoliy muammolarni tushunish va ularni bartaraf etishga kerakli chora-tadbirlarni koʻrib qoʻysangiz, Java 17 ga muammosiz va muvaffaqiyatli tarzda oʻtib olishni kafolatlagan boʻlasiz. Maroqli kodlash tilaymiz!
Manba: Java 17 vs Java 11: Exploring the Latest Features and Improvements