Takip et

Java 9 Özellikleri ve Örnekleri: Modüler Bir Geleceğe Adım

Java 9 Özellikleri ve Örnekleri: Modüler Bir Geleceğe Adım Java, yıllar içinde sürekli gelişerek yazılım dünyasının en köklü ve yaygın dillerinden biri olmuştur.

Java 9 Özellikleri ve Örnekleri: Modüler Bir Geleceğe Adım

Java, yıllar içinde sürekli gelişerek yazılım dünyasının en köklü ve yaygın dillerinden biri olmuştur. Her yeni sürüm, dilin yeteneklerini genişleten, performansı artıran ve geliştirici deneyimini iyileştiren önemli yenilikler sunar. Java 9, 2017 yılında piyasaya sürüldüğünde, bu geleneği sürdürerek özellikle modülerlik alanında devrim niteliğinde değişiklikler getirdi. Bu makalede, Java 9’un getirdiği başlıca özelliklere derinlemesine bakacak, her birini örneklerle açıklayarak Java ekosistemine olan etkilerini inceleyeceğiz.

Java Platform Modül Sistemi (JPMS – Project Jigsaw)

Java 9’un şüphesiz en önemli ve çığır açıcı özelliği, Java Platform Modül Sistemi (JPMS), yani popüler adıyla Project Jigsaw’dur. Bu özellik, Java SE platformunu ve JDK’yı modüler hale getirerek, büyük ve monolitik bir yapıdan daha yönetilebilir, güvenli ve performanslı modüllere ayırmayı amaçlamıştır.

Modülerliğin Temel Faydaları

* Güvenilirlik: Modüller arasındaki bağımlılıklar açıkça tanımlandığı için, çalışma zamanında beklenmeyen bağımlılık sorunları (JAR Hell) azalır. Her modül, hangi paketleri dışa aktardığını ve hangi modüllere ihtiyaç duyduğunu net bir şekilde belirtir.
* Daha Güçlü Kapsülleme: Modüller, yalnızca dışa aktardıkları (exported) paketlerin dışarıdan erişilebilir olmasını sağlar. Bu, iç implementasyon detaylarının dış dünyaya sızmasını engeller ve API’nin daha istikrarlı olmasını sağlar.
* Daha Küçük Çalışma Zamanı Görüntüleri (Runtime Images): jlink aracı sayesinde, uygulamanızın yalnızca ihtiyaç duyduğu JDK modüllerini içeren özel bir Java çalışma zamanı görüntüsü oluşturabilirsiniz. Bu, özellikle mikroservisler ve IoT cihazları için daha küçük dağıtım boyutları ve daha hızlı başlangıç süreleri anlamına gelir.
* Gelişmiş Performans: Daha iyi kapsülleme ve modül sınırları, JVM’nin daha verimli optimizasyonlar yapmasına olanak tanır.
* Ölçeklenebilirlik: Büyük uygulamaları daha küçük, bağımsız modüllere bölmek, uygulamanın farklı parçalarının bağımsız olarak geliştirilmesini ve bakımını kolaylaştırır.

Modül Tanımlama: module-info.java

Bir modül, module-info.java adında özel bir dosya ile tanımlanır. Bu dosya, modülün adını, dışa aktardığı paketleri (exports), ihtiyaç duyduğu diğer modülleri (requires) ve diğer modüllere hizmet sunup sunmadığını (provides/uses) belirtir.

Örnek: Basit bir modül yapısı

Diyelim ki iki modülümüz var: com.example.app (ana uygulama) ve com.example.lib (bir yardımcı kütüphane).

com.example.lib modülü için module-info.java:

// src/com.example.lib/module-info.java
module com.example.lib {
    exports com.example.lib.utils; // Bu paketi dış dünyaya aç
}

com.example.lib içindeki bir sınıf:

// src/com.example.lib/com/example/lib/utils/StringUtil.java
package com.example.lib.utils;

public class StringUtil {
    public static String capitalize(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        return Character.toUpperCase(text.charAt(0)) + text.substring(1).toLowerCase();
    }
}

com.example.app modülü için module-info.java:

// src/com.example.app/module-info.java
module com.example.app {
    requires com.example.lib; // com.example.lib modülüne bağımlıyız
}

com.example.app içindeki ana sınıf:

// src/com.example.app/com/example/app/MainApp.java
package com.example.app;

import com.example.lib.utils.StringUtil; // com.example.lib'den StringUtil'i kullanıyoruz

public class MainApp {
    public static void main(String[] args) {
        String original = "hello world";
        String capitalized = StringUtil.capitalize(original);
        System.out.println("Original: " + original);
        System.out.println("Capitalized: " + capitalized);
    }
}

Bu örnekte, com.example.app modülü, com.example.lib modülünü requires anahtar kelimesiyle gerektirir. com.example.lib ise exports anahtar kelimesiyle com.example.lib.utils paketini diğer modüllerin kullanımına açar. Bu sayede MainApp, StringUtil sınıfına erişebilir. Eğer com.example.lib StringUtil sınıfının paketini dışa aktarmasaydı, MainApp derleme hatası alırdı.

jlink Aracı

jlink, uygulamanız için özel bir çalışma zamanı görüntüsü (runtime image) oluşturmanızı sağlayan bir araçtır. Bu, uygulamanızın yalnızca ihtiyaç duyduğu Java modüllerini içerir ve daha küçük, daha optimize edilmiş dağıtımlar sağlar.

Örnek Kullanım:

# Uygulamanızın JAR dosyalarını ve modüllerini derlediğinizi varsayalım

Modül yolu: --module-path out

Ana modül: com.example.app

Ana sınıf: com.example.app.MainApp

jlink --module-path out:path/to/jdk/jmods \ --add-modules com.example.app \ --output myapp-runtime \ --launcher myapp=com.example.app/com.example.app.MainApp

Bu komut, myapp-runtime adında bir dizin oluşturur ve içine uygulamanızı çalıştırmak için gereken minimum JVM ve modülleri yerleştirir. Artık uygulamanızı bu dizindeki bin/myapp komut dosyasıyla çalıştırabilirsiniz, harici bir JDK kurulumuna ihtiyaç duymadan.

JShell (REPL Aracı)

Java 9 ile birlikte gelen bir diğer önemli özellik JShell’dir. JShell, Java için bir Read-Eval-Print Loop (REPL) aracıdır. Bu, geliştiricilerin Java kod parçacıklarını anında çalıştırmasına, test etmesine ve öğrenmesine olanak tanır. Python veya Ruby gibi dillerdeki interaktif kabuklara benzer.

JShell’in Faydaları

* Hızlı Prototipleme: Küçük kod parçacıklarını veya algoritmaları hızlıca test etmek için idealdir.
* Öğrenme ve Keşfetme: Java’ya yeni başlayanlar için veya mevcut Java geliştiricilerinin yeni API’leri keşfetmesi için harika bir araçtır.
* Hata Ayıklama: Karmaşık bir projeyi derlemeye ve çalıştırmaya gerek kalmadan belirli bir fonksiyonun veya ifadenin davranışını kontrol edebilirsiniz.

Örnek Kullanım

Terminalden jshell yazarak JShell’i başlatabilirsiniz:

jshell

Karşınıza aşağıdaki gibi bir prompt gelecektir:

|  Welcome to JShell -- Version 9.0.4
|  For an introduction type: /help intro

jshell>

Şimdi bazı Java kodları yazalım:

jshell> int x = 10;
x ==> 10

jshell> int y = 20;
y ==> 20

jshell> int sum = x + y;
sum ==> 30

jshell> System.out.println("Toplam: " + sum);
Toplam: 30

jshell> String message = "Merhaba JShell";
message ==> "Merhaba JShell"

jshell> message.toUpperCase();
$5 ==> "MERHABA JSHELL"

jshell> class Greeter {
...>     String greet(String name) {
...>         return "Hello, " + name + "!";
...>     }
...> }
|  created class Greeter

jshell> Greeter greeter = new Greeter();
greeter ==> Greeter@...

jshell> greeter.greet("Dünya");
$8 ==> "Hello, Dünya!"

jshell> /list // Geçmiş komutları listele

JShell, yazdığınız her ifadeyi anında değerlendirir ve sonucunu gösterir. Sınıflar, metotlar ve değişkenler tanımlayabilir, bunları anında kullanabilirsiniz. /list, /vars, /methods, /classes, /imports gibi komutlarla mevcut durumu kontrol edebilirsiniz. Çıkmak için /exit yazmanız yeterlidir.

HTTP/2 İstemcisi (Incubator Modülü)

Java 9, modern HTTP standartlarını (HTTP/2 ve WebSockets) destekleyen yeni bir HTTP istemcisi API’sini bir “incubator” modülü olarak tanıttı. Bu API, eski HttpURLConnection sınıfına kıyasla daha esnek, performanslı ve kullanımı kolay bir alternatif sunar. Java 11’de tam olarak kararlı hale gelmiştir.

Önemli Özellikler

* HTTP/2 Desteği: Çoklu istekleri tek bir bağlantı üzerinden gönderme yeteneği ile performans artışı sağlar.
* WebSockets Desteği: Çift yönlü iletişim için WebSockets protokolünü destekler.
* Asenkron ve Non-Blocking: CompletableFuture ile entegre olarak asenkron istekler gönderme yeteneği sunar, bu da I/O işlemlerini bloke etmeden daha verimli kaynak kullanımı sağlar.
* Fluent API: Daha okunabilir ve zincirleme çağrılara izin veren bir API tasarımı.

Örnek Kullanım (GET İsteği)

// Bu modülü kullanmak için module-info.java dosyanıza
// requires jdk.incubator.http; eklemeniz gerekir.
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import java.net.URI;
import java.util.concurrent.CompletableFuture;

public class Http2ClientExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient(); // Yeni bir HTTP istemcisi oluştur
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/todos/1")) // Hedef URI
                .GET() // GET isteği
                .build();
        
        // Senkron İstek
        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("Senkron İstek Durum Kodu: " + response.statusCode());
        System.out.println("Senkron İstek Yanıtı: " + response.body());

        System.out.println("\n-----------------------------------\n");

        // Asenkron İstek
        CompletableFuture> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        
        futureResponse.thenApply(HttpResponse::body)
                      .thenAccept(body -> System.out.println("Asenkron İstek Yanıtı: " + body))
                      .join(); // Asenkron işlemin tamamlanmasını bekle
        
        System.out.println("Asenkron İstek Tamamlandı.");
    }
}

Bu örnek, hem senkron hem de asenkron bir GET isteğinin nasıl yapılacağını gösterir. HttpClient sınıfı, istekleri göndermek için kullanılırken, HttpRequest isteği yapılandırmak ve HttpResponse ise yanıtı işlemek için kullanılır. Asenkron yaklaşım, CompletableFuture kullanarak non-blocking I/O sağlar.

Arayüzlerde Private Metotlar

Java 8 ile birlikte arayüzlere default ve static metotlar eklendi. Java 9, bu yeteneği daha da genişleterek arayüzlere private metotlar ekleme imkanı sundu. Bu özellik, arayüz içindeki default veya static metotlar arasında kod tekrarını azaltmak ve karmaşık mantığı daha küçük, yönetilebilir parçalara bölmek için kullanışlıdır.

Faydaları

* Kod Tekrarını Azaltma: Birden fazla default veya static metotta kullanılan ortak yardımcı mantığı private bir metotta toplayabilirsiniz.
* Daha Temiz Arayüzler: Karmaşık default metotları daha küçük, okunabilir parçalara ayırarak arayüzün okunabilirliğini artırır.
* Kapsülleme: Bu metotlar yalnızca arayüz içinde kullanılabilir, dışarıdan erişilemezler, bu da arayüzün iç implementasyon detaylarını korur.

Örnek Kullanım

interface MyInterface {
    default void printMessage(String message) {
        System.out.println("Mesajınız: " + formatMessage(message));
    }

    default void logAndPrint(String message) {
        logMessage(message);
        System.out.println("Loglandı ve yazdırıldı: " + formatMessage(message));
    }

    // Private default metot
    private String formatMessage(String message) {
        return "[" + message.toUpperCase() + "]";
    }

    // Private static metot
    private static void logMessage(String message) {
        System.out.println("LOG: " + message);
    }
}

public class InterfacePrivateMethodExample implements MyInterface {
    public static void main(String[] args) {
        MyInterface instance = new InterfacePrivateMethodExample();
        instance.printMessage("Merhaba Dünya");
        instance.logAndPrint("Bu bir test mesajıdır");
    }
}

Bu örnekte, formatMessage metodu printMessage ve logAndPrint default metotları tarafından kullanılmak üzere private olarak tanımlanmıştır. logMessage ise private static bir metottur ve yine arayüz içindeki diğer metotlar tarafından kullanılabilir. Bu sayede, formatlama mantığı tek bir yerde toplanmış ve kod tekrarı önlenmiştir.

Koleksiyonlar İçin Fabrika Metotları

Java 9, immutable (değiştirilemez) koleksiyonlar oluşturmak için yeni, kullanışlı fabrika metotları tanıttı. List, Set ve Map arayüzlerine eklenen bu statik metotlar, daha az kodla ve daha güvenli bir şekilde koleksiyonlar oluşturmayı sağlar.

Faydaları

* Kısalık: Tek satırda immutable koleksiyonlar oluşturmayı sağlar, new ArrayList<>(), add(), Collections.unmodifiableList() gibi uzun kodlara gerek kalmaz.
* Değiştirilemezlik: Oluşturulan koleksiyonlar varsayılan olarak değiştirilemezdir. Bu, programınızın daha güvenli olmasını sağlar, çünkü koleksiyonun içeriğinin yanlışlıkla değiştirilmesini engeller.
* Null-Safety: Bu metotlar null elemanları kabul etmez ve NullPointerException fırlatır, bu da yaygın bir hata türünü önlemeye yardımcı olur.

Örnek Kullanım

import java.util.List;
import java.util.Set;
import java.util.Map;

public class CollectionFactoryMethodsExample {
    public static void main(String[] args) {
        // Immutable List oluşturma
        List immutableList = List.of("Elma", "Armut", "Kiraz");
        System.out.println("Immutable List: " + immutableList);
        // immutableList.add("Muz"); // UnsupportedOperationException fırlatır

        // Immutable Set oluşturma
        Set immutableSet = Set.of(1, 2, 3, 1); // Tekrar eden elemanlar dikkate alınmaz
        System.out.println("Immutable Set: " + immutableSet); // Çıktı: [1, 2, 3] (sıra garanti edilmez)
        // immutableSet.add(4); // UnsupportedOperationException fırlatır

        // Immutable Map oluşturma
        Map immutableMap = Map.of("Bir", 1, "İki", 2, "Üç", 3);
        System.out.println("Immutable Map: " + immutableMap);
        // immutableMap.put("Dört", 4); // UnsupportedOperationException fırlatır

        // Map.ofEntries() ile daha fazla giriş içeren Map oluşturma
        Map countryCapitals = Map.ofEntries(
            Map.entry("Türkiye", "Ankara"),
            Map.entry("Fransa", "Paris"),
            Map.entry("Almanya", "Berlin")
        );
        System.out.println("Ülke Başkentleri: " + countryCapitals);

        // Null eleman denemesi (hata verir)
        try {
            List nullList = List.of("A", null, "B");
        } catch (NullPointerException e) {
            System.err.println("Hata: Null elemanlara izin verilmez: " + e.getMessage());
        }
    }
}

Bu örnekler, List.of(), Set.of(), Map.of() ve Map.ofEntries() metotlarının nasıl kullanılacağını gösterir. Bu metotlar, sırasıyla 10, 10 ve 10 adede kadar eleman/giriş için aşırı yüklenmiş versiyonlara sahiptir. Daha fazla eleman için değişken argümanlı (varargs) versiyonlar kullanılabilir.

Stream API Geliştirmeleri

Java 8 ile tanıtılan Stream API, koleksiyonlar üzerinde fonksiyonel tarzda işlemler yapmayı kolaylaştırmıştı. Java 9, bu API’ye birkaç yeni metot ekleyerek daha da güçlü hale getirdi.

takeWhile() ve dropWhile()

Bu metotlar, bir koşula göre stream’i kesmek veya elemanları atlamak için kullanılır.
* takeWhile(Predicate predicate): Stream’in başından itibaren verilen koşul doğru olduğu sürece elemanları alır ve koşul ilk kez yanlış olduğunda durur.
* dropWhile(Predicate predicate): Stream’in başından itibaren verilen koşul doğru olduğu sürece elemanları atar ve koşul ilk kez yanlış olduğunda kalan tüm elemanları alır.

Örnek:

import java.util.stream.Stream;

public class StreamApiEnhancementsExample {
    public static void main(String[] args) {
        // takeWhile örneği
        Stream.of(1, 2, 3, 4, 5, 4, 3, 2, 1)
              .takeWhile(n -> n < 5)
              .forEach(n -> System.out.print(n + " ")); // Çıktı: 1 2 3 4
        System.out.println("\n");

        // dropWhile örneği
        Stream.of(1, 2, 3, 4, 5, 4, 3, 2, 1)
              .dropWhile(n -> n < 3)
              .forEach(n -> System.out.print(n + " ")); // Çıktı: 3 4 5 4 3 2 1
        System.out.println("\n");

        // ofNullable() örneği
        Stream s1 = Stream.ofNullable("Merhaba");
        s1.forEach(System.out::println); // Çıktı: Merhaba

        Stream s2 = Stream.ofNullable(null);
        s2.forEach(System.out::println); // Hiçbir şey yazdırmaz

        // iterate() geliştirmesi (predicate ile)
        Stream.iterate(1, n -> n < 10, n -> n * 2)
              .forEach(n -> System.out.print(n + " ")); // Çıktı: 1 2 4 8
        System.out.println("\n");
    }
}

ofNullable()

Bu metot, tek bir elemandan bir stream oluşturur. Eğer eleman null ise, boş bir stream döndürür. Bu, null kontrolleri yapmadan stream oluşturmayı kolaylaştırır.

iterate() Geliştirmesi

Stream.iterate() metodu artık bir başlangıç değeri, bir koşul (Predicate) ve bir sonraki elemanı üreten bir tekli işlem (UnaryOperator) alabilir. Bu, sonsuz stream’ler oluşturmak yerine belirli bir koşul altında sonlanan stream’ler oluşturmayı sağlar.

Optional API Geliştirmeleri

Java 8 ile tanıtılan Optional sınıfı, null değerleri yönetmek için güçlü bir yol sunar. Java 9, bu sınıfı daha da kullanışlı hale getiren birkaç yeni metot ekledi.

ifPresentOrElse()

Bu metot, Optional bir değer içeriyorsa belirli bir işlemi gerçekleştirir, aksi takdirde başka bir işlemi gerçekleştirir. ifPresent() ve orElse() kombinasyonuna benzer, ancak daha kısadır.

or()

Eğer Optional boşsa, bu metot verilen Supplier tarafından sağlanan başka bir Optional döndürür.

stream()

Bu metot, Optional bir değer içeriyorsa tek elemanlı bir Stream döndürür, aksi takdirde boş bir Stream döndürür. Bu, Optional değerlerini doğrudan Stream API zincirine entegre etmeyi kolaylaştırır.

Örnek:

import java.util.Optional;

public class OptionalEnhancementsExample {
    public static void main(String[] args) {
        Optional name = Optional.of("Alice");
        Optional emptyName = Optional.empty();

        // ifPresentOrElse() örneği
        name.ifPresentOrElse(
            val -> System.out.println("Merhaba " + val + "!"),
            () -> System.out.println("İsim yok.")
        ); // Çıktı: Merhaba Alice!

        emptyName.ifPresentOrElse(
            val -> System.out.println("Merhaba " + val + "!"),
            () -> System.out.println("İsim yok.")
        ); // Çıktı: İsim yok.

        // or() örneği
        Optional result1 = name.or(() -> Optional.of("Varsayılan İsim"));
        System.out.println("or() ile sonuç 1: " + result1.get()); // Çıktı: Alice

        Optional result2 = emptyName.or(() -> Optional.of("Varsayılan İsim"));
        System.out.println("or() ile sonuç 2: " + result2.get()); // Çıktı: Varsayılan İsim

        // stream() örneği
        name.stream().forEach(s -> System.out.println("Stream'den gelen: " + s)); // Çıktı: Stream'den gelen: Alice
        emptyName.stream().forEach(s -> System.out.println("Stream'den gelen: " + s)); // Hiçbir şey yazdırmaz
    }
}

CompletableFuture API Geliştirmeleri

CompletableFuture, Java 8’de asenkron programlamayı kolaylaştırmak için tanıtılmıştı. Java 9, bu API’ye zaman aşımı ve yeni fabrika metotları gibi eklemelerle daha fazla esneklik ve kontrol sağladı.

orTimeout() ve completeOnTimeout()

Bu metotlar, asenkron bir işlemin belirli bir süre içinde tamamlanmaması durumunda otomatik olarak bir eylem gerçekleştirmeyi sağlar.
* orTimeout(long timeout, TimeUnit unit): Belirtilen süre içinde tamamlanmazsa TimeoutException ile tamamlanır.
* completeOnTimeout(T value, long timeout, TimeUnit unit): Belirtilen süre içinde tamamlanmazsa verilen değerle tamamlanır.

Örnek:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CompletableFutureEnhancementsExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // orTimeout() örneği
        CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3); // 3 saniye bekler
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "İşlem Tamamlandı";
        });

        try {
            String result = future1.orTimeout(1, TimeUnit.SECONDS).get(); // 1 saniye sonra zaman aşımı
            System.out.println("Sonuç 1: " + result);
        } catch (TimeoutException e) {
            System.err.println("Hata 1: İşlem zaman aşımına uğradı!"); // Bu kısım çalışır
        }

        // completeOnTimeout() örneği
        CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3); // 3 saniye bekler
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "İşlem Tamamlandı";
        });

        String result2 = future2.completeOnTimeout("Varsayılan Değer", 1, TimeUnit.SECONDS).get();
        System.out.println("Sonuç 2: " + result2); // Çıktı: Varsayılan Değer
    }
}

Process API Geliştirmeleri

Java 9, işletim sistemi süreçleriyle (processes) etkileşim kurmak için daha güçlü ve standart bir API sunar. Bu geliştirmeler, çalışan süreçler hakkında bilgi almayı, süreçleri yönetmeyi ve yeni süreçler başlatmayı kolaylaştırır.

Önemli Özellikler

* ProcessHandle: İşletim sistemindeki bir süreci temsil eden yeni bir arayüz.
* ProcessHandle.current(): Mevcut Java sürecinin ProcessHandle‘ını döndürür.
* ProcessHandle.Info: Bir süreç hakkında (kullanıcı, komut, argümanlar, başlangıç zamanı vb.) bilgi sağlayan bir arayüz.
* onExit(): Bir sürecin sonlanması durumunda tetiklenecek asenkron bir eylem tanımlamak için CompletableFuture döndürür.
* children() ve descendants(): Bir sürecin alt süreçlerini veya tüm torunlarını almak için metotlar.

Örnek:

import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class ProcessApiEnhancementsExample {
    public static void main(String[] args) throws IOException, InterruptedException {
        // Mevcut sürecin ProcessHandle'ını al
        ProcessHandle currentProcess = ProcessHandle.current();
        System.out.println("Mevcut Süreç PID: " + currentProcess.pid());

        // Süreç bilgisi al
        ProcessHandle.Info info = currentProcess.info();
        System.out.println("Kullanıcı: " + info.user().orElse("Bilinmiyor"));
        System.out.println("Komut: " + info.command().orElse("Bilinmiyor"));
        System.out.println("Başlangıç Zamanı: " + info.startInstant().orElse(null));
        System.out.println("Toplam CPU Süresi: " + info.totalCpuDuration().orElse(null));

        // Yeni bir süreç başlat ve izle
        ProcessBuilder pb = new ProcessBuilder("notepad.exe"); // Windows için
        // ProcessBuilder pb = new ProcessBuilder("sleep", "5"); // Linux/macOS için
        Process p = pb.start();

        System.out.println("Yeni süreç başlatıldı, PID: " + p.pid());

        // Süreç sonlandığında bir eylem gerçekleştir
        CompletableFuture onExitFuture = p.onExit();
        onExitFuture.thenAccept(ph -> {
            System.out.println("Süreç PID " + ph.pid() + " sonlandı. Çıkış kodu: " + ph.exitValue().orElse(-1));
        });

        // 10 saniye bekle (notepad'i kapatmanız için)
        TimeUnit.SECONDS.sleep(10);
        
        // Sürecin hala çalışıp çalışmadığını kontrol et
        if (p.isAlive()) {
            System.out.println("Süreç hala çalışıyor, zorla sonlandırılıyor.");
            p.destroyForcibly(); // Süreci zorla sonlandır
        }
        
        // Alt süreçleri listele (sadece örnek, notepad'in alt süreci olmayabilir)
        System.out.println("\nMevcut sürecin alt süreçleri:");
        currentProcess.children().forEach(child -> System.out.println("  Alt Süreç PID: " + child.pid()));
    }
}

Multi-Release JARs (MRJARs)

Java 9, Multi-Release JAR (MRJAR) özelliğini tanıttı. Bu, tek bir JAR dosyasının, farklı Java sürümleri için farklı sınıf dosyaları içermesine olanak tanır. Bu sayede, kütüphane geliştiricileri, eski Java sürümleriyle uyumluluğu korurken, aynı zamanda daha yeni Java sürümlerinin sunduğu yeni özellikleri kullanabilirler.

Faydaları

* Geriye Dönük Uyumluluk: Tek bir JAR dosyası, Java 8, Java 9, Java 10 vb. gibi farklı Java sürümlerinde çalışabilir.
* Yeni Özelliklerden Yararlanma: Geliştiriciler, eski sürümlerde çalışırken yeni Java sürümlerinin getirdiği API’leri veya sentaksı kullanabilirler.
* Dağıtım Kolaylığı: Farklı Java sürümleri için ayrı JAR dosyaları oluşturmaya ve yönetmeye gerek kalmaz.

Çalışma Prensibi

MRJAR’lar, META-INF/versions dizini altında sürüme özel dizinler (örneğin, META-INF/versions/9, META-INF/versions/10) içerir. JVM, bir sınıfı yüklerken, kendi sürümüne en uygun olan sınıf dosyasını bu dizinlerden bulmaya çalışır. Eğer sürüme özel bir sınıf bulunamazsa, kök dizindeki (base) sınıf kullanılır.

Örnek Yapı:

my-library.jar
├── com/example/MyClass.class        (Java 8 uyumlu versiyon)
├── META-INF/
│   ├── MANIFEST.MF
│   └── versions/
│       └── 9/
│           └── com/example/MyClass.class (Java 9'a özel versiyon)
│           └── com/example/Java9SpecificClass.class (Sadece Java 9'da olan yeni sınıf)

Eğer bu JAR Java 8’de çalıştırılırsa, com/example/MyClass.class (kök dizindeki) yüklenecektir. Eğer Java 9’da çalıştırılırsa, META-INF/versions/9/com/example/MyClass.class yüklenecektir.

Diğer Önemli Özellikler ve Geliştirmeler

Gelişmiş @Deprecated Anotasyonu

Java 9, @Deprecated anotasyonuna iki yeni eleman ekledi:
* forRemoval=true: Bu eleman, API’nin gelecekteki bir sürümde tamamen kaldırılacağını belirtir. Geliştiricilere API’yi kullanmayı bırakmaları için net bir sinyal verir.
* since="versiyon": Bu eleman, API’nin hangi sürümden itibaren kullanımdan kaldırıldığını belirtir.

Örnek:

@Deprecated(forRemoval = true, since = "9")
public class OldUtility {
    public void doSomethingOld() {
        System.out.println("Bu metot gelecekte kaldırılacak.");
    }
}

Bu, geliştiricilerin kod tabanlarını daha proaktif bir şekilde güncellemelerine yardımcı olur.

Unified JVM Logging

Java 9, JVM’nin tüm bileşenlerinden gelen log mesajlarını tek, standart ve yapılandırılabilir bir sistem altında birleştirdi. Bu, JVM’nin iç işleyişini izlemeyi ve hata ayıklamayı çok daha kolay hale getirir. -Xlog komut satırı argümanı ile loglama seviyeleri, çıktı hedefleri ve log içeriği detaylı bir şekilde yapılandırılabilir.

Spin-Wait Hints

Bu özellik, meşgul bekleyen (spin-wait) döngülerinde işlemciye performans ipuçları sağlamak için Thread.onSpinWait() metodunu sunar. Bu, düşük seviyeli eşzamanlılık mekanizmalarının performansını iyileştirmeye yardımcı olabilir.

Segmented Code Cache

JVM’nin kod önbelleği, farklı türdeki derlenmiş kodları (örneğin, non-profiled, profiled, non-method) ayrı segmentlerde depolamak üzere yeniden yapılandırıldı. Bu, kod önbelleği kullanımını optimize eder ve performansı artırır.

Sonuç

Java 9, özellikle Java Platform Modül Sistemi (JPMS) ile Java ekosisteminde köklü bir dönüşüm başlattı. Modülerlik, sadece JDK’nın kendisini değil, aynı zamanda büyük ölçekli uygulamaların geliştirilme ve dağıtılma şeklini de derinden etkiledi. JShell gibi geliştirici verimliliğini artıran araçlar, HTTP/2 istemcisi gibi modern ağ iletişimi yetenekleri, koleksiyon fabrika metotları ve Stream/Optional/CompletableFuture API’lerindeki iyileştirmeler, Java 9’u geliştiriciler için heyecan verici ve güçlü bir sürüm haline getirdi.

Bu özellikler, Java’nın gelecekteki büyümesi ve modern yazılım geliştirme ihtiyaçlarına uyum sağlaması için sağlam bir temel oluşturdu. Java 9’un getirdiği yenilikler, daha güvenli, daha performanslı ve daha yönetilebilir Java uygulamaları oluşturmanın yolunu açarken, geliştiricilere de daha verimli ve keyifli bir deneyim sunmaktadır. Java ekosistemi, bu yeniliklerle birlikte daha da dinamik bir yapıya bürünmüş ve gelecekteki gelişmelere zemin hazırlamıştır.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.