Java 14 Özellikleri: Modern Java Geliştirme İçin Yenilikler
Java platformu, sürekli gelişen ve yenilenen bir ekosistem sunarak dünya genelinde milyonlarca geliştiriciye hizmet vermektedir. Oracle’ın altı aylık yayın döngüsü sayesinde, her yeni sürümde platforma önemli iyileştirmeler ve yeni özellikler eklenmektedir. Java 14, bu döngünün bir parçası olarak Mart 2020’de yayınlanmış ve geliştiricilere bir dizi heyecan verici yenilik sunmuştur. Bu sürüm, özellikle önizleme (preview) ve inkübatör (incubator) aşamasındaki özellikleriyle gelecekteki Java sürümlerine yön veren önemli adımlar atmıştır.
Java 14’ün getirdiği yenilikler arasında geliştirici verimliliğini artıran sözdizimsel kolaylıklar, JVM performansını ve hata ayıklama yeteneklerini geliştiren iyileştirmeler ve platformun genel güvenilirliğini artıran değişiklikler bulunmaktadır. Bu makalede, Java 14 ile sunulan başlıca özellikler ve bunların modern Java geliştirme üzerindeki etkileri detaylı bir şekilde incelenecektir.
Switch İfadeleri (Standard)
Java 12 ve Java 13’te önizleme özelliği olarak sunulan switch ifadeleri, Java 14 ile standart bir özellik haline gelmiştir (JEP 361). Geleneksel switch deyimi, break anahtar kelimesinin unutulmasıyla ortaya çıkabilen “fall-through” hataları ve okunabilirliği düşüren yapısıyla eleştirilmekteydi. switch ifadeleri, bu sorunları ortadan kaldırarak daha kısa, daha güvenli ve daha esnek bir yapı sunar.
Geleneksel switch deyimi bir ifade (statement) iken, yeni switch yapısı bir ifade (expression) olarak kullanılabilir, yani bir değer döndürebilir. Bu, kodu önemli ölçüde basitleştirir. Yeni sözdiziminde, case etiketleri -> (ok) operatörü ile birleştirilir ve her case bloğu tek bir ifade, bir kod bloğu veya bir throw ifadesi içerebilir.
// Geleneksel switch deyimi
int ay = 3;
String mevsim;
switch (ay) {
case 1:
case 2:
case 12:
mevsim = "Kış";
break;
case 3:
case 4:
case 5:
mevsim = "İlkbahar";
break;
case 6:
case 7:
case 8:
mevsim = "Yaz";
break;
case 9:
case 10:
case 11:
mevsim = "Sonbahar";
break;
default:
mevsim = "Bilinmiyor";
break;
}
System.out.println(mevsim);
// Yeni switch ifadesi
int ayYeni = 3;
String mevsimYeni = switch (ayYeni) {
case 1, 2, 12 -> "Kış";
case 3, 4, 5 -> "İlkbahar";
case 6, 7, 8 -> "Yaz";
case 9, 10, 11 -> "Sonbahar";
default -> "Bilinmiyor";
};
System.out.println(mevsimYeni);
Yukarıdaki örnekte görüldüğü gibi, yeni switch ifadesi çok daha kompakt ve okunabilir bir yapı sunar. break kullanma zorunluluğu ortadan kalktığı için fall-through hataları da engellenmiş olur. Ayrıca, bir switch bloğundan değer döndürmek için yield anahtar kelimesi de kullanılabilir. Bu, karmaşık hesaplamaların veya birden fazla ifadenin bir case bloğunda yer alması gerektiğinde faydalıdır.
int puan = 75;
String notDurumu = switch (puan) {
case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 -> "FF";
case 50, 51, 52, 53, 54, 55, 56, 57, 58, 59 -> "DD";
case 60, 61, 62, 63, 64, 65, 66, 67, 68, 69 -> {
System.out.println("Notunuz DD'den yüksek.");
yield "CC";
}
case 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 -> {
System.out.println("Notunuz CC'den yüksek.");
yield "BB";
}
case 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 -> {
System.out.println("Tebrikler!");
yield "AA";
}
default -> {
System.out.println("Geçersiz puan.");
yield "Geçersiz";
}
};
System.out.println("Not Durumu: " + notDurumu);
switch ifadeleri, özellikle enum’lar veya sabit değerlerle çalışırken kodun temizliğini ve güvenliğini önemli ölçüde artırır.
Pattern Matching for instanceof (Preview)
Java 14 ile önizleme özelliği olarak sunulan “Pattern Matching for instanceof” (JEP 305), instanceof operatörünün kullanımını basitleştirerek yaygın bir kod kalıbını ortadan kaldırır. Geleneksel olarak, bir nesnenin belirli bir türe ait olup olmadığını kontrol ettikten sonra, o nesneyi ilgili türe dönüştürmek (cast etmek) ve ardından kullanmak gerekiyordu. Bu, özellikle birden fazla instanceof kontrolü olduğunda tekrarlayan ve hataya açık bir yapı oluşturuyordu.
// Geleneksel instanceof kullanımı
Object obj = "Merhaba Java 14";
if (obj instanceof String) {
String s = (String) obj; // Tip dönüşümü (cast)
System.out.println(s.length());
}
// Yeni Pattern Matching for instanceof
if (obj instanceof String s) { // obj String ise, s olarak atanır
System.out.println(s.length());
}
Yeni sözdiziminde, instanceof operatörü ile bir tip kontrolü yapıldığında, eğer kontrol başarılı olursa, nesne otomatik olarak belirtilen tipe dönüştürülür ve bir “pattern değişkenine” atanır. Bu değişkenin kapsamı, if bloğu ile sınırlıdır ve bu sayede yanlışlıkla başka bir yerde kullanılması engellenir.
Bu özellik, özellikle hiyerarşik yapıdaki nesnelerle çalışırken veya farklı türlerdeki nesneleri işleyen metotlarda kodun okunabilirliğini ve kısalığını artırır. Örneğin, bir List içindeki farklı türleri işlemek için:
import java.util.List;
import java.util.ArrayList;
public class PatternMatchingExample {
public static void main(String[] args) {
List
Bu özellik, tip dönüşümü (casting) ihtiyacını ortadan kaldırarak kodun daha temiz ve güvenli olmasını sağlar. Ayrıca, gelecekteki Java sürümlerinde switch ifadelerine de desen eşleştirme yeteneklerinin gelmesinin önünü açmaktadır.
Records (Preview)
Java 14 ile önizleme özelliği olarak sunulan “Records” (JEP 359), veri taşıyıcı sınıflar (data carrier classes) için daha kompakt bir sözdizimi sunar. Geleneksel Java’da, sadece veri tutan sınıflar (örneğin DTO’lar – Data Transfer Objects) bile çok fazla “boilerplate” kod gerektiriyordu: yapıcı metotlar (constructors), erişim metotları (getters), equals(), hashCode() ve toString() metotları. Bu durum, sınıfın asıl amacını gölgede bırakabiliyor ve kod tekrarına yol açabiliyordu.
record anahtar kelimesi, bu tür sınıfları tanımlamayı son derece basitleştirir. Bir record tanımladığınızda, derleyici otomatik olarak aşağıdaki bileşenleri oluşturur:
* Tüm bileşenler için bir private final alan.
* Tüm bileşenler için bir public erişim metodu (getter), alan adıyla aynı ada sahip.
* Tüm bileşenleri argüman olarak alan bir public kanonik yapıcı metot.
* Tüm bileşenleri temel alan equals() ve hashCode() metotları.
* Tüm bileşenleri içeren bir toString() metodu.
// Geleneksel veri sınıfı
class PersonGeleneksel {
private final String name;
private final int age;
public PersonGeleneksel(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersonGeleneksel person = (PersonGeleneksel) o;
return age == person.age && name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "PersonGeleneksel{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// Yeni Record tanımı
record Person(String name, int age) {}
public class RecordExample {
public static void main(String[] args) {
PersonGeleneksel p1G = new PersonGeleneksel("Alice", 30);
System.out.println(p1G);
Person p1 = new Person("Alice", 30);
Person p2 = new Person("Bob", 25);
Person p3 = new Person("Alice", 30);
System.out.println(p1); // Çıktı: Person[name=Alice, age=30]
System.out.println(p1.name()); // Çıktı: Alice (getter metodu)
System.out.println(p1.age()); // Çıktı: 30
System.out.println(p1.equals(p3)); // Çıktı: true
System.out.println(p1.hashCode() == p3.hashCode()); // Çıktı: true
System.out.println(p1.equals(p2)); // Çıktı: false
}
}
Records, varsayılan olarak değişmez (immutable) veri yapılarıdır. Alanlar final olarak tanımlanır ve setter metotları otomatik olarak oluşturulmaz. Bu, özellikle fonksiyonel programlama tarzını benimseyen veya değişmez veri yapılarının avantajlarından yararlanmak isteyen geliştiriciler için büyük bir kolaylık sağlar.
Records, arayüzleri uygulayabilir, statik alanlar ve metotlar tanımlayabilir, hatta örnek metotlar tanımlayabilir. Ancak, instance alanları (record bileşenleri dışında) tanımlayamaz, diğer sınıfları extend edemez ve soyut (abstract) olamaz. Bu kısıtlamalar, record’ların veri taşıma amacına odaklanmasını sağlar.
Text Blocks (İkinci Önizleme)
Java 13’te önizleme olarak tanıtılan “Text Blocks” (JEP 368), Java 14 ile ikinci bir önizleme özelliği olarak sunulmuştur. Bu özellik, çok satırlı string’lerin Java’da daha kolay ve okunabilir bir şekilde tanımlanmasını sağlar. Geleneksel olarak, SQL sorguları, JSON, XML veya HTML gibi çok satırlı metinleri Java’da oluşturmak, escape karakterlerinin (\n, \") ve string birleştirme operatörünün (+) yoğun kullanımı nedeniyle oldukça zahmetli ve hataya açıktı.
Text Blocks, üç çift tırnak (""") ile başlar ve biter. Bu blok içindeki metin, herhangi bir escape karakteri kullanmadan doğrudan yazılabilir ve girintiler otomatik olarak yönetilir.
// Geleneksel çok satırlı string
String htmlGeleneksel = "\n" +
" \n" +
" Merhaba, Java!
\n" +
" \n" +
"";
System.out.println(htmlGeleneksel);
// Yeni Text Block
String htmlYeni = """
Merhaba, Java!
""";
System.out.println(htmlYeni);
Text Blocks, derleyici tarafından otomatik olarak girintileri ayarlar. Derleyici, kapanış """ işaretine göre en soldaki girintiyi belirler ve bu girintiyi metnin geri kalanından kaldırır. Bu, kodunuzun içindeki girintiyi korurken, nihai string’in istenen girinti seviyesine sahip olmasını sağlar.
Text Blocks, ayrıca bazı yeni escape dizileri de sunar:
* \s: Tek bir boşluk karakteri (trailing boşlukları korumak için).
* \: Bir satır sonunu atlar, yani iki satırı tek bir mantıksal satırda birleştirir.
String query = """
SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE"
WHERE "CITY" = 'Istanbul'
ORDER BY "EMP_ID"
""";
System.out.println(query);
String json = """
{
"name": "Alice",
"age": 30,
"city": "New York"
}""";
System.out.println(json);
String multiLine = """
Bu çok uzun bir metin,
ancak bu iki satır \
tek bir mantıksal satır olarak birleşecek.
""";
System.out.println(multiLine);
Text Blocks, özellikle harici formatlarla (JSON, XML, SQL, HTML) çalışan uygulamalarda kodun okunabilirliğini ve yazım kolaylığını önemli ölçüde artırır.
NullPointerException İyileştirmeleri (JEP 358)
NullPointerException (NPE), Java geliştiricilerinin en sık karşılaştığı ve hata ayıklaması bazen zor olabilen çalışma zamanı hatalarından biridir. Geleneksel olarak, bir NPE mesajı yalnızca hangi satırda hata oluştuğunu belirtir, ancak o satırdaki hangi değişkenin null olduğunu göstermez. Karmaşık ifadelerde veya zincirleme metot çağrılarında bu durum, null olan referansı bulmayı zorlaştırabilir.
Java 14, bu sorunu çözmek için NPE mesajlarını daha açıklayıcı hale getiren bir iyileştirme sunar. Bu özellik, JVM’nin hata oluştuğunda daha fazla bilgi sağlamasını sağlar. Örneğin:
// Geleneksel NPE mesajı (Java 13 ve öncesi)
// Exception in thread "main" java.lang.NullPointerException
// at com.example.MyApp.main(MyApp.java:7)
// Java 14'teki gelişmiş NPE mesajı
// Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toLowerCase()" because "user.name" is null
// at com.example.MyApp.main(MyApp.java:7)
class User {
String name;
Address address;
public User(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
class Address {
String street;
public Address(String street) {
this.street = street;
}
public String getStreet() {
return street;
}
}
public class NPEImprovementExample {
public static void main(String[] args) {
User user = new User("Alice", null);
// user.getAddress() null, bu yüzden .getStreet() çağrısı NPE fırlatacak.
System.out.println(user.getAddress().getStreet().toLowerCase());
}
}
Java 14’te bu kodu çalıştırdığınızda, hata mesajı Cannot invoke "Address.getStreet()" because "user.address" is null şeklinde daha spesifik bir bilgi sağlayacaktır. Bu, geliştiricilerin null olan referansı hızla tespit etmesine ve hata ayıklama sürecini önemli ölçüde hızlandırmasına yardımcı olur. Bu özellik varsayılan olarak etkindir ve java.lang.NullPointerException::detailMessage sistem özelliği aracılığıyla kontrol edilebilir.
Dış Bellek Erişim API’si (Incubator)
“Foreign-Memory Access API” (JEP 370), Java 14 ile inkübatör modülünde (jdk.incubator.foreign) sunulan önemli bir özelliktir. Bu API, Java uygulamalarının yığın dışındaki (off-heap) belleğe güvenli ve verimli bir şekilde erişmesini sağlar. Geleneksel olarak, Java uygulamaları yığın belleği (heap) üzerinde çalışır ve yığın dışı belleğe erişim için ByteBuffer veya JNI (Java Native Interface) gibi mekanizmalar kullanılırdı. Ancak bu yaklaşımların kendi sınırlamaları ve karmaşıklıkları vardı.
ByteBuffer‘lar sınırlı kapasiteye sahiptir ve genellikle belleği doğrudan yönetmek için yeterli esnekliği sağlamaz. JNI ise karmaşık, hataya açık ve platforma bağımlı kod yazmayı gerektirir. Dış Bellek Erişim API’si, bu sorunlara çözüm getirerek Java geliştiricilerine yığın dışı belleği doğrudan, güvenli ve performant bir şekilde kullanma imkanı sunar.
Bu API’nin temel bileşenleri şunlardır:
* MemorySegment: Belirli bir dış bellek bölgesini temsil eder. Bu bölge, yığın dışı bellek, paylaşılan bellek veya kalıcı bellek olabilir.
* MemoryAddress: Bellek segmenti içindeki belirli bir adresi temsil eder.
* MemoryLayout: Bellek segmentindeki verilerin düzenini (tip, boyut, ofset) tanımlar.
Bu API’nin kullanım alanları oldukça geniştir:
* Büyük Veri İşleme: Çok büyük veri kümelerini bellekte tutmak ve işlemek, ancak Java yığınının GC maliyetlerinden kaçınmak.
* Serileştirme/Deserileştirme: Verileri doğrudan dış belleğe yazma ve okuma, ağ veya disk I/O performansını artırma.
* Native Kütüphanelerle Etkileşim: Native kütüphaneler tarafından ayrılan belleğe doğrudan erişim ve veri transferi.
* Paylaşılan Bellek: Süreçler arası iletişim için paylaşılan bellek bölgelerini kullanma.
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.ValueLayout;
import java.nio.ByteBuffer;
public class ForeignMemoryAccessExample {
public static void main(String[] args) {
// 100 byte'lık bir yığın dışı bellek segmenti tahsis et
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
// Bir int değerini belleğe yaz
int value = 42;
segment.set(ValueLayout.JAVA_INT, 0, value); // Ofset 0'a yaz
// Bellekten int değerini oku
int readValue = segment.get(ValueLayout.JAVA_INT, 0);
System.out.println("Okunan değer: " + readValue); // Çıktı: Okunan değer: 42
// Bir byte dizisini belleğe yaz
byte[] data = "Merhaba Java".getBytes();
segment.asSlice(4, data.length).copyFrom(MemorySegment.ofArray(data));
// Bellekten byte dizisini oku
byte[] readData = segment.asSlice(4, data.length).toByteArray();
System.out.println("Okunan metin: " + new String(readData)); // Çıktı: Okunan metin: Merhaba Java
} // segment try-with-resources ile otomatik olarak serbest bırakılır
}
}
Bu API, Java’nın performans kritik uygulamalarda ve native kodlarla entegrasyonda daha güçlü olmasını sağlayacak potansiyele sahiptir.
Packaging Tool (Incubator)
Java 14 ile inkübatör modülünde (jdk.incubator.jpackage) sunulan “Packaging Tool” (JEP 343), Java uygulamalarını platforma özgü paketler halinde (örneğin Windows için MSI veya EXE, macOS için DMG, Linux için DEB veya RPM) dağıtmayı kolaylaştıran jpackage aracını getirmiştir. Geleneksel olarak, Java uygulamalarını son kullanıcılara dağıtmak, özellikle bağımlılıkları ve bir JVM’yi de içerecek şekilde paketlemek karmaşık bir süreçti.
jpackage aracı, bir Java uygulamasını ve onun bağımlılıklarını (JAR dosyaları, modüller) tek bir çalıştırılabilir pakette birleştirerek, son kullanıcının Java Runtime Environment (JRE) kurma ihtiyacını ortadan kaldırır. Bu, özellikle masaüstü uygulamaları için kullanıcı deneyimini önemli ölçüde iyileştirir.
jpackage‘ın temel özellikleri:
* Platforma Özgü Paketler: Windows (.msi, .exe), macOS (.dmg), Linux (.deb, .rpm) gibi farklı işletim sistemleri için yerel paketler oluşturabilir.
* JRE Dahil Etme: Uygulama ile birlikte bir Java Runtime Environment’ı (JRE) paketleyebilir, böylece kullanıcının sisteminde Java yüklü olup olmaması fark etmez.
* Bağımlılık Yönetimi: Uygulamanın tüm JAR bağımlılıklarını pakete dahil edebilir.
* Uygulama İkonları ve Metadatalar: Uygulama için ikonlar, sürüm bilgileri ve diğer metadataları ayarlama imkanı sunar.
Örnek bir jpackage komutu (basitleştirilmiş):
jpackage --input lib --main-jar myapp.jar --main-class com.example.MyMainClass --name "MyJavaApp" --type dmg --dest output
Bu komut, lib klasöründeki bağımlılıkları içeren myapp.jar dosyasını kullanarak com.example.MyMainClass ana sınıfına sahip “MyJavaApp” adında bir macOS DMG paketi oluşturur ve output klasörüne kaydeder.
Bu araç, Java’nın masaüstü uygulamaları geliştirme ve dağıtma yeteneklerini güçlendirerek, geliştiricilerin uygulamalarını daha kolay bir şekilde son kullanıcılara ulaştırmasına olanak tanır.
Garbage Collector İyileştirmeleri ve Kaldırılanlar
Java 14, JVM’nin bellek yönetimi ve çöp toplama (Garbage Collection – GC) mekanizmalarında önemli iyileştirmeler ve bazı eski GC algoritmalarının kaldırılmasını içerir.
ZGC ve Shenandoah İyileştirmeleri
Java’nın düşük gecikme süreli çöp toplayıcıları olan ZGC (Z Garbage Collector) ve Shenandoah, Java 14 ile daha geniş platform desteği kazanmıştır.
* ZGC on macOS and Windows (JEP 364): ZGC, Linux’ta tanıtıldıktan sonra Java 14 ile macOS ve Windows işletim sistemlerine de taşınmıştır. Bu, geliştiricilerin bu platformlarda da ZGC’nin ultra düşük gecikme süreli çöp toplama yeteneklerinden faydalanabilmesini sağlar. ZGC, büyük yığınlara sahip uygulamalar için dahi duraklama sürelerini (pause times) milisaniyelerin altında tutmayı hedefler.
* Shenandoah on Windows: Benzer şekilde, Shenandoah GC de Windows’a taşınarak, Windows üzerinde düşük gecikme süreli GC ihtiyacı olan uygulamalara bir seçenek daha sunulmuştur.
G1 için NUMA-Aware Bellek Tahsisi (JEP 345)
G1 (Garbage-First) Garbage Collector, Java 14 ile NUMA (Non-Uniform Memory Access) mimarilerine daha duyarlı hale getirilmiştir. NUMA sistemlerde, işlemciler belleğe farklı hızlarda erişebilir (kendi yerel belleğine hızlı, diğer işlemcilerin belleğine daha yavaş). Bu iyileştirme, G1’in bellek tahsisini ve çöp toplama işlemlerini NUMA topolojisini göz önünde bulundurarak yapmasını sağlar. Sonuç olarak, NUMA mimarilerinde çalışan Java uygulamalarının performansı ve verimi artırılabilir.
CMS ve Paralel + Seri GC Kombinasyonunun Kaldırılması (JEP 363, JEP 366)
Java 14, bazı eski ve bakımı zor GC algoritmalarını platformdan kaldırma kararı almıştır:
* Remove the Concurrent Mark Sweep (CMS) Garbage Collector (JEP 363): CMS GC, düşük gecikme süresi hedefleriyle tasarlanmış eski bir çöp toplayıcıydı. Ancak, bakımı zorlaşmış, karmaşık ve modern donanım mimarileriyle tam uyumlu değildi. Daha yeni ve daha verimli alternatifler olan ZGC ve Shenandoah’ın yanı sıra, G1 de düşük gecikme süresi hedeflerine ulaşmada önemli ilerlemeler kaydetmiştir. Bu nedenle, CMS GC Java 14’te tamamen kaldırılmıştır.
* Deprecate the ParallelScavenge + SerialOld GC Combination (JEP 366): ParallelScavenge ve SerialOld çöp toplayıcılarının birlikte kullanıldığı kombinasyon (genç nesil için ParallelScavenge, yaşlı nesil için SerialOld), Java 14’te kullanımdan kaldırılmıştır. Bu kombinasyonun kullanımı oldukça nadirdi ve bakım maliyeti faydalarını aşmaya başlamıştı. Gelecekteki sürümlerde kaldırılması beklenmektedir.
Bu değişiklikler, JVM’nin GC mekanizmalarını modernize etme, daha yeni ve daha verimli algoritmalara odaklanma stratejisinin bir parçasıdır.
JFR Event Streaming (JEP 349)
Java Flight Recorder (JFR), Java uygulamalarının ve JVM’nin çalışma zamanı davranışını izlemek ve sorun gidermek için güçlü bir araçtır. Geleneksel olarak, JFR verileri bir dosyaya kaydedilir ve daha sonra analiz edilirdi. Java 14 ile sunulan “JFR Event Streaming” (JEP 349), JFR olaylarının canlı olarak bir API aracılığıyla tüketilmesine olanak tanır.
Bu özellik, uygulama çalışırken performans verilerini, sistem olaylarını ve diğer JVM metriklerini gerçek zamanlı olarak izlemek isteyen geliştiriciler ve operasyon ekipleri için büyük bir avantajdır. Artık JFR verilerini sürekli olarak bir dosyaya yazıp okumaya gerek kalmadan, doğrudan bellek içi akışlardan işlenebilir.
JFR Event Streaming’in faydaları:
* Gerçek Zamanlı İzleme: Uygulama davranışını anında gözlemleme, performans sorunlarını hızla tespit etme.
* Dinamik Analiz: Uygulamanın performansına ve kaynak kullanımına dinamik olarak yanıt veren araçlar geliştirme.
* Otomasyon: JFR verilerini otomatik sistemlere entegre ederek uyarılar oluşturma veya otomatik eylemler tetikleme.
* Daha Az Disk I/O: Verileri doğrudan akıştan okuyarak disk yazma yükünü azaltma.
Bu API, jdk.jfr.consumer paketi altında bulunur ve geliştiricilerin kendi özel JFR tüketicilerini yazmalarına olanak tanır.
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
import java.time.Duration;
public class JfrStreamExample {
public static void main(String[] args) throws InterruptedException {
// 1 saniyelik bir kayıt akışı oluştur
try (RecordingStream rs = new RecordingStream()) {
rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1)); // CPU yükünü her saniye etkinleştir
rs.enable("jdk.GarbageCollection"); // GC olaylarını etkinleştir
rs.onEvent("jdk.CPULoad", event -> {
System.out.println("CPU Yükü: " + event.getFloat("machineTotal") * 100 + "%");
});
rs.onEvent("jdk.GarbageCollection", event -> {
String gcName = event.getString("name");
long duration = event.getDuration().toMillis();
System.out.println("GC Olayı: " + gcName + " - Süre: " + duration + " ms");
});
rs.startAsync(); // Akışı asenkron olarak başlat
// Uygulamanın bir süre çalışmasına izin ver
Thread.sleep(10000);
}
}
}
Bu örnek, CPU yükünü ve GC olaylarını canlı olarak dinler ve konsola yazdırır.
Diğer Önemli İyileştirmeler
Java 14, yukarıda detaylandırılan büyük özelliklerin yanı sıra, platformun farklı alanlarında bir dizi başka iyileştirme ve değişiklik de getirmiştir.
Non-Volatile Mapped Byte Buffers (JEP 352)
Bu JEP, MappedByteBuffer için yeni bir mod olan MAP_SYNC‘yi tanıtır. Bu mod, kalıcı bellek (persistent memory – NVM) üzerinde çalışan MappedByteBuffer‘ların verilerini doğrudan ve senkronize bir şekilde depolama aygıtına yazmasını sağlar. Kalıcı bellek, güç kesintilerinde bile verilerini koruyabilen yeni bir depolama teknolojisidir. Bu özellik, Java uygulamalarının kalıcı bellekten daha verimli bir şekilde yararlanmasına ve yüksek performanslı, dayanıklı veri depolama çözümleri geliştirmesine olanak tanır.
Deprecation of the Solaris and SPARC Ports (JEP 362)
Java 14 ile Solaris/SPARC, Solaris/x64 ve Linux/SPARC portları kullanımdan kaldırılmıştır. Bu, Oracle’ın ve OpenJDK topluluğunun bu platformlara olan desteği azaltma ve kaynaklarını daha yaygın kullanılan platformlara (Linux, Windows, macOS) odaklama stratejisinin bir parçasıdır. Bu portlar gelecekteki sürümlerde tamamen kaldırılabilir.
Sonuç
Java 14, Java platformunun modernizasyon yolculuğunda önemli bir kilometre taşıdır. switch ifadelerinin standart hale gelmesi, geliştiricilere daha temiz ve güvenli kod yazma imkanı sunarken, Pattern Matching for instanceof ve Records gibi önizleme özellikleri gelecekteki Java sürümlerinin ne kadar yenilikçi olacağının sinyallerini vermektedir. Text Blocks, çok satırlı string’lerle çalışmayı basitleştirirken, NullPointerException iyileştirmeleri hata ayıklama sürecini hızlandırmaktadır.
Ayrıca, Foreign-Memory Access API ve Packaging Tool gibi inkübatör özellikleri, Java’nın performans kritik alanlarda ve dağıtım kolaylığında daha da güçleneceğini göstermektedir. Çöp toplayıcılarındaki iyileştirmeler (ZGC ve Shenandoah’ın genişlemesi, G1’in NUMA farkındalığı) ve eski, verimsiz GC’lerin kaldırılması, JVM’nin genel verimliliğini ve kararlılığını artırmaktadır. JFR Event Streaming ise, uygulamaların canlı izlenmesi ve analizi için yeni kapılar açmaktadır.
Java 14, hem geliştirici verimliliğini artıran sözdizimsel şekerler hem de platformun temel performans ve yönetim yeteneklerini geliştiren derinlemesine iyileştirmeler sunarak, modern Java geliştirmesi için sağlam bir temel oluşturmuştur. Geliştiricilerin bu yeni özellikleri keşfetmeleri ve projelerine entegre etmeleri, daha verimli, okunabilir ve güçlü Java uygulamaları oluşturmalarına yardımcı olacaktır.