Java Sürüm Evrimi (8 → 25)
Java, 2014'teki 8 sürümünden bu yana her 6 ayda bir yeni sürüm, her 2 yılda bir uzun destekli (LTS) sürüm çıkarıyor. LTS'ler: 8, 11, 17, 21, 25. Bu bölümün amacı sadece "şu özellik hangi sürümde geldi" demek değil; he…
Java Sürüm Evrimi (8 → 25)
Java, 2014'teki 8 sürümünden bu yana her 6 ayda bir yeni sürüm, her 2 yılda bir uzun destekli (LTS) sürüm çıkarıyor. LTS'ler: 8, 11, 17, 21, 25. Bu bölümün amacı sadece "şu özellik hangi sürümde geldi" demek değil; her sürümde neyin önemli olduğunu ve bir sürümden diğerine geçerken kodun nasıl değiştiğini karşılaştırmalı, çalışan örneklerle göstermek. Örnekler bu ortamdaki JDK 21 ile uyumludur; her örnek "öncesi vs sonrası" stiliyle yazılmıştır (iki stil de 21'de derlenir, böylece farkı doğrudan görürsün).
Java 8 (2014, LTS) — Dili yeniden tanımlayan devrim
En önemli sürüm. Java'yı modern, fonksiyonel-etkili bir dile dönüştürdü:
| Özellik | Ne işe yarar | Neyi değiştirdi |
|---|---|---|
| Lambda | Davranışı değer gibi taşıma | Anonim iç sınıf gürültüsü bitti |
| Stream API | Bildirimsel veri işleme | for döngüleri yerini filter/map/collect'e bıraktı |
| Optional | null güvenliği | "milyar dolarlık hata"ya tip düzeyinde önlem |
| java.time | Tarih/saat | Hatalı, değişebilen Date/Calendar'ın halefi |
| default metotlar | Interface'e gövdeli metot | API'leri bozmadan genişletme |
Örnek 1 (./Ornek1.java) dört işi Java 8 öncesi ve sonrası yan yana yapar: anonim
Comparator→lambda, döngü→stream, elle null kontrolü→Optional, Date→java.time. Aynı sonuç, çok
daha temiz kod.
Geçiş notu: 8'e geçişin pratikte dezavantajı yoktu; bugün hâlâ 8'de kalan projeler güvenlik ve performans güncellemelerini kaçırır.
Java 9–11 — Modülerlik, var ve araçlar
- Java 9 (2017): Modül sistemi (JPMS/Jigsaw),
List/Map.offabrikaları,jshell, Stream eklentileri (takeWhile,dropWhile, 3-argümanlıiterate). - Java 10 (2018):
var— yerel değişken tip çıkarımı. - Java 11 (2018, LTS): Yeni
Stringmetotları (strip,isBlank,repeat,lines), standart HttpClient, tek dosya çalıştırma (java Dosya.java— bu portalın temeli!).
Örnek 2 (./Ornek2.java) koleksiyon fabrikalarını (3 adım vs List.of), var'ı, Java 11 String
metotlarını ve Java 9 Stream eklentilerini öncesi/sonrası karşılaştırır.
Geçiş notu (8→11): En büyük tuzak, 11'de kaldırılan Java EE/CORBA modülleridir (JAXB, JAX-WS). Bunları kullanan projeler bağımlılıkları ayrıca eklemek zorunda kaldı.
Java 14–17 — Dil ergonomisi olgunlaşıyor
Bu aralık, kodu belirgin biçimde kısaltan özellikler getirdi:
| Özellik (sürüm) | Öncesi | Sonrası |
|---|---|---|
| switch expression (14) | break'li statement, mutable değişken | Değer döndüren, eksiksizlik denetimli ifade |
| text block (15) | "\n" + + cehennemi | """...""" çok satırlı metin |
| records (16) | ~30 satır boilerplate veri sınıfı | Tek satır (record) |
| pattern matching instanceof (16) | instanceof + ayrı cast | Tek adımda kontrol + bağlama |
| sealed sınıflar (17, LTS) | Kontrolsüz kalıtım | Kapalı, denetlenebilir hiyerarşi |
Örnek 3 (./Ornek3.java) bir record'u elle yazılmış sınıfla, switch expression'ı statement'la,
pattern matching'i instanceof+cast ile ve sealed+switch'i karşılaştırır.
Java 18–21 — Eşzamanlılıkta sıçrama
- Java 18 (2022): UTF-8 varsayılan karakter kümesi, basit web sunucusu (
jwebserver). - Java 21 (2023, LTS): Önceki sürümlerde önizleme olan büyük özellikler standartlaştı:
- Virtual threads — on binlerce eşzamanlı görevi ucuza
- Record patterns + switch pattern matching — veriyi switch'te parçalama
- Sequenced Collections —
getFirst/getLast/reversed - Generational ZGC — düşük gecikmeli çöp toplama
Örnek 4 (./Ornek4.java) record patterns'ı iç içe instanceof ile, sequenced collections'ı
get(0)/get(size-1) ile ve sanal thread'leri platform thread havuzuyla karşılaştırır (G/Ç
ağırlıklı yükte sanal thread'lerin farkını ölçer).
Geçiş notu (17→21): Genelde düşük riskli, yüksek kazançlı bir geçiştir. Sanal thread kullanacaksan
synchronizedbloklarındaki "pinning"e dikkat (kritik bölümlerdeReentrantLock).
Java 22–25 — En yeni (JDK 25 gerektirir, burada çalışmaz)
Yalnızca tanıtım amaçlı (bu ortamdaki JDK 21'de derlenmez):
- Java 22 (2024): Foreign Function & Memory API (JNI halefi) standartlaştı; isimsiz
değişkenler/desenler (
_). - Java 24 (2025): Stream Gatherers (
Stream.gather(...)) ile özel ara işlemler. - Java 25 (2025, LTS): Sadeleştirilmiş giriş noktası (
void main()), modül import bildirimleri (import module java.base;), scoped values, esnek constructor gövdeleri.
// Java 25: sade kaynak dosya + instance main (örnek; JDK 25 gerektirir) void main() { IO.println("Merhaba"); // "public static void main(String[])" şart değil }
Not: 22–25'teki bazı özelliklerin "önizleme → standart" yolculuğu sürümden sürüme değişebilir; kesin durum için resmî sürüm notlarına bak.
Sürüm-sürüm derin anlatım (ayrı konular)
Bu konu genel resmi ve dönemsel karşılaştırmaları verir. Her sürümün ayrı, derin anlatımı ve "öncesi vs sonrası" örnekleri için şu konulara bak:
- Java 8 → 16:
54-java8…62-java16(her biri çalışan karşılaştırmalı örneklerle) - Java 17 → 21:
97-java17…101-java21(LTS'ler dahil; örnekler bu ortamda çalışır) - Java 22 → 25:
102-java22…105-java25(en yeni; JDK 21'de çalışmadığından kavramsal anlatım)
Yani her sürüm için "o sürümde ne önemli + bir önceki sürümden ne değişti" sorusu, ilgili konuda örneklerle yanıtlanır.
Sürüm seçerken pratik kural
- Üretim için her zaman bir LTS (17, 21 veya 25). Ara sürümler kısa desteklidir.
- Yeni projede bugün 21 (veya destekleniyorsa 25) mantıklı varsayılandır.
- Çoğu zaman seçimini kütüphane/çerçeve uyumu belirler (kullandığın Spring/Hibernate hangi JDK'yı destekliyor?).
Özet
Java'nın 8'deki lambda/Stream devriminden 11'in modülerlik/araçlarına, 17'nin record/sealed ergonomisine ve 21'in sanal thread'lerine uzanan evrimini öncesi/sonrası karşılaştırmalı örneklerle gördük; 22–25'in en yeni eklerine değindik. Her geçişin kazanımları genelde maliyetinden büyüktür — ama riskler de vardır. Sırada tam olarak bunu konuşuyoruz: sürüm geçişi: riskler ve kazanımlar (özellikle 17/21 → en yeni).
▶ Kod Örnekleri(4)
Ornek1
çalıştırılabilir1// Ornek1: JAVA 8 DEVRİMİ — "öncesi vs sonrası" karşılaştırması.
2// Aynı işleri Java 8 öncesi stil ve Java 8 stiliyle yan yana yazıp farkı görüyoruz.
3// (İki stil de JDK 21'de çalışır; amaç değişimi göstermektir.)
4// Çalıştırma: java Ornek1.java
5import java.text.SimpleDateFormat;
6import java.util.ArrayList;
7import java.util.Comparator;
8import java.util.Date;
9import java.util.List;
10import java.util.Optional;
11import java.time.LocalDate;
12import java.time.format.DateTimeFormatter;
13import java.util.stream.Collectors;
14
15public class Ornek1 {
16
17 record Urun(String ad, double fiyat) {}
18
19 public static void main(String[] args) {
20 List<Urun> urunler = List.of(
21 new Urun("Klavye", 450), new Urun("Mouse", 250),
22 new Urun("Monitör", 3200), new Urun("Kulaklık", 900));
23
24 // ============ 1) Sıralama: anonim sınıf (eski) vs lambda (Java 8) ============
25 List<Urun> eskiSirali = new ArrayList<>(urunler);
26 eskiSirali.sort(new Comparator<Urun>() { // ESKİ: anonim iç sınıf, 5 satır gürültü
27 @Override public int compare(Urun a, Urun b) {
28 return Double.compare(a.fiyat(), b.fiyat());
29 }
30 });
31 List<Urun> yeniSirali = new ArrayList<>(urunler);
32 yeniSirali.sort(Comparator.comparingDouble(Urun::fiyat)); // JAVA 8: tek satır lambda/method ref
33
34 System.out.println("Sıralama sonucu aynı mı? "
35 + eskiSirali.equals(yeniSirali) + " (anonim sınıf vs lambda)");
36
37 // ============ 2) Filtreleme+toplama: döngü (eski) vs Stream (Java 8) ============
38 // ESKİ: elle döngü, geçici liste
39 double eskiToplam = 0;
40 for (Urun u : urunler) {
41 if (u.fiyat() >= 500) eskiToplam += u.fiyat();
42 }
43 // JAVA 8: bildirimsel stream pipeline
44 double yeniToplam = urunler.stream()
45 .filter(u -> u.fiyat() >= 500)
46 .mapToDouble(Urun::fiyat)
47 .sum();
48 System.out.println("500+ ürün cirosu (döngü vs stream): " + eskiToplam + " == " + yeniToplam);
49
50 // İsim listesi: döngü vs stream+collect
51 List<String> adlar = urunler.stream().map(Urun::ad).collect(Collectors.toList());
52 System.out.println("Adlar (stream): " + adlar);
53
54 // ============ 3) null kontrolü: elle (eski) vs Optional (Java 8) ============
55 Urun bulunan = bul(urunler, "Mouse");
56 String eskiAd = (bulunan != null) ? bulunan.ad() : "yok"; // ESKİ: elle null kontrol
57 String yeniAd = Optional.ofNullable(bul(urunler, "Yok")).map(Urun::ad).orElse("yok"); // JAVA 8
58 System.out.println("null güvenliği: eski=" + eskiAd + ", optional=" + yeniAd);
59
60 // ============ 4) Tarih: Date/SimpleDateFormat (eski) vs java.time (Java 8) ============
61 // ESKİ: Date + SimpleDateFormat (değişebilir, thread-safe değil)
62 String eskiTarih = new SimpleDateFormat("dd.MM.yyyy").format(new Date(0L));
63 // JAVA 8: java.time (değişmez, güvenli, okunaklı)
64 String yeniTarih = LocalDate.of(1970, 1, 1).format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
65 System.out.println("tarih (Date vs java.time): " + eskiTarih + " == " + yeniTarih);
66
67 System.out.println("""
68
69 --- Java 8 NE getirdi? (en kritik sürüm) ---
70 Lambda + Stream + Optional + java.time + interface default metotları.
71 Etki: anonim sınıf gürültüsü gitti, döngüler bildirimsel oldu, null ve tarih güvenli hale geldi.
72 Bugün yazdığın modern Java'nın temeli bu sürümle atıldı.""");
73 }
74
75 static Urun bul(List<Urun> liste, String ad) {
76 return liste.stream().filter(u -> u.ad().equals(ad)).findFirst().orElse(null);
77 }
78}Ornek2
çalıştırılabilir1// Ornek2: JAVA 9-11 — koleksiyon fabrikaları, var, yeni String metotları, Stream eklentileri.
2// "Öncesi vs sonrası" karşılaştırmalarıyla 9, 10 ve 11'in getirdikleri.
3// Çalıştırma: java Ornek2.java
4import java.util.ArrayList;
5import java.util.Collections;
6import java.util.List;
7import java.util.stream.Collectors;
8import java.util.stream.Stream;
9
10public class Ornek2 {
11
12 public static void main(String[] args) {
13 // ============ JAVA 9: değişmez koleksiyon fabrikaları ============
14 // ESKİ: liste oluştur, doldur, unmodifiable'a sar (3 adım)
15 List<String> eski = new ArrayList<>();
16 eski.add("a"); eski.add("b"); eski.add("c");
17 List<String> eskiDegismez = Collections.unmodifiableList(eski);
18 // JAVA 9: tek satır
19 List<String> yeni = List.of("a", "b", "c");
20 System.out.println("Koleksiyon fabrikası (eski vs List.of): " + eskiDegismez.equals(yeni));
21
22 // ============ JAVA 10: var ile yerel değişken tip çıkarımı ============
23 // ESKİ: tip iki kez yazılır
24 List<String> acikTip = new ArrayList<>();
25 // JAVA 10: tip çıkarılır (sağ taraftan)
26 var cikarilanTip = new ArrayList<String>();
27 acikTip.add("x"); cikarilanTip.add("x");
28 System.out.println("var (açık tip vs var): " + acikTip.equals(cikarilanTip));
29
30 // ============ JAVA 11: yeni String metotları ============
31 String metin = " merhaba ";
32 System.out.println("\n--- Java 11 String metotları ---");
33 System.out.println("strip() : '" + metin.strip() + "' (trim'den Unicode-doğru hali)");
34 System.out.println("isBlank(): " + " ".isBlank());
35 System.out.println("repeat(3): " + "ab".repeat(3));
36 System.out.println("lines() : " + "a\nb\nc".lines().count() + " satır");
37
38 // ============ JAVA 9: Stream eklentileri (takeWhile / dropWhile / iterate) ============
39 System.out.println("\n--- Java 9 Stream eklentileri ---");
40 List<Integer> sayilar = List.of(1, 2, 3, 4, 5, 1, 2);
41 // takeWhile: koşul bozulana kadar al
42 System.out.println("takeWhile(<4): " + sayilar.stream().takeWhile(n -> n < 4).collect(Collectors.toList()));
43 // dropWhile: koşul bozulana kadar atla
44 System.out.println("dropWhile(<4): " + sayilar.stream().dropWhile(n -> n < 4).collect(Collectors.toList()));
45 // iterate (3 argümanlı, Java 9): klasik for gibi
46 System.out.println("iterate : " + Stream.iterate(1, n -> n <= 16, n -> n * 2).collect(Collectors.toList()));
47
48 System.out.println("""
49
50 --- Java 9-11 NE getirdi? ---
51 Java 9 : Modül sistemi (JPMS), List/Map.of, jshell, Stream eklentileri.
52 Java 10: var (yerel tip çıkarımı).
53 Java 11 (LTS): yeni String metotları, standart HttpClient, TEK DOSYA çalıştırma (java Dosya.java).
54 Geçiş tuzağı: Java 11'de Java EE/CORBA modülleri KALDIRILDI (JAXB/JAX-WS ayrı eklenir).""");
55 }
56}Ornek3
çalıştırılabilir1// Ornek3: JAVA 14-17 — records, switch expression, pattern matching, sealed (öncesi vs sonrası).
2// Çalıştırma: java Ornek3.java
3import java.util.Objects;
4
5public class Ornek3 {
6
7 // ============ record (Java 16): "öncesi" tam bir sınıf, "sonrası" tek satır ============
8 // ÖNCESİ: değişmez bir veri sınıfı için onlarca satır boilerplate gerekirdi.
9 static final class NoktaEski {
10 private final int x, y;
11 NoktaEski(int x, int y) { this.x = x; this.y = y; }
12 int x() { return x; } int y() { return y; }
13 @Override public boolean equals(Object o) {
14 if (!(o instanceof NoktaEski n)) return false;
15 return x == n.x && y == n.y;
16 }
17 @Override public int hashCode() { return Objects.hash(x, y); }
18 @Override public String toString() { return "NoktaEski[x=" + x + ", y=" + y + "]"; }
19 }
20 // SONRASI (Java 16): tek satır — equals/hashCode/toString/getter'lar OTOMATİK.
21 record Nokta(int x, int y) {}
22
23 sealed interface Sekil permits Daire, Kare {} // Java 17: kapalı hiyerarşi
24 record Daire(double r) implements Sekil {}
25 record Kare(double k) implements Sekil {}
26
27 public static void main(String[] args) {
28 System.out.println("record (eski sınıf vs record): "
29 + new NoktaEski(1, 2).equals(new NoktaEski(1, 2)) + " / " + new Nokta(1, 2).equals(new Nokta(1, 2)));
30 System.out.println("record toString: " + new Nokta(3, 4));
31
32 // ============ switch: statement (eski) vs expression (Java 14) ============
33 int gun = 6;
34 // ESKİ: switch statement — break, mutable değişken, hataya açık
35 String tipEski;
36 switch (gun) {
37 case 6: case 7: tipEski = "hafta sonu"; break;
38 default: tipEski = "hafta içi";
39 }
40 // YENİ (Java 14): switch expression — değer döndürür, break yok, eksiksizlik denetimi
41 String tipYeni = switch (gun) {
42 case 6, 7 -> "hafta sonu";
43 default -> "hafta içi";
44 };
45 System.out.println("\nswitch (statement vs expression): " + tipEski + " == " + tipYeni);
46
47 // ============ pattern matching for instanceof (Java 16) ============
48 Object o = "merhaba";
49 // ESKİ: instanceof + ayrı cast
50 if (o instanceof String) {
51 String s = (String) o;
52 System.out.println("\neski instanceof+cast: uzunluk " + s.length());
53 }
54 // YENİ: tek adımda kontrol + bağlama
55 if (o instanceof String s) {
56 System.out.println("yeni pattern matching: uzunluk " + s.length());
57 }
58
59 // ============ sealed + switch pattern (Java 17) ============
60 Sekil sekil = new Daire(2);
61 // sealed olduğu için switch EKSİKSİZ (default gerekmez); derleyici tüm tipleri zorlar.
62 double alan = switch (sekil) {
63 case Daire d -> Math.PI * d.r() * d.r();
64 case Kare k -> k.k() * k.k();
65 };
66 System.out.printf("%nsealed + switch: alan = %.2f%n", alan);
67
68 System.out.println("""
69
70 --- Java 14-17 NE getirdi? ---
71 switch expression (14), text block (15), records (16), pattern matching instanceof (16),
72 sealed sınıflar (17, LTS). Etki: çok daha az boilerplate, daha güvenli ve okunaklı modelleme.
73 Java 17 bugün hâlâ çok yaygın bir üretim tabanıdır.""");
74 }
75}Ornek4
çalıştırılabilir1// Ornek4: JAVA 21 LTS — record patterns, sequenced collections, virtual threads (öncesi vs sonrası).
2// Çalıştırma: java Ornek4.java (JDK 21 gerektirir)
3import java.util.ArrayList;
4import java.util.List;
5import java.util.concurrent.Executors;
6import java.util.concurrent.atomic.AtomicInteger;
7
8public class Ornek4 {
9
10 sealed interface Olay permits Tikla, Yaz {}
11 record Tikla(int x, int y) implements Olay {}
12 record Yaz(String metin) implements Olay {}
13
14 public static void main(String[] args) throws InterruptedException {
15 // ============ record patterns (Java 21): iç içe instanceof vs desen ayrıştırma ============
16 Olay olay = new Tikla(120, 45);
17
18 // ÖNCESİ (Java 16-17): instanceof + alanları elle çekme
19 String eski;
20 if (olay instanceof Tikla t) {
21 eski = "tık (" + t.x() + "," + t.y() + ")";
22 } else if (olay instanceof Yaz y) {
23 eski = "yaz: " + y.metin();
24 } else { eski = "?"; }
25
26 // SONRASI (Java 21): switch + RECORD PATTERN — alanlar doğrudan parçalanır
27 String yeni = switch (olay) {
28 case Tikla(int x, int y) -> "tık (" + x + "," + y + ")";
29 case Yaz(String m) -> "yaz: " + m;
30 };
31 System.out.println("record patterns (eski vs yeni): " + eski + " == " + yeni);
32
33 // ============ Sequenced Collections (Java 21): get(0)/get(size-1) vs getFirst/getLast ============
34 List<String> liste = new ArrayList<>(List.of("giriş", "işlem", "çıkış"));
35 // ESKİ
36 String ilkEski = liste.get(0);
37 String sonEski = liste.get(liste.size() - 1);
38 // YENİ (Java 21)
39 String ilkYeni = liste.getFirst();
40 String sonYeni = liste.getLast();
41 System.out.println("\nSequenced (eski vs yeni): ilk " + ilkEski + "/" + ilkYeni
42 + ", son " + sonEski + "/" + sonYeni);
43 System.out.println("reversed(): " + liste.reversed());
44
45 // ============ Virtual Threads (Java 21): platform thread vs sanal thread ============
46 System.out.println("\n--- 10.000 G/Ç görevi: platform vs sanal thread ---");
47 long platform = sureOlc(Executors.newFixedThreadPool(50)); // sınırlı havuz -> yavaş
48 long sanal = sureOlc(Executors.newVirtualThreadPerTaskExecutor()); // her göreve sanal thread
49 System.out.printf("Platform havuzu (50): ~%4d ms%n", platform);
50 System.out.printf("Sanal thread'ler : ~%4d ms%n", sanal);
51
52 System.out.println("""
53
54 --- Java 21 LTS NE getirdi? ---
55 Virtual threads (yüksek eşzamanlılık), record patterns + switch pattern matching,
56 sequenced collections, generational ZGC. Çoğu projede 17->21 düşük riskli, yüksek kazançlı geçiştir.
57 (Java 22-25 için README'ye bak: FFM API, Stream Gatherers, sade main, modül import...)""");
58 }
59
60 static long sureOlc(java.util.concurrent.ExecutorService exec) throws InterruptedException {
61 AtomicInteger biten = new AtomicInteger();
62 long t0 = System.currentTimeMillis();
63 for (int i = 0; i < 10_000; i++) {
64 exec.submit(() -> {
65 try { Thread.sleep(10); } catch (InterruptedException ignored) {}
66 biten.incrementAndGet();
67 });
68 }
69 exec.shutdown();
70 exec.awaitTermination(60, java.util.concurrent.TimeUnit.SECONDS);
71 return System.currentTimeMillis() - t0;
72 }
73}