آشنایی با امکانات جدید جاوا ۱۴ — راهنمای کاربردی
جاوا 14 در تاریخ 17 مارس 2020 (27 اسفند 1398) منتشر میشود. علاوه بر 2400 باگ و بهبود جزئی، نسخه جدید جاوا شامل 16 بهبود عمده است که JEP نام دارند. JEP اختصاری برای عبارت «پیشنهاد بهبود جاوا» (Java Enhancement Proposals) محسوب میشود. در این مقاله نگاهی دقیقتر به این بهروزرسانیهای عمده در جاوا 14 میپردازیم که شامل عبارتهای جدید switch , NullPointerException-های بهتر، بهبود در garbage collection، استریم کردن رویداد JFR و موارد دیگر میشود. با ما تا انتهای این راهنما همراه باشید تا با امکانات جدید جاوا 14 آشنا شوید.
عبارتهای سوئیچ
این بهروزرسانی در زبان جاوا قبلاً در نسخههای 12 و 13 جاوا نیز موجود بود، اما صرفاً به عنوان یک قابلیت «پیشنمایشی» (preview language feature) ارائه شده بود، یعنی به صورت پیشفرض فعال نبود. در نهایت عبارت جدید سوئیچ در جاوا 14 انتشار یافته است. اگر بخواهیم خلاصه بیان کنیم جاوا 14 یک شکل جدید و سادهشدهای از بلوک Switch را با برچسبهای ... <- case L معرفی کرده است. عبارتهای جدید سوئیچ میتوانند به سادهسازی کد در برخی موارد کمک کنند. در ادامه برخی مثالها برای آشنایی بیشتر ارائه شدهاند.
فرض کنید که یک enum داریم که روزهای هفته را توصیف میکند. میتوانیم کد زیر را با استفاده از عبارتهای جدید Switch بنویسیم:
1switch (day) {
2 case MONDAY -> System.out.println("Aweful");
3 case TUESDAY, WEDNESDAY -> System.out.println("Okay");
4 case THURSDAY -> System.out.println("Good");
5 case FRIDAY -> System.out.println("Great");
6 case SATURDAY, SUNDAY -> System.out.println("Awesome");
7}
در این کد صرفاً از یک عبارت منفرد برای هر case استفاده کردهایم. توجه داشته باشید که بلوک Switch از هیچ گزاره break استفاده نمیکند و این امر موجب کوتاهتر شدن آن میشود. مثال بعدی عبارتهای جدید Switch را نشان میدهد که میتوانند یک مقدار بازگشت دهند:
1int numLetters = switch (day) {
2 case MONDAY, FRIDAY, SUNDAY -> 6;
3 case TUESDAY -> 7;
4 case THURSDAY, SATURDAY -> 8;
5 case WEDNESDAY -> 9;
6};
همچنین میتوان بلوکهای چندخطی نوشت و یک مقدار با یک کلیدواژه yield بازگشت داد:
1int result = switch (s) {
2 case "Foo" -> 1;
3 case "Bar" -> 2;
4 default -> {
5 System.out.println("Neither Foo nor Bar, hmmm...");
6 yield 0;
7 }
8};
چندین نکته مهم وجود دارند که هنگام استفاده از عبارتهای جدید Switch باید به خاطر داشته باشیم. برای مثال کیسهای این عبارت جدید سوئیچ باید جامع باشند. یعنی در مورد همه مقادیر باید یک برچسب سوئیچ متناظر وجود داشته باشد. همچنین با توجه به این که yield اینک یک کلیدواژه محسوب میشود، یک کلاس با نام yield در جاوا 14 غیر معتبر خواهد بود.
اگر میخواهید در مورد عبارتهای جدید سوئیچ در جاوا 14 بیشتر بدانید پیشنهاد میکنیم این صفحه JEP 341 (+) را مطالعه کنید. نویسندگان صفحه اطلاعات مفید بسیار زیادی در مورد عبارتهای جدید سوئیچ ارائه کردهاند.
NullPointerExceptions مفید
JVM در زمانی که کد تلاش کند یک ارجاع null را «ارجاع زدایی» (dereference) کند یک خطای NPE یا «استثنای اشارهگر تهی» (NullPointerExceptions) ایجاد میکند. همه توسعهدهندگان جاوا آن را قبلاً دیدهاند. برای نمونه کد زیر موجب بروز یک NPE میشود:
foo.bar = 10;
این NPE ظاهری شبیه زیر دارد:
Exception in thread "main" java.lang.NullPointerException at App.main(App.java:17)
این پیام استثنا شامل یک نام فایل و یک شماره خط است که ارجاع زدایی تهی در آن رخ داده است. در مورد گزاره foo.bar = 10; درک این که NPE به دلیل تهی بودن foo رخ داده است، کار دشواری محسوب نمیشود. اما متأسفانه گاهی اوقات مشخص نیست که چه چیزی دقیقاً موجب بروز یک NPE شده است. برای نمونه در مثال زیر اگر یکی از موارد a ،b یا c به صورت null باشند، ممکن است یک NPE رخ دهد:
a.b.c.d = 42;
با این حال مهم نیست که کدام یکی از آنها تهی است، چون NPE در هر حال یکسان خواهد بود و هیچ سرنخی در مورد فیلدی که عملاً تهی است به دست نمیدهد. در ادامه مثال دیگری میبینید. اگر یکی از آرایههای تودرتو تهی باشند، یک NPE بازگشت مییابد:
a[i][j][k] = 99;
در این مورد نیز مهم نیست که کدام ارائه تهی است، چون NPE در هر مورد رفتار یکسانی نشان میدهد. جاوا 14 این مشکل را رفع کرده و باعث شده NPE عملکرد بهتری داشته باشد. اکنون JVM میتواند تشخیص دهد کدام متغیر تهی بوده است و سپس اطلاعات در مورد آن از طریق پیام مربوطه در اختیار کاربر قرار دهد. برای نمونه یک ارجاع زدایی تهی در خط foo.bar = 10; در جاوا 14 نتیجه زیر را به دست میدهد:
Exception in thread "main" java.lang.NullPointerException: Cannot assign field "bar" because "foo" is null at App.main(App.java:17)
همچنین یک ارجاع زدایی تهی در مثال a.b.c.d = 41; در صورت تهی بودن a.b موجب ارائه پیام استثنای زیر میشود:
Exception in thread "main" java.lang.NullPointerException: Cannot read field "c" because "a.b" is null at App.main(App.java:17)
اطلاعات جدیدی که NullPointerException ارائه میکند، در تحلیل علت ریشهای مشکل کمک زیادی میکند و موجب سهولت زیادی در کار توسعهدهنده میشود. در هر حال این بهبود در JVM نسخه SAP از سال 2006 وجود داشته است. متأسفانه 14 سال طول کشیده است تا در نهایت به OpenJDK برسد.
در صورتی که به مطالعه بیشتر در خصوص این موضوع علاقهمند هستید، پیشنهاد میکنیم صفحه JEP 358 (+) را مطالعه کنید.
ابزار بستهبندی (انکوباتور)
در حال حاضر یک اپلیکیشن جاوا به طور معمول به صورت یک فایل ساده JAR توزیع میشود. با این حال، به خصوص برای یک کاربر اپلیکیشن این شرایط چندان ساده نیست. بسیار بهتر میبود اگر اپلیکیشن جاوا یک پکیج قابل نصب مانند MSI روی ویندوز یا DMG روی مک میبود. بدین ترتیب اپلیکیشنهای جاوا میتوانستند به روشی مناسبتر برای کاربران توزیع، نصب و لغو نصب شوند.
JEP 343 ابزار jpackage را معرفی کرده است که اپلیکیشن جاوا را در یک پکیج خاص پلتفرم، بستهبندی میکند که همه وابستگیهای ضروری را در خود دارد. در ادامه فهرستی از قالبهای پکیج پشتیبانی شده را میبینید:
- DEB و RPM روی لینوکس
- PKG و DMG روی macOS
- MSI و EXE روی ویندوز
در ادامه مثالی از شیوه استفاده از این ابزار جدید میبینید:
$ jpackage --name myapp --input lib --main-jar main.jar \ --main-class myapp.Main
این دستور یک فایل lib/main.jar میگیرد و یک پکیج تولید میکند که برای سیستمی که روی آن اجرا خواهد مناسبترین قالب را دارد. «نقطه ورودی» (entry point) کلاس myapp.Main است.
نویسندگان این JEP اطلاعات زیادی مورد آن در این صفحه (+) ارائه کردهاند. با این که ابزار jpackage در نسخه 14 JDK ارائه شده است، اما به صورت یک ماژول انکوباتور ارائه شده است، یعنی ثبات کارکرد آن تضمین نشده است و ممکن است در نسخههای آتی مورد تجدیدنظر قرار گیرد.
Garbage Collection به روش بهتر
جاوا 14 چندین بهبود در زمینه Garbage Collection دارد. JEP 345 (+) موجب بهبود garbage collector از طریق پیادهسازی تخصیص حافظه با کسب اطلاع از NUMA شده است. این NUMA اختصاری برای عبارت «دسترسی غیریکنواخت حافظه» (Non-Uniform Memory Access) است. این قابلیت در garbage collector مدتهای زیادی است که پیادهسازی شده است. اکنون میتوان آن را در G1 نیز با اجرای جاوا با گزینه جدید خط فرمان به صورت +XX:+UseNUMA فعال کرد. بدین ترتیب عملکرد G1 روی ماشینهای بزرگ بهبود مییابد.
JEP 363 (+) خصوصیت «Concurrent Mark Sweep» با به اختصار CMS مربوط به garbage collector را که چند سال پیش منسوخ شده بود، حذف کرده است.
JEP 364 و JEP 365 موجب شدهاند که garbage collector Z یعنی ZGS روی سیستمهای مک و ویندوز نیز فعال شود. ZGS یک گاربیج کالکتور موازی است که چند سال پیش به JVM اضافه شده است. ZGS تلاش میکند تا زمانهای مکث گردآوری گاربیج را کاهش دهد و بتواند هیپهایی با اندازهای از چند صد مگابایت تا چند ترابایت را مدیریت کند. پیشتر این کالکتور تنها روی لینوکس اجرا میشد.
JEP 366 (+) ترکیب Parallel Scavenge و الگوریتمهای گاربیج کالکشن قدیمی سریال را منسوخ کرده است. این ترکیب را میشد با استفاده از گزینه خط فرمان زیر فعال ساخت:
-XX:+UseParallelGC -XX:-UseParallelOldGC
نویسندگان بر این باورند که این ترکیب غیر رایج است، اما نیازمند تلاش نگهداری زیادی است. در عمل گزینه-XX:-UseParallelOldGC اینک منسوخ شده است. در صورتی که حالتهای منسوخ شده فعال شوند، هشداری نمایش مییابد.
استریم کردن رویداد JFR
JFR اختصاری برای عبارت «رکوردر پرواز JDK» (JDK Flight Recorder) است. JFR یک رکورد رویداد است که در خود JVM قرار دارد و اپلیکیشن اجرا شده روی JVM آن را دارد. JFR معمولاً به نام یک ابزار پولی شناخته میشود، اما در سال 2018 جاوا آن را به عنوان بخشی از OpenJDK 11 منتشر کرده است.
برای مصرف دادههای تولید شده از سوی JFR کاربر باید شروع به ضبط کردن، متوقف کردن و سپس دامپ کردن محتوا روی دیسک کند و سپس فایل رکورد شده را تحلیل بکند. این وضعیت برای مقاصد پروفایل کردن مناسب است، اما به منظور مانتیور کردن اپلیکیشن چندان کاربردی ندارد.
JFR در جاوا 14 امکان ثبت نام کاربران به صورت ناهمگام را در رویدادها فراهم ساخته است. بدین ترتیب کاربران اینک میتوانند یک دستگیره (Handler) ثبت کنند که در پاسخ به رسیدن یک رویداد فعال میشود. کلاس RecordingStream روشی یکنواخت برای فیلتر کردن و مصرف رویدادها ارائه میکند. در ادامه مثالی را میبینید که نویسندگان JFR ارائه کردهاند:
1try (var rs = new RecordingStream()) {
2 rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
3 rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
4 rs.onEvent("jdk.CPULoad", event -> {
5 System.out.println(event.getFloat("machineTotal"));
6 });
7 rs.onEvent("jdk.JavaMonitorEnter", event -> {
8 System.out.println(event.getClass("monitorClass"));
9 });
10 rs.start();
11}
برای کسب اطلاعات بیشتر به صفحه JEP 349 (+) مراجعه کنید.
قابلیتهای پیشنمایشی زبان جاوا
جاوا 14 چندین بهروزرسانی در زبان ارائه کرده است که هنوز به صورت پیشفرض فعال نیستند. مورد نخست JEP 305 است که عملگر instanceof را با یک متغیر binding بسط داده است. به مثال زیر توجه کنید:
1if (obj instanceof String s) {
2 // can use s here
3}
اگر obj وهلهای از String اشد، در این صورت به String تبدیل میشود و به متغیر binding به نام s انتساب مییابد. مورد دوم JEP 359 است که رکوردها را وارد زبان جاوا کرده است. منظور از رکورد یک نام و یک توصیف حالت است. توصیف حالت کامپوننتهای رکورد را اعلان میکند. یک رکورد میتواند یک بدنه داشته باشد. به مثال ساده زیر توجه کنید:
1record Point(int x, int y) {}
نکته سوم پس از گردآوری بازخوردها به نسخه جاوا 13 ارائه شده است. JEP 368 چند دنباله escape برای بلوکهای متنی است که قبلاً در جاوا 13 به عنوان قابلیت پیشنمایشی زبان معرفی شده بود.
متأسفانه این سه بهروزرسانی همچنان تنها به صورت قابلیت پیشنمایشی معرفی شدهاند و به صورت پیشفرض فعال نیستند. برای فعالسازی این موارد باید کامپایلر جاوا را با گزینههای زیر اجرا کنید:
1-enable-preview --release 14
و سپس java را با گزینه -enable-preview به صورت زیر اجرا نمایید:
$ javac -d classes --enable-preview --release 14 Test.java $ java -classpath classes --enable-preview Test
موارد دیگر
در این بخش برخی موارد دیگر که در جاوا 14 تغییر یافتهاند را معرفی میکنیم.
JEP 370 یک API معرفی کرده است که به اپلیکیشنهای جاوا امکان میدهد تا به صورت امن و کارآمد به حافظه بیگانه خارج از هیپ جاوا دسترسی یابند. گرچه این امکان ترسناک به نظر میرسد، اما API جدید جایگزینی برای کلاسهای java.nio.ByteBuffer و sun.misc.Unsafe محسوب میشود. این قابلیت به عنوان یک ماژول انکوبات شده معرفی شده است.
JEP 352 امکان نگاشت فایل جدیدی اضافه کرده است به طوری که میتوان از API به نام FileChannel برای ایجاد وهلههای MappedByteBuffer که به حافظه غیر فرار (NVM) اشاره دارند استفاده کرد.
ابزار Pack200 در جاوا 11 منسوخ شده است. اینک JEP 367 این ابزار و API مربوطه را حذف کرده است.
در حالتی که با Solaris و SPARC آشنا باشید، JEP 362 امکان پشتیبانی از پلتفرمهای Solaris/SPARC، Solaris/x64 و Linux/SPARC را فراهم ساخته است. در آینده به احتمال زیاد پورتهای روی این پلتفرمها از OpenJDK حذف خواهند شد.
سخن پایانی
در مقایسه با 5 JEP در نسخه 13 جاوا، نسخه جدید این زبان شاهد بهبودهای عمده بیشتری بوده است. این بهروزرسانیها زمینههای مختلفی را تخت تأثیر قرار میدهند. به احتمال زیاد جالبترین بهروزرسانی برای توسعهدهندگان جاوا عبارتهای جدید سوئیچ و بهبود در NullPointerException خواهد بود. فراموش نکنید که قابلیتهای پیشنمایشی جدید این نسخه را نیز امتحان کنید و بازخورد خود را در اختیار توسعهدهندگان JDK قرار دهید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای جاوا (Java)
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامهنویسی جاوا - از صفر تا صد
- پایتون یا جاوا کدام بهتر است؟ — راهنمای جامع
- متدهای جاوا — به زبان ساده
==