Java 11 Özellikleri: Uzun Süreli Destek ve Modern Geliştirme İçin Kapsamlı Bir Bakış
Java ekosistemi, Oracle’ın her altı ayda bir yeni bir sürüm yayınlama stratejisini benimsemesiyle sürekli bir evrim içindedir. Bu hızlı yayın döngüsü, geliştiricilere en son yenilikleri daha hızlı ulaştırmayı hedeflerken, kurumsal düzeydeki uygulamalar için uzun vadeli istikrar ve destek beklentisi farklı bir ihtiyacı doğurmuştur. İşte bu noktada, Java 11 kritik bir rol oynamaktadır. Eylül 2018’de yayınlanan Java 11, Java 8’den sonraki ikinci Uzun Süreli Destek (LTS) sürümü olup, kurumsal uygulamalar için güvenilir bir temel sunmaktadır. Bu makale, Java 11’in getirdiği başlıca özellikleri, geliştiriciler için sunduğu avantajları ve modern Java geliştirme süreçlerindeki yerini detaylı bir şekilde inceleyecektir.
Java 11’in Önemi ve LTS Desteği
Java 11, Java platformunun evriminde önemli bir dönüm noktasını temsil eder. Java 8’den bu yana gelen ilk LTS sürümü olması, onu birçok kurumsal uygulamanın hedefi haline getirmiştir. LTS sürümleri, Oracle ve OpenJDK topluluğu tarafından uzun yıllar boyunca güvenlik güncellemeleri ve hata düzeltmeleri almayı garanti eder. Bu durum, özellikle büyük ve kritik sistemler için stabilite ve sürdürülebilirlik açısından hayati önem taşır. Java 11’e geçiş yapan şirketler, en azından 2026’ya kadar resmi destek almanın rahatlığını yaşayacaklardır. Bu, geliştirme ekiplerinin yeni özelliklere odaklanmasına ve temel platformun güvenliği konusunda endişelenmemesine olanak tanır.
Java 11, sadece uzun süreli destek sunmakla kalmaz, aynı zamanda Java 9 ve Java 10’da tanıtılan birçok yeniliği de bünyesinde barındırır. Modülerlik (JPMS), geliştirilmiş performans, yeni API’ler ve daha temiz bir kod tabanı, Java 11’i modern Java uygulamaları için cazip bir seçenek haline getirir. Şimdi, Java 11’in öne çıkan özelliklerini daha yakından inceleyelim.
Geliştirici Verimliliğini Artıran Yenilikler
Java 11, geliştiricilerin günlük işlerini kolaylaştıran ve kod kalitesini artıran bir dizi yeni özellik ve iyileştirme sunar. Bu yenilikler, özellikle modern, temiz ve okunabilir kod yazmaya odaklanmıştır.
Var ile Lambda Parametreleri (JEP 323)
Java 10 ile tanıtılan var anahtar kelimesi, yerel değişken tür çıkarımı sağlayarak kodun daha kısa ve okunabilir olmasını sağlamıştı. Java 11, bu özelliği lambda ifadelerine genişletmiştir. Artık lambda parametrelerinde var kullanarak değişken türlerini açıkça belirtmek yerine, derleyicinin türü çıkarmasına izin verebilirsiniz. Bu özellik, özellikle lambda ifadelerine ek açıklamalar eklemek istediğinizde kullanışlıdır.
Örneğin, Java 8/9/10’da şöyle yazılırdı:
(String s1, String s2) -> s1 + s2;
Java 11 ile var kullanarak:
(var s1, var s2) -> s1 + s2;
Bu, tek başına çok büyük bir değişiklik gibi görünmeyebilir, ancak lambda parametrelerine ek açıklamalar eklemek istediğinizde var‘ın faydası ortaya çıkar:
(@Nonnull var s1, @Nullable var s2) -> s1 + s2;
Eğer var kullanmasaydınız, her iki parametrenin de açıkça tür belirtmesi gerekirdi ve bu, daha uzun ve tekrarlayan bir kod bloğu oluştururdu. var ile bu tür senaryolarda kod daha temiz ve düzenli kalır.
Yeni String Metotları
Java 11, String sınıfına günlük programlama görevlerini kolaylaştıran beş yeni metot eklemiştir:
* isBlank(): Bir dizenin boş olup olmadığını veya sadece boşluk karakterleri içerip içermediğini kontrol eder. trim().isEmpty() kontrolüne benzer, ancak daha basittir.
" ".isBlank(); // true
"".isBlank(); // true
" hello ".isBlank(); // false
* strip(): Dizinin başındaki ve sonundaki tüm boşluk karakterlerini (Unicode boşlukları dahil) kaldırır. trim() metodunun daha kapsamlı bir versiyonudur, çünkü trim() sadece ASCII boşluk karakterlerini kaldırır.
"\t hello \n".strip(); // "hello"
" hello ".trim(); // "hello" (aynı sonuç, ancak farklı boşluk türlerinde strip() daha etkilidir)
* stripLeading(): Dizinin başındaki boşluk karakterlerini kaldırır.
" hello ".stripLeading(); // "hello "
* stripTrailing(): Dizinin sonundaki boşluk karakterlerini kaldırır.
" hello ".stripTrailing(); // " hello"
* repeat(int count): Diziyi belirtilen sayıda tekrarlar.
"na".repeat(2); // "nana"
"hello ".repeat(3); // "hello hello hello "
Bu metotlar, dize manipülasyonu işlemlerini basitleştirir ve daha okunabilir kod yazmaya olanak tanır.
Yeni File Metotları
Dosya okuma ve yazma işlemleri de Java 11 ile daha kolay hale gelmiştir. Files sınıfına iki yeni statik metot eklenmiştir:
* readString(Path path): Belirtilen dosyanın tüm içeriğini bir String olarak okur.
Path filePath = Files.writeString(Files.createTempFile("test", ".txt"), "Hello Java 11!");
String content = Files.readString(filePath);
System.out.println(content); // Hello Java 11!
* writeString(Path path, CharSequence csq, OpenOption... options): Belirtilen CharSequence‘ı (genellikle bir String) bir dosyaya yazar.
Path filePath = Path.of("myFile.txt");
Files.writeString(filePath, "This is a test.");
Bu metotlar, küçük dosyaların içeriğini okuma veya yazma işlemlerini tek satırda gerçekleştirmeyi mümkün kılarak BufferedReader ve BufferedWriter gibi daha düşük seviyeli API’lerle uğraşma ihtiyacını azaltır.
Collection to Array Dönüşümü
Collection arayüzüne yeni bir toArray metodu eklenmiştir: toArray(IntFunction. Bu metot, bir koleksiyonu belirli bir türde bir diziye dönüştürmek için daha modern ve verimli bir yol sunar.
Önceki sürümlerde:
List names = List.of("Alice", "Bob");
String[] namesArray = names.toArray(new String[0]); // veya new String[names.size()]
Java 11 ile:
List names = List.of("Alice", "Bob");
String[] namesArray = names.toArray(String[]::new);
String[]::new bir metot referansıdır ve IntFunction arayüzünün gerektirdiği apply(int value) metodunu uygular. Bu, daha kısa ve okunabilir bir sözdizimi sağlar.
Predicate.not() Metodu
Predicate arayüzüne eklenen not() statik metodu, var olan bir Predicate‘ın mantıksal tersini oluşturmayı kolaylaştırır. Özellikle stream API ile çalışırken lambda ifadelerini daha okunabilir hale getirir.
Önceki sürümlerde:
List names = List.of("Alice", "Bob", "", "Charlie");
List nonEmptyNames = names.stream()
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
Java 11 ile:
List names = List.of("Alice", "Bob", "", "Charlie");
List nonEmptyNames = names.stream()
.filter(Predicate.not(String::isEmpty))
.collect(Collectors.toList());
Bu, özellikle karmaşık koşullarda veya bir Predicate‘ı yeniden kullanırken kodun amacını daha net ifade etmeye yardımcı olur.
HTTP Client API’nin Standardlaştırılması (JEP 321)
Java 9’da deneysel olarak tanıtılan ve Java 10’da küçük iyileştirmeler alan yeni HTTP Client API, Java 11 ile java.net.http paketi altında nihayet standart hale getirilmiştir. Bu, Java’da modern ve verimli HTTP/2 ve WebSocket istemcileri oluşturmak için uzun zamandır beklenen bir gelişmedir.
Yeni HTTP Client API, hem senkron hem de asenkron (CompletableFuture tabanlı) istekleri destekler ve HTTP/1.1 ile HTTP/2 protokollerini kullanabilir. Güçlü, esnek ve kullanımı kolay bir API sunar.
Basit bir GET isteği örneği:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class HttpClientExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/todos/1"))
.GET()
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body());
}
}
Asenkron bir istek örneği:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
public class AsyncHttpClientExample {
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/todos/2"))
.GET()
.build();
CompletableFuture> responseFuture =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
responseFuture.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join(); // Asenkron işlemi bekler
}
}
Bu yeni API, üçüncü taraf kütüphanelere bağımlılığı azaltır ve Java geliştiricilerine modern web servisleriyle etkileşim kurmak için yerel, yüksek performanslı bir çözüm sunar.
Tek Kaynak Dosya Çalıştırma (JEP 330)
Java 11 ile gelen bu özellik, küçük programlar veya betik benzeri uygulamalar için geliştirme sürecini önemli ölçüde basitleştirir. Artık tek bir Java kaynak dosyasını doğrudan java komutuyla çalıştırabilirsiniz, derleme adımına gerek kalmaz.
Örneğin, HelloWorld.java adında bir dosyanız varsa:
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello from Java 11!");
}
}
Java 11’den önce:
javac HelloWorld.java
java HelloWorld
Java 11 ile:
java HelloWorld.java
Bu özellik, özellikle yeni başlayanlar için Java öğrenimini kolaylaştırır, küçük yardımcı programlar yazarken veya prototip oluştururken geliştirme döngüsünü hızlandırır. Ayrıca, shebang (#!) satırını kullanarak betik benzeri Java dosyaları oluşturmak da mümkündür, bu da onları doğrudan çalıştırılabilir hale getirir.
Performans ve Diagnostik İyileştirmeleri
Java 11, sadece yeni API’ler getirmekle kalmaz, aynı zamanda sanal makine (JVM) seviyesinde önemli performans ve diagnostik iyileştirmeleri de sunar.
Epsilon GC (JEP 318): No-Op Garbage Collector
Epsilon GC, “No-Op” (işlem yapmayan) bir çöp toplayıcıdır. Yani, belleği tahsis eder ancak asla geri kazanmaz. Bu, ilk bakışta garip gelebilir, ancak belirli senaryolar için çok faydalıdır:
* Performans Testleri: Çöp toplama işleminin performans üzerindeki etkisini tamamen ortadan kaldırarak saf uygulama performansını ölçmek için kullanılır.
* Çok Kısa Ömürlü Uygulamalar: Çok kısa sürede tamamlanan ve çöp toplama işleminin başlamasına bile gerek kalmayacak uygulamalar için idealdir.
* Bellek Kullanım Analizi: Uygulamaların ne kadar bellek kullandığını ve ne zaman bellek dışı hatası verdiğini gözlemlemek için kullanılabilir.
Epsilon GC, java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC komutuyla etkinleştirilebilir.
ZGC (JEP 333): Düşük Gecikmeli Çöp Toplayıcı
Z Garbage Collector (ZGC), Java 11’de deneysel olarak sunulan, ölçeklenebilir ve çok düşük gecikmeli bir çöp toplayıcıdır. Hedefi, uygulama duraklamalarını (pause times) milisaniyenin altına indirmektir, hatta terabaytlarca belleğe sahip heap’lerde bile.
ZGC’nin temel özellikleri:
* Düşük Gecikme: Çoğu çöp toplama işlemi eşzamanlı olarak (uygulama iş parçacıkları çalışırken) gerçekleştirilir.
* Ölçeklenebilirlik: Çok büyük heap’leri (terabaytlar) verimli bir şekilde yönetebilir.
* Daha Az Yapılandırma: Çoğu durumda, kullanıcıların ZGC’yi manuel olarak ayarlamasına gerek kalmaz.
ZGC, özellikle büyük veri işleme, gerçek zamanlı sistemler ve yüksek performanslı sunucu uygulamaları gibi düşük gecikmenin kritik olduğu alanlarda oyunun kurallarını değiştirebilir. Java 11’de deneysel olmasına rağmen, sonraki sürümlerde daha da geliştirilerek kararlı hale gelmiştir. java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC ile etkinleştirilebilir.
Java Flight Recorder (JFR) ve Mission Control’un Açık Kaynak Olması (JEP 328)
Java Flight Recorder (JFR), JVM’deki olayları kaydetmek için kullanılan güçlü bir profil oluşturma ve diagnostik aracıdır. Daha önce ticari bir özellik olan JFR, Java 11 ile açık kaynak hale getirilmiş ve OpenJDK’ya dahil edilmiştir. Bu, tüm Java geliştiricilerinin bu güçlü araca erişebileceği anlamına gelir.
JFR, performansı etkileyen darboğazları, bellek sızıntılarını, kilitlenmeleri ve diğer sorunları tespit etmek için kullanılabilir. Uygulamanın çalışma zamanı davranışına dair zengin veriler toplar ve bu veriler Java Mission Control (JMC) gibi araçlarla görselleştirilip analiz edilebilir. JFR’nin açık kaynak olması, Java uygulamalarının performansını ve güvenilirliğini artırmak isteyen geliştiriciler için büyük bir nimettir.
Modülerlik ve Temizleme
Java 9 ile başlayan modülerlik (Java Platform Module System – JPMS) süreci, Java 11 ile devam etmiş ve platformun daha hafif, daha güvenli ve daha sürdürülebilir hale gelmesi için bazı modüller kaldırılmıştır.
Nashorn JavaScript Motoru ve jjs Aracının Kaldırılması (JEP 335)
Java 8 ile tanıtılan Nashorn JavaScript motoru, Java 11 ile birlikte kaldırılmıştır. Bunun nedeni, JavaScript ekosisteminin çok hızlı gelişmesi ve Nashorn’un bu hıza ayak uydurmakta zorlanmasıydı. JavaScript motoruna ihtiyaç duyan uygulamalar artık V8 motoru gibi alternatiflere yönelebilirler. Bu kaldırma, JDK’nın boyutunu küçültmeye ve bakım yükünü azaltmaya yardımcı olur.
Java EE ve CORBA Modüllerinin Kaldırılması (JEP 320)
Java 9’da kullanımdan kaldırılan (deprecated) Java EE (Enterprise Edition) ve CORBA modülleri, Java 11 ile JDK’dan tamamen kaldırılmıştır. Bu modüller şunları içerir:
* java.activation
* java.xml.ws (JAX-WS)
* java.xml.bind (JAXB)
* java.xml.ws.annotation
* java.corba
* java.transaction
* java.se.ee
Bu kaldırma, JDK’nın çekirdeğini daha hafif ve modern tutma stratejisinin bir parçasıdır. Java EE API’lerini kullanan uygulamaların, bunları bir modül veya Maven/Gradle bağımlılığı olarak harici olarak eklemesi gerekecektir. Bu, geliştiricilere hangi bağımlılıkları kullanacakları konusunda daha fazla esneklik sağlar ve JDK’nın temel işlevselliğini gereksiz yüklerden arındırır.
Stack Walking API (JEP 329)
Java 11, çağrı yığınını (stack trace) daha verimli bir şekilde dolaşmak için yeni bir Stack-Walking API sunar. Bu API, StackWalker sınıfı aracılığıyla geliştiricilere yığın izlerini daha esnek ve performanslı bir şekilde inceleme olanağı tanır. Özellikle güvenlik yöneticileri veya profil oluşturma araçları gibi belirli çerçeveler için faydalıdır. Geleneksel Thread.getStackTrace() metoduna göre daha iyi performans ve daha fazla kontrol sunar.
Dynamic Class-File Constants (JEP 309)
Bu JEP, Java sanal makinesine (JVM) yeni bir sabit havuzu (constant pool) girişi olan CONSTANT_Dynamic ekler. Bu, derleyicilerin ve çalışma zamanının, sabit havuzunda doğrudan sabit değerler yerine dinamik olarak hesaplanan sabitler kullanmasına olanak tanır. Özellikle lambda ifadeleri ve dinamik diller için daha verimli kod üretilmesini sağlar ve gelecekteki JVM performans iyileştirmelerinin temelini oluşturur.
Neden Java 11’e Geçmelisiniz?
Java 11, bir LTS sürümü olarak, Java 8’den bu yana geçen sürede biriken tüm yenilikleri ve iyileştirmeleri bir araya getirir. Java 8’den veya daha eski bir sürümden yükseltme yapmayı düşünen ekipler için Java 11, göz önünde bulundurulması gereken ideal bir hedeftir.
* Uzun Süreli Destek: Kurumsal uygulamalar için kritik olan uzun vadeli güvenlik güncellemeleri ve hata düzeltmeleri sunar.
* Geliştirilmiş Performans: Yeni çöp toplayıcılar (ZGC, Epsilon GC) ve JFR gibi araçlar sayesinde uygulama performansını izleme ve optimize etme yetenekleri artırılmıştır.
* Geliştirici Verimliliği: Yeni String, File metotları, var ile lambda parametreleri ve tek kaynak dosya çalıştırma gibi özellikler, geliştirme sürecini hızlandırır ve kodun okunabilirliğini artırır.
* Modernizasyon: Standardize edilmiş HTTP Client API, modern web servisleriyle etkileşim kurmak için güçlü ve yerel bir çözüm sunar.
* Daha Temiz ve Hafif JDK: Eski ve nadiren kullanılan modüllerin kaldırılması, JDK’nın daha küçük ve daha yönetilebilir olmasını sağlar.
* Geleceğe Hazırlık: Java 11’e geçiş, Java 17 (bir sonraki LTS) ve sonraki sürümlere geçiş için bir basamak görevi görür, böylece en son Java yeniliklerinden faydalanmaya devam edebilirsiniz.
Geçiş Sırasında Dikkat Edilmesi Gerekenler
Java 11’e geçiş yaparken bazı önemli değişiklikleri göz önünde bulundurmak gerekir:
* Modül Bağımlılıkları: Java EE ve CORBA modüllerinin kaldırılması nedeniyle, bu API’leri kullanan projelerin ilgili bağımlılıkları manuel olarak eklemesi gerekecektir (örneğin, Maven veya Gradle üzerinden).
* JDK İç API’leri: Java 9 ile başlayan güçlü kapsülleme (strong encapsulation) nedeniyle, uygulamanız JDK’nın iç API’lerini kullanıyorsa, illegal-access uyarıları alabilir veya IllegalAccessException hatalarıyla karşılaşabilirsiniz. Bu durumda, ya alternatif genel API’ler kullanmalı ya da geçici olarak --add-exports veya --add-opens gibi JVM argümanlarını kullanmalısınız.
* Nashorn Bağımlılığı: Eğer uygulamanız Nashorn JavaScript motorunu kullanıyorsa, yerine başka bir JavaScript motoru (örneğin GraalVM’nin JavaScript motoru) entegre etmeniz gerekecektir.
* Derleme ve Çalışma Zamanı Ortamları: Eski araçlar ve kütüphaneler, Java 11 ile uyumlu olmayabilir. Tüm bağımlılıklarınızın ve derleme araçlarınızın Java 11’i desteklediğinden emin olun.
Sonuç
Java 11, Java platformu için önemli bir kilometre taşıdır. Bir Uzun Süreli Destek sürümü olarak, kurumsal geliştiricilere uzun vadeli istikrar ve güvenilirlik sunarken, aynı zamanda Java 9 ve 10’un getirdiği birçok yeniliği de bünyesinde barındırır. Yeni API’ler, geliştirilmiş performans, daha iyi diagnostik araçları ve daha temiz bir JDK, Java 11’i modern Java uygulamaları geliştirmek için güçlü ve cazip bir seçenek haline getirir.
Java 8’den bu yana gelen önemli değişiklikler nedeniyle, Java 11’e geçiş bazı ayarlamalar gerektirse de, uzun vadede sunduğu avantajlar bu çabaya değerdir. Geliştiriciler, Java 11’in sunduğu olanakları keşfederek daha verimli, performanslı ve sürdürülebilir uygulamalar geliştirebilirler. Java ekosisteminin sürekli evriminde Java 11, sağlam bir temel ve geleceğe yönelik bir köprü görevi görmektedir.