اگر شما هم فکر می‌کنید که جاوا یک زبان قدیمی و خشک است باید بگم که هنوز جاوا رو به درستی نشناخته اید . در ماه سپتامبر نسخه 25 جاوا منتشر شده است که به عنوان یک نسخه LTS یعنی نسخه‌ای که به صورت بلند مدت پشتیبانی خواهد شد در دسترس برنامه نویسان قرار گرفته است و شرکت اوراکل تا سال 2033 از این نسخه پشتیبانی خواهد کرد. این مسأله برای شرکتها و برنامه نویسان مهم است زیرا که لازم نیست چند ماه به چند ماه نسخه جاوای خود را ارتقا دهند. در این مقاله به ویژگی‌های جدیدی که در این نسخه به جاوا اضافه شده است می‌پردازیم و ویژگی‌های جدید را بررسی می کنیم.

الگوهای رکورد (Record Patterns)

رکوردها در جاوا برای تعریف کلاس‌های داده‌ای انجام می‌شود. اصلاً دلیل ایجاد رکورد ها هم همین بود که به جای تعریف یک کلاس که فقط وظیفه نگهداری داده‌ها را داشت و کلی متد getter, setter را داشت را به شکل ساده‌تر به صورت یک رکورد تعریف کنیم.

حالا با استفاده از ویژگی Record Patterns که در نسخه 25 به جاوا اضافه شده است می‌توانید در دستورات شرطی مثل if, switch به صورت مستقیم به اعضا و فیلد های این رکورد ها دسترسی داشته باشید. مثل کد زیر:

record Point(int x, int y) {}




static String printShape(Object obj) {

return switch (obj) {

case Point(int x, int y) -> "Point: " + x + "," + y;

default -> "Unknown";

};

}

با استفاده از این ویژگی دیگر لازم نیست که برای دسترسی به فیلدها از متدهای getter, setter استفاده کنیم و کدها تمیزتر و خواناتر می‌شود.

مجموعه های ترتیبی (Sequenced Collections)

این ویژگی نظم را به دنیای مجموعه ها در جاوا آورده است. تا قبل از این شاید به راحتی نمی توانستیم اولین یا آخرین عضو یک Set و یا Map را به دست بیاوریم اما در این نسخه از جاوا اینترفیس هایی بر روی مجموعه ها تعریف شده است که باعث می‌شود که اعضای مجموعه ها ترتیب را حفظ کنند. اینترفیس هایی مانند SequencedSet, SequencedMap, SequencedList برای مثال به کد زیر توجه کنید.

SequencedSet<String> names = new LinkedHashSet<>();

names.addFirst("Mehdi");

names.addLast("Adeli");

System.out.println(names.getFirst()); // Mehdi

System.out.println(names.getLast()); // Adeli

با استفاده از این اینترفیس ها می‌توانیم عناصری را به انتها و یا ابتدای مجموعه اضافه کنیم و به صورت مستقیم به آن‌ها دسترسی داشته باشیم و کد اضافی دیگری لازم نیست.

قالب‌های متنی (String Templates)

این ویژگی شما را از دردسر های الحاق و به هم چسباندن رشته‌ها خلاص می کند. دیگر نیازی به استفاده از علامت + برای اتصال دو رشته به هم نیست و حتی String.format هم دیگر لازم نیست زیرا که می‌توانید با String template مغادیر متغیر ها را مستقیماً درون رشته‌ها قرار دهید مثل کد زیر

String name = "Mehdi";

int age = 26;

String msg = STR."Hello, my name is \{name} and I'm \{age} years old.";

خروجی این کد به شکل زیر خواهد بود

 

Hello, my name is Mehdi and I'm 26 years old.

این ویژگی در زبان‌های مدرن دیگر مثل Kotlin , C# هم وجود دارد که به جاوا اضافه شده است که خوانایی کد را بالاتر می برد.

مقادیر محدوده‌ای (Scoped Values)

تا قبل از نسخه 25 در جاوا وقتی چند نخ همزمان اجرا می‌شدند ممکن بود بخواهیم هر نخ نسخه ی مخصوص به خودش را از یک متغیر خاص داشته باشد. این نیاز با استفاده از کلاس ThreadLocal انجام می شد. اما این روش بعضی اوقات مشکل نشت حافظه (Memory leak) را داشت. در نسخه 25 یک جایگزین برای این کلاس ارائه شده است که هم سبک وزن است و هم مشکل نشت حافظه را دارد. مثل کد زیر

static final ScopedValue<String> USER = ScopedValue.newInstance();

void handleRequest(String user) {
    ScopedValue.where(USER, user).run(() -> process());
}

این روش علاوه بر اینکه مشکل نشت حافظه را ندارد با نخ های مجازی (virtula threads)هم سازگاری دارد و برای سیستم‌های همروند (concurrent) مناسب است.

نخ های مجازی (Virtual Threads)

اگر شما بخواهید در یک برنامه چند کار را همزمان انجام دهید از نخ ها یا thread ها استفاده می کنید. اما همین نخ ها منابع با‌ارزشی هستند و استفاده از آن‌ها نیازمند استفاده از حافظه و پردازنده است است همچنین سوییچ کردن وجابه جا شدن بین نخ ها هم برای سیستم هزینه دارد. اگر ما بخواهیم یک نخ بسازیم باید از سیستم عامل درخواست یک نخ جدید کنیم. اگر تعداد این نخ ها به تعداد بسیار زیادی برسد مثلاً چند هزار تا در یک برنامه جابه جا شدن بین این نخ ها خود هزینه بسیار زیادی دارد. حال برای حل این مشکل در نسخه 25 شما در جاوا نخ مجازی تعریف می کنید. ماشین مجازی جاوا (JVM) می‌تواند چندین نخ مجازی را سوار بر یک نخ واقعی کند و به این شکل هزینه جابه جایی بین نخ ها کم می شود. کد زیر یک مثال از استفاده از نخ های مجازی را نشان می دهد.

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> executor.submit(() -> doWork(i)));
}

این ویژگی برای کار با پردازش های موازی که با I/O کار می‌کند مثل (دیتابیس و وب سرویس ها و منابع خارجی) بسیار مناسب است. همچنین نیاز به کتابخانه اضافه‌ای برای این کار نیست و سرعت اجرای فوق‌العاده‌ای دارد.

Stream Gatherers

استریم ها ابتدا در جاوانسخه 8 معرفی شدند و رفته‌رفته بالغ تر شدند. با اینکه از همان ابتدا استریم ها یکی از انقلابی ترین ویژگی‌ها محسوب می‌شدند در نسخه 25 بسیار بهتر شده اند. ویژگی که در این نسخه به استریم (Stream) امکان تعریف کردن stream gatherers است. یعنی شما می‌توانید در میانه Stream یک gatherer سفارشی تعریف کنید. مثل کد زیر

var doubled = Stream.of(1, 2, 3, 4)
    .gather(Gatherers.map(x -> x * 2))
    .toList();


با استفاده از این ویژگی می‌توان عملیات سفارشی را راحتتر تعریف کرد و نیازی به Collector های پیچیده نیست زیرا که عملیات gathering مثل دسته بندی و محدودسازی و تبدیل های مختلف را راحت‌تر می کند.

Class-File API

استفاده از Reflection همیشه کند و پر از ریسک بود اما با معرفی Class-File API می‌توانید به صورت بهینه و ساختارمند به محتوای فایل‌های کلاس‌ها دسترسی پیدا کنید مثل کد زیر

try (var reader = ClassFile.of().read(MyService.class)) {
    reader.methods().forEach(m -> System.out.println(m.name()));
}


این ویژگی برای برنامه نویسان فریمورک بسیار با‌ارزش و قدرتمند است.

جمع بندی:

اگه شما در حال ساخت اپلیکیشن‌های مدرن، پربازده و همزمان (مثل میکروسرویس‌ها، سیستم‌های مبتنی بر API) هستید، مهاجرت از جاوا ۲۱ (LTS) به جاوا ۲۵ ارزشش رو داره. ویژگی‌های نهایی شده‌ی همزمانی، نحوه نوشتن کدهای تمیز و قابل اعتماد رو به کل تغییر می‌دن.
حتی اگه ترجیح می‌دید فقط روی نسخه‌های LTS بمونید (نسخه بعدی Java 27 در سپتامبر ۲۰۲۶ خواهد بود)، شدیداً توصیه می‌کنم همین الان شروع به یادگیری و آزمایش این ویژگی‌های جدید بکنید.