Eğitim Portalı/Sürüm Analizi/Java 12 (Mart 2019) — Detaylı Sürüm Analizi
Sürüm Analizi05-java-versiyon-analizi/05-Java-12

Java 12 (Mart 2019) — Detaylı Sürüm Analizi

> Çıkış Tarihi: 19 Mart 2019 > Sürüm Tipi: Standart (feature) sürüm — LTS DEĞİL > Destek Modeli: Java 12, 6 aylık sürüm döngüsünün bir parçasıdır. Java 13 (Eylül 2019) çıktığında resmî destek bitmiştir. Yani Java 12 k…

Java 12 (Mart 2019) — Detaylı Sürüm Analizi

Çıkış Tarihi: 19 Mart 2019 Sürüm Tipi: Standart (feature) sürüm — LTS DEĞİL Destek Modeli: Java 12, 6 aylık sürüm döngüsünün bir parçasıdır. Java 13 (Eylül 2019) çıktığında resmî destek bitmiştir. Yani Java 12 kısa ömürlü bir "ara sürümdür"; üretimde uzun süre kalması beklenmez. Bu sürümdeki kalıcı özellikler sonraki LTS olan Java 17'ye taşınmıştır.


İçindekiler

  1. Genel Bakış
  2. Java 11'den Java 12'ye Geçişte Ne Değişti?
  3. Özellik 1: Switch Expressions (Preview)
  4. Özellik 2: Compact Number Formatting
  5. Özellik 3: Teeing Collector
  6. Özellik 4: Shenandoah GC (Deneysel)
  7. Özellik 5: JVM Constants API
  8. Preview → Kalıcı Evrimi
  9. Avantaj / Dezavantaj / Risk Tablosu
  10. Dosya Listesi

Genel Bakış

Java 12, 6 aylık hızlı sürüm modelinin üçüncü adımıdır (Java 9 → 10 → 11 → 12). Java 11 büyük bir LTS sürümüydü; Java 12 ise daha çok geliştirici ergonomisi (kod yazma kolaylığı) ve çöp toplayıcı (GC) iyileştirmeleri üzerine odaklandı.

Bu sürümün en dikkat çeken yeniliği, ilk kez "Preview Feature" (önizleme özelliği) kavramının uygulamaya konmasıdır. Switch Expressions bu mekanizmayla geldi: özellik tam olgunlaşmadan önce topluluğun denemesi ve geri bildirim vermesi sağlandı.

Java 12'de gelen başlıca yenilikler:

#ÖzellikJEP / TipDurum (Java 12'de)
1Switch ExpressionsJEP 325Preview (önizleme)
2Compact Number FormattingJDK-8188147Kalıcı
3Teeing CollectorJDK-8209685Kalıcı
4Shenandoah GCJEP 189Deneysel (Experimental)
5JVM Constants APIJEP 334Kalıcı
6Default CDS ArchivesJEP 341Kalıcı (performans)
7Abortable Mixed Collections for G1JEP 344Kalıcı (G1 iyileştirme)
8Promptly Return Unused Committed Memory from G1JEP 346Kalıcı (G1 iyileştirme)

Bu dokümanda istenen 5 ana özellik (1–5) detaylandırılmıştır.


Java 11'den Java 12'ye Geçişte Ne Değişti?

KonuJava 11Java 12
switchYalnızca klasik statement (deyim), break zorunlu, fall-through riskiYeni expression söz dizimi (->, yield) — preview olarak eklendi
Sayı kısaltmaEl ile bölme + string birleştirmeNumberFormat.getCompactNumberInstance() ile dile duyarlı kısaltma
Stream'den çoklu özetİki ayrı stream geçişi veya özel collectorCollectors.teeing() ile tek geçişte iki sonuç
Düşük gecikmeli GCZGC (deneysel, JDK 11)ZGC + Shenandoah (deneysel olarak eklendi)
Dinamik sabitlerYokjava.lang.constant paketi (JVM Constants API)
Başlangıç hızıCDS arşivi manuel oluşturulmalıVarsayılan CDS arşivi ile daha hızlı başlangıç

Geçiş notu: Java 11 → 12 geçişi çoğu uygulama için kaynak-uyumludur (source-compatible). Var olan kod büyük ihtimalle değişiklik gerektirmeden derlenir. Tek dikkat edilecek nokta, eğer preview özellik (Switch Expressions) kullanacaksanız --enable-preview bayrağının gerekmesidir.


Özellik 1: Switch Expressions (Preview)

İlgili dosya: SwitchExpressionsPreview.java

NEDİR?

switch yapısının, bir deyim (statement) olmanın yanı sıra artık bir ifade (expression) olarak da kullanılabilmesidir; yani doğrudan bir değer üretip döndürebilir. Yeni söz dizimi -> (ok) operatörünü, çoklu case etiketlerini (case 1, 2, 3 ->) ve çok satırlı bloklarda değer döndürmek için yield anahtar sözcüğünü getirir.

NEDEN GELDİ? (Hangi problemi çözüyor)

Klasik switch üç büyük sorun barındırıyordu:

  1. Fall-through riski: Her case sonunda break yazmayı unutmak, sessiz ve bulunması zor bir hataya yol açar.
  2. Değer üretememe: switch doğrudan değer döndüremediği için, her seferinde ekstra bir değişken tanımlayıp her case içinde ona atama yapmak gerekirdi (gürültülü, tekrarlı kod).
  3. Eksik kapsama uyarısı yok: Klasik switch tüm durumların ele alındığını derleme zamanında kontrol etmez.

NE İŞE YARAR?

  • break ihtiyacını ortadan kaldırır → fall-through hatası imkânsız hale gelir.
  • switch'i sağ tarafa yazıp doğrudan bir değişkene atayabilirsiniz: int x = switch(...) { ... };
  • Birden çok etiketi tek satırda gruplayabilirsiniz: case PAZARTESI, SALI, CARSAMBA -> ...

NEREDE KOLAYLIK SAĞLAR?

Çok dallı eşleme (mapping) mantıklarında: enum → davranış, HTTP kodu → mesaj, gün → çalışma saati, durum makineleri (state machine). Kod daha kısa, daha okunur ve daha güvenli olur.

ESKİ vs YENİ Kod Karşılaştırması

ESKİ (Java 11 ve öncesi — statement):

int calismaSaati;
switch (gun) {
    case 1: case 2: case 3: case 4: case 5:
        calismaSaati = 8;
        break;            // <-- unutulursa fall-through!
    case 6:
        calismaSaati = 4;
        break;
    case 7:
        calismaSaati = 0;
        break;
    default:
        calismaSaati = -1;
}

YENİ (Java 12 preview — expression):

int calismaSaati = switch (gun) {
    case 1, 2, 3, 4, 5 -> 8;   // coklu case, break yok
    case 6             -> 4;
    case 7             -> 0;
    default            -> -1;
};                              // <-- expression, sonunda ';'

Çok satırlı işlem gerektiğinde yield:

String aciklama = switch (gun) {
    case 6 -> {
        int saat = 4;
        yield "Cumartesi - " + saat + " saat";   // yield ile deger dondur
    }
    default -> "Diger";
};

GERÇEK HAYAT ÖRNEĞİ

Bir REST API'de gelen HTTP durum kodunu kullanıcıya gösterilecek Türkçe mesaja çevirmek (200 → "Başarılı", 404 → "Bulunamadı", 500 → "Sunucu hatası"). Tek switch ifadesiyle, ekstra değişken ve break olmadan temiz bir şekilde çözülür. Detaylı çalışan örnek için SwitchExpressionsPreview.java dosyasına bakın.

Derleme Notu (PREVIEW)

Java 12'de bu özellik preview olduğu için:

javac --release 12 --enable-preview SwitchExpressionsPreview.java
java  --enable-preview SwitchExpressionsPreview

Java 14+ (örn. sisteminizdeki Java 21) ile özellik kalıcı olduğundan hiçbir bayrak gerekmeden derlenir.


Özellik 2: Compact Number Formatting

İlgili dosya: CompactNumberFormat.java

NEDİR?

NumberFormat.getCompactNumberInstance(Locale, Style) ile büyük sayıları kısa, insan-dostu biçimde gösterme yeteneğidir. 1000 → "1K", 1000000 → "1M" gibi. İki stil vardır: SHORT ("1K") ve LONG ("1 thousand").

NEDEN GELDİ? (Hangi problemi çözüyor)

Sosyal medya beğeni sayısı, takipçi, görüntülenme, dosya boyutu gibi değerleri "1.2K", "3.4M" şeklinde göstermek çok yaygındır. Önceden bunu el ile yapmak gerekiyordu ve bu yaklaşım:

  • Dile duyarlı değildi (Türkçe'de "bin/milyon", İngilizce'de "K/M" gerekir),
  • yuvarlama ve çoğullaşma kurallarını yanlış yönetiyordu,
  • her projede yeniden yazılıyordu (kod tekrarı).

NE İŞE YARAR?

Sayıyı, verdiğiniz locale'in (dilin) kurallarına göre otomatik olarak kısaltır ve doğru eki (K/M/B veya bin/milyon/milyar) seçer.

NEREDE KOLAYLIK SAĞLAR?

Dashboard/istatistik ekranları, sosyal medya uygulamaları, analitik panelleri, dosya yöneticileri, uluslararası (i18n) uygulamalar.

ESKİ vs YENİ Kod Karşılaştırması

ESKİ:

String kisalt(long sayi) {
    if (sayi < 1_000)            return String.valueOf(sayi);
    else if (sayi < 1_000_000)   return (sayi / 1_000) + "K";
    else if (sayi < 1_000_000_000) return (sayi / 1_000_000) + "M";
    else                         return (sayi / 1_000_000_000) + "B";
}
// Sorun: locale bilmez, yuvarlama kaba, "1.234.567" -> "1M" (ondalik kaybi)

YENİ:

NumberFormat nf = NumberFormat.getCompactNumberInstance(
        Locale.forLanguageTag("tr-TR"), NumberFormat.Style.SHORT);
nf.setMaximumFractionDigits(1);
nf.format(1_234_567);   // -> "1,2 Mn"  (Turkce, dile duyarli!)

GERÇEK HAYAT ÖRNEĞİ

  • Sosyal medya: 1.234.567 takipçi → "1,2 Mn" (Türkçe) veya "1.2M" (İngilizce).
  • Dosya boyutu: 5.242.880 byte → "5.2MB".

CompactNumberFormat.java dosyası SHORT/LONG stillerini ve Türkçe/İngilizce locale'leri yan yana gösterir.

Derleme Notu

Bu özellik Java 12'de kalıcıdır (preview değil); ekstra bayrak gerekmez.


Özellik 3: Teeing Collector

İlgili dosya: TeeingCollector.java

NEDİR?

Collectors.teeing(downstream1, downstream2, merger): Bir stream'in elemanlarını aynı anda iki ayrı collector'a gönderir, her ikisi de kendi sonucunu üretir, sonra bir merger (BiFunction) bu iki sonucu tek bir sonuca birleştirir. Adı tesisattaki "T-bağlantısı"ndan (tee) gelir: tek akış ikiye ayrılır, işlenir, tekrar birleşir.

NEDEN GELDİ? (Hangi problemi çözüyor)

Bir koleksiyondan iki farklı özet (örn. hem ortalama hem toplam) hesaplamak için önceden ya stream iki kez dolaşılırdı (iki kat iş yükü) ya da karmaşık özel bir collector yazılırdı. teeing bu işi tek geçişte ve okunabilir biçimde çözer.

NE İŞE YARAR?

Tek bir veri geçişiyle iki bağımsız istatistiği hesaplayıp birleştirmenizi sağlar: ortalama + toplam, min + max, geçen sayısı + ortalama, vb.

NEREDE KOLAYLIK SAĞLAR?

Raporlama, analitik, finansal hesaplamalar; özellikle büyük veri setlerinde çift geçişi tek geçişe indirerek performans kazandırır.

ESKİ vs YENİ Kod Karşılaştırması

ESKİ (iki geçiş):

double ortalama = urunler.stream().mapToDouble(Urun::fiyat).average().orElse(0);
double toplam   = urunler.stream().mapToDouble(Urun::fiyat).sum();   // 2. gecis!

YENİ (tek geçiş, teeing):

var ozet = urunler.stream().collect(
    Collectors.teeing(
        Collectors.averagingDouble(Urun::fiyat),   // 1. collector
        Collectors.summingDouble(Urun::fiyat),     // 2. collector
        (ort, top) -> "Ortalama=" + ort + ", Toplam=" + top   // merger
    )
);

GERÇEK HAYAT ÖRNEĞİ

Bir sınıfın not listesinden tek geçişte hem sınıf ortalamasını hem de 50 ve üzeri alan (geçen) öğrenci sayısını hesaplayıp bir rapor nesnesinde birleştirmek. TeeingCollector.java dosyasında ürün fiyatları, öğrenci notları ve min/max fark örnekleri vardır.

Derleme Notu

Bu özellik Java 12'de kalıcıdır; ekstra bayrak gerekmez.


Özellik 4: Shenandoah GC (Deneysel)

Bu özellik için ayrı bir .java dosyası yoktur; çünkü GC seçimi kodla değil, JVM bayraklarıyla yapılır.

NEDİR?

Shenandoah, Red Hat tarafından geliştirilen düşük duraklama süreli (low-pause-time) bir çöp toplayıcıdır. Java 12'de deneysel (experimental) olarak eklendi.

NEDEN GELDİ? (Hangi problemi çözüyor)

Klasik GC'lerde (Parallel, G1) "stop-the-world" duraklamaları, heap (yığın) büyüdükçe uzar. 100+ GB heap'lerde bu duraklamalar saniyeler sürebilir; düşük gecikme isteyen uygulamalar (oyun sunucuları, finans, gerçek zamanlı sistemler) için kabul edilemez.

NE İŞE YARAR?

Shenandoah, çöp toplama işinin büyük kısmını (özellikle nesnelerin taşınması/evacuation) uygulama thread'leriyle eş zamanlı (concurrent) olarak yapar. Böylece duraklama süresi heap boyutundan büyük ölçüde bağımsız ve çok kısa (genellikle birkaç milisaniye) kalır.

NEREDE KOLAYLIK SAĞLAR?

Büyük heap'li, düşük gecikme kritik uygulamalar.

ZGC ile Karşılaştırma

Java'da iki düşük-gecikme GC vardır: ZGC (Java 11) ve Shenandoah (Java 12). İkisi de eş zamanlı çalışır; Shenandoah daha çok OpenJDK/Red Hat ekosisteminde öne çıkar.

Nasıl Etkinleştirilir? (Java 12'de deneysel olduğu için)

java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC MyApp

Not: Shenandoah, Java 15'te (JEP 379) production-ready (kalıcı) hale geldi ve artık -XX:+UseShenandoahGC tek başına yeterlidir.

Avantaj / Dezavantaj

  • Avantaj: Çok kısa, öngörülebilir duraklama; heap boyutundan bağımsız gecikme.
  • Dezavantaj: Eş zamanlı çalışma CPU ve toplam throughput (iş hacmi) maliyeti getirir; throughput öncelikli batch işler için ideal değildir.

Özellik 5: JVM Constants API

Bu özellik için ayrı .java dosyası yoktur; çünkü doğrudan uygulama geliştiricileri değil, daha çok derleyici, çerçeve (framework) ve bytecode araçları geliştirenler için bir altyapı API'sidir.

NEDİR?

java.lang.constant paketi (JEP 334) ile gelen, constant pool (sabit havuzu) girdilerini sembolik olarak temsil eden bir API'dir. Başlıca tipler: ConstantDesc, ClassDesc, MethodTypeDesc, MethodHandleDesc, DynamicConstantDesc.

NEDEN GELDİ? (Hangi problemi çözüyor)

JVM'in class dosyalarındaki constant pool, sınıf, metot ve yöntem tipi referanslarını tutar. Bytecode üreten/işleyen araçlar (derleyiciler, ASM benzeri kütüphaneler, dinamik dil çalışma zamanları) bu sabitleri güvenli ve doğrulanabilir biçimde temsil etmek istiyordu. Önceden bunun standart, güvenli bir yolu yoktu; ham Class nesneleri yüklemeyi zorunlu kılıyor, bu da hatalara ve gereksiz sınıf yüklemelerine yol açıyordu.

NE İŞE YARAR?

Bir sabiti (örneğin "java.lang.String sınıfı") gerçekten o sınıfı yüklemeden, salt sembolik (nominal) bir tanımlayıcıyla ifade etmeyi sağlar. Bu, özellikle invokedynamic ve constantdynamic (JEP 309, Java 11) gibi düşük seviye mekanizmalarla çalışan araçlar için kritiktir.

NEREDE KOLAYLIK SAĞLAR?

  • Derleyici ve bytecode kütüphaneleri (örn. javac, jlink, dinamik dil run-time'ları).
  • Çoğu uygulama geliştiricisi bu API'yi doğrudan kullanmaz; bu daha çok bir "JDK iç altyapısı / araç geliştirici" API'sidir.

Küçük Örnek (kavramsal)

import java.lang.constant.ClassDesc;

// "java.lang.String" sinifini YUKLEMEDEN sembolik olarak tanimla:
ClassDesc stringDesc = ClassDesc.of("java.lang.String");
System.out.println(stringDesc.descriptorString());  // -> "Ljava/lang/String;"

Preview → Kalıcı Evrimi

Java 12'nin en önemli kavramsal katkılarından biri Preview Feature mekanizmasıdır. Bir özellik önce "preview" olarak yayınlanır, topluluk dener ve geri bildirim verir, gerekirse söz dizimi düzeltilir, sonra "kalıcı (standard)" olur.

Switch Expressions evrimi:

Java SürümüTarihDurumNot
Java 12Mart 2019Preview (JEP 325)İlk hali; ->, çoklu case, blok bloklarında break <deger>
Java 13Eylül 20192. Preview (JEP 354)break <deger> yerine yield anahtar sözcüğü getirildi
Java 14Mart 2020KALICI (JEP 361)Standart hale geldi; artık --enable-preview gerekmez

Önemli ders: Java 12'de break <değer>; ile değer döndürülüyordu; Java 13'te bu, daha okunur olan yield ile değiştirildi. Bu, preview mekanizmasının neden değerli olduğunu gösterir: söz dizimi, kalıcı olmadan önce iyileştirilebildi. Bu klasördeki SwitchExpressionsPreview.java dosyası, taşınabilirlik için (Java 14+ ile bayraksız derlensin diye) yield söz dizimini kullanır.

Diğer preview özelliklerin de benzer yolu izlediğini unutmayın (örn. Text Blocks: Java 13 preview → Java 15 kalıcı; Records: Java 14 preview → Java 16 kalıcı; Pattern Matching for switch: Java 17 preview → Java 21 kalıcı).


Avantaj / Dezavantaj / Risk Tablosu

Avantajlar

  • Switch Expressions: Daha kısa, daha güvenli (fall-through yok), değer döndürebilen, derleyici kapsama kontrolü yapabilen kod.
  • Compact Number Formatting: Dile duyarlı, standart, tekrar yazılmayan sayı kısaltma.
  • Teeing Collector: Tek geçişte iki istatistik → performans ve okunabilirlik.
  • Shenandoah GC: Çok düşük, öngörülebilir GC duraklamaları.
  • Daha hızlı başlangıç: Varsayılan CDS arşivi (JEP 341) ile JVM daha hızlı açılır.

Dezavantajlar

  • Switch Expressions Java 12'de henüz preview — üretimde kullanmak risklidir (söz dizimi değişebilir, ki gerçekten değişti).
  • Shenandoah ve ZGC deneyseldir ve eş zamanlı çalışma throughput maliyeti getirir.
  • JVM Constants API sıradan uygulama geliştiricisine doğrudan fayda sağlamaz.

Riskler

  • Preview kullanımı: --enable-preview ile derlenen sınıflar yalnızca aynı major sürümün çalışma zamanında çalışır ve sürümler arası taşınamaz. Bu yüzden preview özellikler production'da önerilmez.
  • Kısa ömür: Java 12 LTS değildir; Java 13 çıkınca desteği bitmiştir. Üretim için Java 17 / Java 21 gibi LTS sürümler tercih edilmelidir. Java 12'nin kalıcı özellikleri zaten bu LTS sürümlerde mevcuttur.

Bu Klasördeki Dosyalar

DosyaAçıklama
README.mdBu doküman
SwitchExpressionsPreview.javaSwitch Expressions (preview): eski statement vs yeni ->/yield; gün→çalışma saati ve HTTP kodu→mesaj örnekleri
TeeingCollector.javaCollectors.teeing: ürün fiyatı (ortalama+toplam), öğrenci notu (ortalama+geçen sayısı), min/max fark örnekleri
CompactNumberFormat.javagetCompactNumberInstance: SHORT/LONG stilleri, Türkçe/İngilizce locale, sosyal medya ve dosya boyutu örnekleri

Derleme ve Çalıştırma

# Java 14+ (örn. Java 21) ile - hicbir ek bayrak gerekmez:
javac SwitchExpressionsPreview.java TeeingCollector.java CompactNumberFormat.java
java SwitchExpressionsPreview
java TeeingCollector
java CompactNumberFormat

# Java 12 ile Switch Expressions'i derlemek isterseniz (preview):
javac --release 12 --enable-preview SwitchExpressionsPreview.java
java  --enable-preview SwitchExpressionsPreview

Bu klasördeki tüm .java dosyaları Java 21 ile derlenip çalıştırılarak doğrulanmıştır.

Kod Örnekleri(3)

CompactNumberFormat

çalıştırılabilir
CompactNumberFormat.java
1// =============================================================================
2//  CompactNumberFormat.java
3//  Java 12 - Compact Number Formatting (JEP 357 / JDK-8188147)
4// =============================================================================
5//
6//  NEDIR?
7//  ----------------------------------------------------------------------------
8//  NumberFormat.getCompactNumberInstance(...) Java 12'de geldi.
9//  Buyuk sayilari, insanlarin kolayca okuyabilecegi KISA (kompakt) bicimde
10//  gosterir:
11//        1.000      -> "1K"   (Ingilizce, SHORT)  /  "1B" (Turkce, SHORT)
12//        1.000.000  -> "1M"   (Ingilizce)         /  "1Mn" (Turkce)
13//        1.000      -> "1 thousand" (Ingilizce, LONG) / "1 bin" (Turkce, LONG)
14//
15//  NEDEN GELDI / HANGI PROBLEMI COZER?
16//  ----------------------------------------------------------------------------
17//  Onceden sosyal medya beğeni, takipci, goruntuleme sayisi gibi degerleri
18//  "1.2K", "3.4M" seklinde gostermek icin EL ile bolme + string birlestirme
19//  yapmak gerekiyordu. Bu kod:
20//    - locale'e (dile) gore DOGRU ekleri (K/M/B/Bn) bilmez,
21//    - cogullasmayi/yuvarlamayi yanlis yapar,
22//    - her uygulamada tekrar tekrar yazilir.
23//  Compact Number Formatting bu isi locale-aware (dile duyarli) ve standart
24//  bicimde halleder.
25//
26//  STILLER:
27//    - NumberFormat.Style.SHORT : "1K", "1M", "1B"
28//    - NumberFormat.Style.LONG  : "1 thousand", "1 million", "1 bin"
29//
30//  DERLEME / CALISTIRMA (Java 12'de KALICI; preview degil):
31//        javac CompactNumberFormat.java
32//        java  CompactNumberFormat
33// =============================================================================
34
35import java.text.NumberFormat;
36import java.util.Locale;
37
38public class CompactNumberFormat {
39
40    public static void main(String[] args) {
41
42        System.out.println("================================================");
43        System.out.println(" JAVA 12 - COMPACT NUMBER FORMATTING ORNEKLERI");
44        System.out.println("================================================\n");
45
46        long[] sayilar = {123, 1_000, 1_500, 12_345, 1_000_000, 2_300_000, 1_000_000_000L};
47
48        // ---------------------------------------------------------------------
49        // ESKI YONTEM: Manuel bolme + string birlestirme
50        // ---------------------------------------------------------------------
51        // Dezavantajlari:
52        //   - Dil/locale farkliliklarini bilmez (hep "K","M" yazar).
53        //   - Yuvarlama, ondalik ve cogul kurallari elle yonetilir, hataya acik.
54        //   - Her projede yeniden yazilir.
55        System.out.println(">>> ESKI YONTEM (manuel bolme + string birlestirme):");
56        for (long s : sayilar) {
57            System.out.println("   " + s + " -> " + manuelKisalt(s));
58        }
59        System.out.println();
60
61        // ---------------------------------------------------------------------
62        // YENI YONTEM - INGILIZCE (US) locale, SHORT stil
63        // ---------------------------------------------------------------------
64        System.out.println(">>> YENI YONTEM - Ingilizce (US), SHORT stil:");
65        NumberFormat enShort = NumberFormat.getCompactNumberInstance(
66                Locale.US, NumberFormat.Style.SHORT);
67        for (long s : sayilar) {
68            System.out.println("   " + s + " -> " + enShort.format(s));
69        }
70        System.out.println();
71
72        // ---------------------------------------------------------------------
73        // YENI YONTEM - INGILIZCE (US) locale, LONG stil
74        // ---------------------------------------------------------------------
75        System.out.println(">>> YENI YONTEM - Ingilizce (US), LONG stil:");
76        NumberFormat enLong = NumberFormat.getCompactNumberInstance(
77                Locale.US, NumberFormat.Style.LONG);
78        for (long s : sayilar) {
79            System.out.println("   " + s + " -> " + enLong.format(s));
80        }
81        System.out.println();
82
83        // ---------------------------------------------------------------------
84        // YENI YONTEM - TURKCE (tr-TR) locale, SHORT stil
85        // ---------------------------------------------------------------------
86        System.out.println(">>> YENI YONTEM - Turkce (tr-TR), SHORT stil:");
87        Locale tr = Locale.forLanguageTag("tr-TR");
88        NumberFormat trShort = NumberFormat.getCompactNumberInstance(
89                tr, NumberFormat.Style.SHORT);
90        for (long s : sayilar) {
91            System.out.println("   " + s + " -> " + trShort.format(s));
92        }
93        System.out.println();
94
95        // ---------------------------------------------------------------------
96        // YENI YONTEM - TURKCE (tr-TR) locale, LONG stil
97        // ---------------------------------------------------------------------
98        System.out.println(">>> YENI YONTEM - Turkce (tr-TR), LONG stil:");
99        NumberFormat trLong = NumberFormat.getCompactNumberInstance(
100                tr, NumberFormat.Style.LONG);
101        for (long s : sayilar) {
102            System.out.println("   " + s + " -> " + trLong.format(s));
103        }
104        System.out.println();
105
106        // ---------------------------------------------------------------------
107        // ONDALIK HASSASIYET: setMaximumFractionDigits ile "1.5K" gibi gosterim
108        // ---------------------------------------------------------------------
109        // Varsayilan olarak compact format ondalik gostermez (1.500 -> "2K").
110        // Daha hassas gostermek icin maksimum ondalik basamak sayisini artiririz.
111        System.out.println(">>> ONDALIK HASSASIYET (1 ondalik basamak, US/SHORT):");
112        NumberFormat hassas = NumberFormat.getCompactNumberInstance(
113                Locale.US, NumberFormat.Style.SHORT);
114        hassas.setMaximumFractionDigits(1);
115        long[] hassasSayilar = {1_500, 12_345, 2_300_000};
116        for (long s : hassasSayilar) {
117            System.out.println("   " + s + " -> " + hassas.format(s));
118        }
119        System.out.println();
120
121        // ---------------------------------------------------------------------
122        // GERCEK HAYAT ORNEGI 1: Sosyal medya begeni / takipci gosterimi
123        // ---------------------------------------------------------------------
124        System.out.println(">>> GERCEK HAYAT: Sosyal medya istatistikleri (tr-TR):");
125        NumberFormat sosyal = NumberFormat.getCompactNumberInstance(
126                tr, NumberFormat.Style.SHORT);
127        sosyal.setMaximumFractionDigits(1);
128        System.out.println("   Takipci  : " + sosyal.format(1_234_567));
129        System.out.println("   Begeni   : " + sosyal.format(45_900));
130        System.out.println("   Goruntulenme: " + sosyal.format(12_300_000));
131        System.out.println();
132
133        // ---------------------------------------------------------------------
134        // GERCEK HAYAT ORNEGI 2: Dosya boyutu (byte -> kisa gosterim)
135        // ---------------------------------------------------------------------
136        System.out.println(">>> GERCEK HAYAT: Dosya boyutu gosterimi (US/SHORT):");
137        NumberFormat boyut = NumberFormat.getCompactNumberInstance(
138                Locale.US, NumberFormat.Style.SHORT);
139        boyut.setMaximumFractionDigits(1);
140        long[] boyutlar = {850, 2_048, 5_242_880, 1_073_741_824L};
141        for (long b : boyutlar) {
142            System.out.println("   " + b + " byte -> " + boyut.format(b) + "B");
143        }
144
145        System.out.println("\n================================================");
146        System.out.println(" BITTI.");
147        System.out.println("================================================");
148    }
149
150    // -------------------------------------------------------------------------
151    // ESKI yontemi temsil eden manuel kisaltma metodu (locale'i bilmez!).
152    // -------------------------------------------------------------------------
153    private static String manuelKisalt(long sayi) {
154        if (sayi < 1_000) {
155            return String.valueOf(sayi);
156        } else if (sayi < 1_000_000) {
157            return (sayi / 1_000) + "K";
158        } else if (sayi < 1_000_000_000) {
159            return (sayi / 1_000_000) + "M";
160        } else {
161            return (sayi / 1_000_000_000) + "B";
162        }
163    }
164}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

SwitchExpressionsPreview

çalıştırılabilir
SwitchExpressionsPreview.java
1// =============================================================================
2//  SwitchExpressionsPreview.java
3//  Java 12 - Switch Expressions (Preview Özelliği - JEP 325)
4// =============================================================================
5//
6//  ÖNEMLI NOT (DERLEME / ÇALIŞTIRMA):
7//  ----------------------------------------------------------------------------
8//  - Switch Expressions, Java 12'de "PREVIEW" (önizleme) olarak geldi.
9//    Java 12 ile derlemek için şu komut gerekiyordu:
10//        javac --release 12 --enable-preview SwitchExpressionsPreview.java
11//        java  --enable-preview SwitchExpressionsPreview
12//    (--enable-preview olmadan Java 12'de bu söz dizimi DERLENMEZDİ.)
13//
14//  - Özellik Java 14'te (JEP 361) KALICI (standard) hale geldi.
15//    Java 14 ve sonrasında (örneğin sizin sisteminizdeki Java 21'de)
16//    bu dosya HİÇBİR ek bayrağa gerek kalmadan normal şekilde derlenir:
17//        javac SwitchExpressionsPreview.java
18//        java  SwitchExpressionsPreview
19//
20//  Bu yüzden aşağıdaki kod, "preview" döneminde yazılmış söz dizimini
21//  kullanır; ancak Java 14+ ile --enable-preview olmadan da çalışır.
22//  Aşağıdaki yorumlarda hangi parçanın preview'a özgü olduğu açıklanmıştır.
23// =============================================================================
24
25public class SwitchExpressionsPreview {
26
27    public static void main(String[] args) {
28
29        System.out.println("================================================");
30        System.out.println(" JAVA 12 - SWITCH EXPRESSIONS (PREVIEW) ORNEKLERI");
31        System.out.println("================================================\n");
32
33        // ---------------------------------------------------------------------
34        // BÖLÜM 1: ESKİ YÖNTEM (Klasik switch STATEMENT)
35        // ---------------------------------------------------------------------
36        // Java 12 öncesinde switch bir "ifade (expression)" değil, bir
37        // "deyim (statement)" idi. Yani doğrudan bir DEĞER ÜRETEMEZDİ;
38        // değeri ancak yan etki (bir değişkene atama) ile elde edebilirdik.
39        //
40        // PROBLEMLER:
41        //   1) Her case sonunda 'break' yazmayı UNUTURSAK "fall-through"
42        //      (bir alttaki case'e düşme) hatası oluşur. Sessiz bir bug'tır.
43        //   2) Değer üretmek için ekstra bir değişken tanımlamak gerekir.
44        //   3) Çok satırlı, gürültülü ve hataya açık koddur.
45        System.out.println(">>> ESKI YONTEM (klasik switch statement):");
46        for (int gun = 1; gun <= 7; gun++) {
47            int calismaSaatiEski;
48            switch (gun) {
49                case 1:   // Pazartesi
50                case 2:   // Sali
51                case 3:   // Carsamba
52                case 4:   // Persembe
53                case 5:   // Cuma
54                    calismaSaatiEski = 8;   // hafta ici tam mesai
55                    break;                  // <-- break unutulursa fall-through!
56                case 6:   // Cumartesi
57                    calismaSaatiEski = 4;   // yarim gun
58                    break;
59                case 7:   // Pazar
60                    calismaSaatiEski = 0;   // tatil
61                    break;
62                default:
63                    calismaSaatiEski = -1;  // gecersiz gun
64            }
65            System.out.println("   Gun " + gun + " -> " + calismaSaatiEski + " saat");
66        }
67        System.out.println();
68
69        // ---------------------------------------------------------------------
70        // BÖLÜM 2: YENİ YÖNTEM (Switch EXPRESSION + '->' oku)
71        // ---------------------------------------------------------------------
72        // Java 12'de gelen yeni '->' (ok) söz dizimi:
73        //   - Ok'un sağındaki ifade ÇALIŞIR ve DEĞER ÜRETİR.
74        //   - 'break' YOKTUR; otomatik olarak fall-through OLMAZ. Güvenli!
75        //   - Birden çok etiketi tek case'te virgülle yazabiliriz:
76        //         case 1, 2, 3, 4, 5 ->
77        //   - switch'in tamamı bir DEĞER döndürür; doğrudan atayabiliriz.
78        System.out.println(">>> YENI YONTEM (switch expression, '->' oku):");
79        for (int gun = 1; gun <= 7; gun++) {
80            int calismaSaatiYeni = switch (gun) {
81                // Cogu etiketi tek satirda birlestirebiliyoruz (coklu case):
82                case 1, 2, 3, 4, 5 -> 8;   // hafta ici
83                case 6            -> 4;    // cumartesi
84                case 7            -> 0;    // pazar
85                default           -> -1;   // gecersiz
86            };  // <-- DİKKAT: expression olduğu için sonunda ';' var.
87            System.out.println("   Gun " + gun + " -> " + calismaSaatiYeni + " saat");
88        }
89        System.out.println();
90
91        // ---------------------------------------------------------------------
92        // BÖLÜM 3: 'yield' İLE ÇOK SATIRLI CASE BLOĞU
93        // ---------------------------------------------------------------------
94        // Bir case'in sağında tek bir ifade değil de birkaç satır işlem
95        // yapmamız gerekiyorsa { } blok açarız ve değeri 'yield' ile döndürürüz.
96        // ('yield', switch expression'dan değer döndürmenin yoludur;
97        //  metottan değer döndüren 'return' ile karıştırılmamalıdır.)
98        System.out.println(">>> 'yield' ile cok satirli case blogu:");
99        for (int gun = 1; gun <= 7; gun++) {
100            String aciklama = switch (gun) {
101                case 1, 2, 3, 4, 5 -> {
102                    int saat = 8;
103                    // ... burada loglama, hesaplama vs. yapilabilir ...
104                    yield "Hafta ici - " + saat + " saat mesai";  // <-- yield
105                }
106                case 6 -> {
107                    yield "Cumartesi - yarim gun (4 saat)";
108                }
109                case 7 -> "Pazar - tatil";   // tek ifade -> yield gerekmez
110                default -> "Gecersiz gun!";
111            };
112            System.out.println("   Gun " + gun + " -> " + aciklama);
113        }
114        System.out.println();
115
116        // ---------------------------------------------------------------------
117        // BÖLÜM 4: GERÇEK HAYAT ÖRNEĞİ - HTTP DURUM KODU -> MESAJ
118        // ---------------------------------------------------------------------
119        System.out.println(">>> GERCEK HAYAT: HTTP durum kodu -> kullanici mesaji:");
120        int[] kodlar = {200, 201, 301, 404, 500, 418, 999};
121        for (int kod : kodlar) {
122            String mesaj = httpDurumMesaji(kod);
123            System.out.println("   HTTP " + kod + " -> " + mesaj);
124        }
125        System.out.println();
126
127        // ---------------------------------------------------------------------
128        // BÖLÜM 5: ESKİ '\' (iki nokta) söz dizimini de switch EXPRESSION
129        //          olarak kullanabilirsiniz (eski stil ':' + yield).
130        // ---------------------------------------------------------------------
131        // Not: Java 12 ':' ile de expression yazmaya izin verir; ancak bu
132        // durumda fall-through riski geri gelir. '->' kullanmak tercih edilir.
133        System.out.println(">>> Eski ':' stiliyle expression (yield ile):");
134        int puan = 85;
135        String harfNotu = switch (puan / 10) {
136            case 10, 9 : yield "AA";
137            case 8     : yield "BA";
138            case 7     : yield "BB";
139            case 6     : yield "CC";
140            default    : yield "FF (kaldi)";
141        };
142        System.out.println("   Puan " + puan + " -> Harf notu: " + harfNotu);
143
144        System.out.println("\n================================================");
145        System.out.println(" BITTI.");
146        System.out.println("================================================");
147    }
148
149    // -------------------------------------------------------------------------
150    // Yardımcı metot: HTTP durum kodunu '->' switch expression ile mesaja çevirir.
151    // -------------------------------------------------------------------------
152    private static String httpDurumMesaji(int kod) {
153        return switch (kod) {
154            case 200, 201, 204 -> "Basarili";
155            case 301, 302      -> "Yonlendirme";
156            case 400           -> "Hatali istek";
157            case 401, 403      -> "Yetkisiz erisim";
158            case 404           -> "Bulunamadi";
159            case 500, 502, 503 -> "Sunucu hatasi";
160            default            -> "Bilinmeyen durum kodu (" + kod + ")";
161        };
162    }
163}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

TeeingCollector

çalıştırılabilir
TeeingCollector.java
1// =============================================================================
2//  TeeingCollector.java
3//  Java 12 - Teeing Collector (Collectors.teeing) - JDK-8209685
4// =============================================================================
5//
6//  NEDIR?
7//  ----------------------------------------------------------------------------
8//  Collectors.teeing(downstream1, downstream2, merger):
9//    Bir stream'in elemanlarini AYNI ANDA iki ayri collector'a (downstream)
10//    gonderir; her iki collector da kendi sonucunu uretir; en sonda
11//    'merger' (birlestirici BiFunction) bu iki sonucu TEK bir sonuca birlestirir.
12//
13//  Adi "tee" (T borusu) tesisatcilikteki T-baglantisindan gelir: tek bir akis
14//  ikiye ayrilir, iki farkli isleme tabi tutulur, sonra tekrar birlestirilir.
15//
16//  NEDEN GELDI / HANGI PROBLEMI COZER?
17//  ----------------------------------------------------------------------------
18//  Java 12 oncesinde bir koleksiyondan IKI farkli ozet (orn. hem ortalama
19//  hem toplam, hem min hem max) hesaplamak istediginizde ya:
20//    (a) stream'i IKI KEZ dolasmaniz (iki ayri stream() cagrisi),
21//    (b) ya da ozel/karmasik bir collector yazmaniz gerekiyordu.
22//  teeing ile TEK GECISTE iki sonucu birlikte hesaplayip birlestirebiliriz.
23//
24//  DERLEME / CALISTIRMA:
25//    Collectors.teeing Java 12'de KALICI (standard) olarak geldi (preview degil).
26//        javac TeeingCollector.java
27//        java  TeeingCollector
28// =============================================================================
29
30import java.util.List;
31import java.util.stream.Collectors;
32
33public class TeeingCollector {
34
35    // Basit bir "Urun" kaydi (ad + fiyat).
36    record Urun(String ad, double fiyat) {}
37
38    // teeing'in merger'inin urettigi sonucu tutacak kayit.
39    record FiyatOzeti(double ortalama, long adet, double toplam) {}
40
41    public static void main(String[] args) {
42
43        System.out.println("================================================");
44        System.out.println(" JAVA 12 - TEEING COLLECTOR ORNEKLERI");
45        System.out.println("================================================\n");
46
47        List<Urun> urunler = List.of(
48                new Urun("Klavye",   450.0),
49                new Urun("Mouse",    250.0),
50                new Urun("Monitor", 3200.0),
51                new Urun("Kulaklik", 800.0),
52                new Urun("Webcam",   600.0)
53        );
54
55        // ---------------------------------------------------------------------
56        // ESKI YONTEM: Stream'i IKI KEZ dolasarak ortalama ve toplam hesaplama
57        // ---------------------------------------------------------------------
58        // Dezavantaj: ayni veri seti uzerinde iki ayri stream gecisi yapilir.
59        // Buyuk veri setlerinde bu, iki katlik is yuku demektir.
60        System.out.println(">>> ESKI YONTEM (iki ayri stream gecisi):");
61        double ortalamaEski = urunler.stream()
62                .mapToDouble(Urun::fiyat)
63                .average()
64                .orElse(0.0);
65        double toplamEski = urunler.stream()
66                .mapToDouble(Urun::fiyat)
67                .sum();
68        System.out.printf("   Ortalama fiyat: %.2f TL%n", ortalamaEski);
69        System.out.printf("   Toplam fiyat  : %.2f TL%n", toplamEski);
70        System.out.println();
71
72        // ---------------------------------------------------------------------
73        // YENI YONTEM: Collectors.teeing ile TEK GECISTE iki sonuc + birlestirme
74        // ---------------------------------------------------------------------
75        // 1. downstream  -> ortalama (Collectors.averagingDouble)
76        // 2. downstream  -> adet     (Collectors.counting)
77        // merger         -> iki sonucu FiyatOzeti'ne birlestirir; ayrica
78        //                   ortalama * adet ile toplami da hesaplayip ekleriz.
79        System.out.println(">>> YENI YONTEM (Collectors.teeing - tek gecis):");
80        FiyatOzeti ozet = urunler.stream().collect(
81                Collectors.teeing(
82                        Collectors.averagingDouble(Urun::fiyat),  // 1. collector
83                        Collectors.counting(),                    // 2. collector
84                        (ort, adet) -> new FiyatOzeti(ort, adet, ort * adet) // merger
85                )
86        );
87        System.out.printf("   Ortalama fiyat: %.2f TL%n", ozet.ortalama());
88        System.out.printf("   Urun adedi    : %d%n", ozet.adet());
89        System.out.printf("   Toplam fiyat  : %.2f TL%n", ozet.toplam());
90        System.out.println();
91
92        // ---------------------------------------------------------------------
93        // GERCEK HAYAT ORNEGI: Ogrenci notlari -> ortalama + gecen ogrenci sayisi
94        // ---------------------------------------------------------------------
95        // Tek geciste hem sinif ortalamasini hem de 50 ve uzeri alan
96        // (gecen) ogrenci sayisini birlikte hesapliyoruz.
97        System.out.println(">>> GERCEK HAYAT: Sinif notlari analizi:");
98        List<Integer> notlar = List.of(45, 80, 55, 30, 90, 65, 48, 72, 100, 50);
99
100        record SinifRaporu(double ortalama, long gecenSayisi, int toplamOgrenci) {}
101
102        SinifRaporu rapor = notlar.stream().collect(
103                Collectors.teeing(
104                        // 1. downstream: tum notlarin ortalamasi
105                        Collectors.averagingInt(Integer::intValue),
106                        // 2. downstream: 50 ve uzeri alanlari say (gecenler)
107                        Collectors.filtering(n -> n >= 50, Collectors.counting()),
108                        // merger: iki sonucu birlestir, toplam ogrenciyi de ekle
109                        (ort, gecen) -> new SinifRaporu(ort, gecen, notlar.size())
110                )
111        );
112        System.out.printf("   Sinif ortalamasi : %.1f%n", rapor.ortalama());
113        System.out.printf("   Gecen ogrenci    : %d / %d%n",
114                rapor.gecenSayisi(), rapor.toplamOgrenci());
115        System.out.printf("   Basari orani     : %%%.0f%n",
116                100.0 * rapor.gecenSayisi() / rapor.toplamOgrenci());
117        System.out.println();
118
119        // ---------------------------------------------------------------------
120        // EK ORNEK: teeing ile MIN ve MAX'i tek geciste bulup fark hesaplama
121        // ---------------------------------------------------------------------
122        System.out.println(">>> EK ORNEK: En ucuz / en pahali urun farki:");
123        record MinMax(double min, double max, double fark) {}
124
125        MinMax minMax = urunler.stream().collect(
126                Collectors.teeing(
127                        Collectors.minBy((a, b) -> Double.compare(a.fiyat(), b.fiyat())),
128                        Collectors.maxBy((a, b) -> Double.compare(a.fiyat(), b.fiyat())),
129                        (minOpt, maxOpt) -> {
130                            double min = minOpt.map(Urun::fiyat).orElse(0.0);
131                            double max = maxOpt.map(Urun::fiyat).orElse(0.0);
132                            return new MinMax(min, max, max - min);
133                        }
134                )
135        );
136        System.out.printf("   En ucuz : %.2f TL%n", minMax.min());
137        System.out.printf("   En pahali: %.2f TL%n", minMax.max());
138        System.out.printf("   Fark    : %.2f TL%n", minMax.fark());
139
140        System.out.println("\n================================================");
141        System.out.println(" BITTI.");
142        System.out.println("================================================");
143    }
144}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.