Eğitim Portalı/Sürüm Analizi/Java 8 — Detaylı Sürüm Analizi ve Örnekler
Sürüm Analizi05-java-versiyon-analizi/01-Java-8

Java 8 — Detaylı Sürüm Analizi ve Örnekler

> Bu klasör, Java 8 sürümünün getirdiği tüm önemli özellikleri derinlemesine > anlatan Türkçe bir dokümantasyon ve her özellik için çalışan, derlenebilir > örnek .java dosyaları içerir.

Java 8 — Detaylı Sürüm Analizi ve Örnekler

Bu klasör, Java 8 sürümünün getirdiği tüm önemli özellikleri derinlemesine anlatan Türkçe bir dokümantasyon ve her özellik için çalışan, derlenebilir örnek .java dosyaları içerir.


İçindekiler

  1. Sürüm Bilgisi
  2. Java 8 Neden Devrim Niteliğindedir?
  3. Özellikler
  4. Java 8'e Geçiş: Avantajlar, Dezavantajlar, Riskler
  5. Dosya Listesi ve Çalıştırma

Sürüm Bilgisi

BilgiDeğer
SürümJava SE 8 (JDK 1.8)
Çıkış Tarihi18 Mart 2014
LTS DurumuEvet — Uzun Vadeli Destek (LTS). Java'nın en uzun ömürlü, en yaygın kullanılan sürümlerinden biridir.
Önemli SürümlerOracle JDK ve OpenJDK olarak dağıtıldı. Oracle kamu güncellemelerini 2019'da sonlandırdı (ticari destek devam etti). OpenJDK tabanlı dağıtımlar (Adoptium/Temurin, Amazon Corretto, Azul Zulu, Red Hat) destek vermeye devam ediyor.
JLS / JVMJava Language Specification 8, ayrıca PermGen kaldırılıp Metaspace getirildi.

Oracle / OpenJDK Notu

Java 8 döneminde Oracle JDK ile OpenJDK arasında pratikte çok az fark vardı. 2019 sonrası Oracle, ticari kullanımda lisans (abonelik) modeline geçti; bu yüzden bugün çoğu kurum OpenJDK tabanlı ücretsiz dağıtımlar (Eclipse Temurin, Amazon Corretto vb.) kullanır. Java 8, hâlâ kurumsal dünyada en çok production'da çalışan sürümlerden biridir.


Java 8 Neden Devrim Niteliğindedir?

Java 8, dilin tarihindeki en büyük paradigma değişimini getirdi: fonksiyonel programlama unsurlarını ana akım Java'ya soktu.

Java 8 öncesinde Java tamamen nesne yönelimli (OOP) ve emir kipi (imperative) bir dildi: "bilgisayara adım adım NASIL yapacağını söyle". Java 8 ile birlikte deklaratif (bildirimsel) programlama mümkün oldu: "NE istediğini söyle, nasıl yapılacağıyla kütüphane ilgilensin".

Bu değişimi mümkün kılan üç temel taş:

  1. Lambda ifadeleri — davranışı (kod) bir veri gibi geçirebilmek.
  2. Fonksiyonel arayüzler — lambdaların "tipi".
  3. Stream API — koleksiyonlar üzerinde fonksiyonel boru hatları.

Bunların yanında Optional (null güvenliği), java.time (modern tarih), default metotlar (arayüz evrimi) ve CompletableFuture (asenkron) gibi yenilikler, Java'yı modern bir dil seviyesine taşıdı. İşte bu yüzden Java 8, "öncesi ve sonrası" diye anılan bir dönüm noktasıdır.


Özellikler

Her özellik için: NEDİR / NEDEN GELDİ / NE İŞE YARAR / NEREDE KOLAYLIK / ESKİ vs YENİ / GERÇEK HAYAT başlıklarını bulacaksınız ve ilgili .java dosyasına referans verilmiştir.


1. Lambda İfadeleri

Bkz: LambdaOrnekleri.java

  • NEDİR? İsimsiz (anonim) bir fonksiyondur. (parametreler) -> { gövde } sözdizimiyle, bir davranışı (kod parçasını) doğrudan değer gibi geçirmemizi sağlar.
  • NEDEN GELDİ? Java 8 öncesinde bir davranış geçirmek için anonim iç sınıf yazmak zorundaydık. 1 satırlık iş için 5-6 satırlık "tören kodu" (boilerplate) gerekiyordu.
  • NE İŞE YARAR / NEREDE KOLAYLIK? Sıralama (Comparator), olay dinleyicileri, Stream işlemleri, callback'ler. Kodu kısaltır ve okunabilir kılar.

ESKİ YÖNTEM (anonim iç sınıf):

Collections.sort(isimler, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

YENİ YÖNTEM (lambda):

Collections.sort(isimler, (a, b) -> a.compareTo(b));
  • GERÇEK HAYAT: Bir e-ticaret sitesinde çalışan listesini önce departmana, sonra isme göre sıralamak; bir kullanıcı listesini forEach ile dolaşmak.

2. Fonksiyonel Arayüzler

Bkz: FonksiyonelArayuzler.java

  • NEDİR? İçinde tek bir soyut metot bulunan arayüz (SAM — Single Abstract Method). Lambdaların ve method reference'ların "tipi" budur. @FunctionalInterface anotasyonu, ikinci soyut metot eklenirse derleme hatası vererek güvence sağlar.
  • NEDEN GELDİ? Lambdaların atanabileceği bir tip gerekiyordu. Java 8, java.util.function paketinde hazır, genel amaçlı arayüzler getirdi; böylece her ihtiyaç için yeni arayüz tanımlamaya gerek kalmadı.
  • NE İŞE YARAR: Davranışı parametre olarak geçirmenin standart yolunu sunar.

En çok kullanılanlar:

ArayüzGirdi → ÇıktıMetotÖrnek kullanım
Function<T,R>T → RapplyDönüştürme (map)
Consumer<T>T → voidacceptYan etki (yazdırma)
Supplier<T>() → TgetTembel (lazy) üretim
Predicate<T>T → booleantestFiltreleme koşulu
BiFunction<T,U,R>(T,U) → Rapplyİki girdili işlem
UnaryOperator<T>T → TapplyAynı tipe dönüştürme
BinaryOperator<T>(T,T) → Tapplyİki aynı tipi birleştirme
  • GERÇEK HAYAT: E-ticarette Predicate<Urun> stokta ve Predicate<Urun> pahali kurallarını .and() ile birleştirip "stokta VE pahalı" ürünleri filtrelemek; Function<Urun, Double> ile KDV'li fiyat hesaplamak.

3. Method References

Bkz: MethodReferences.java

  • NEDİR? Lambda'nın daha da kısa hâli. Eğer bir lambda yalnızca var olan bir metodu çağırıyorsa, :: operatörüyle doğrudan metodu işaret ederiz.
  • NEDEN GELDİ? x -> Sinif.metot(x) gibi sadece delege eden lambdalar hâlâ gürültülüydü. Method reference niyeti netleştirir ve okunabilirliği artırır.

Dört türü:

TürSöz dizimiLambda karşılığı
1. Static metotInteger::parseInts -> Integer.parseInt(s)
2. Belirli nesnenin metoduSystem.out::printlns -> System.out.println(s)
3. Rastgele nesnenin örnek metoduString::toUpperCases -> s.toUpperCase()
4. Constructor referansıKullanici::newad -> new Kullanici(ad)

ESKİ vs YENİ:

// LAMBDA
list.stream().map(s -> s.toUpperCase());
// METHOD REFERENCE
list.stream().map(String::toUpperCase);
  • GERÇEK HAYAT: Çalışanları maaşa göre sıralarken Comparator.comparing(Calisan::getMaas); isim listesini toplu olarak nesneye çevirmek için .map(Kullanici::new).

4. Stream API

Bkz: StreamApiOrnekleri.java

  • NEDİR? Koleksiyonlar üzerinde deklaratif işlem yapmayı sağlayan bir veri akışı (pipeline) API'si: kaynak → ara işlemler → sonlandırıcı.
  • NEDEN GELDİ? Elle yazılan iç içe for döngüleri ve geçici listeler, filtrele-dönüştür-topla gibi işlemleri okunması zor ve hataya açık hâle getiriyordu. Stream bunları tek bir akıcı zincire indirger.
  • NE İŞE YARAR: filter, map, reduce, collect, flatMap, sorted, limit, distinct, istatistik, ve paralel stream ile çoklu çekirdek.

Önemli kavramlar:

  • Lazy (tembel): Ara işlemler, terminal işlem çağrılana kadar çalışmaz.
  • Tek kullanımlık: Bir stream tüketildikten sonra tekrar kullanılamaz.
  • Paralel: .parallelStream() veya .parallel() ile çekirdeklere dağılır.

ESKİ YÖNTEM:

List<String> sonuc = new ArrayList<>();
for (Urun u : urunler) {
    if (u.getKategori().equals("Elektronik") && u.getFiyat() > 1000) {
        sonuc.add(u.getAd().toUpperCase());
    }
}

YENİ YÖNTEM (Stream):

List<String> sonuc = urunler.stream()
    .filter(u -> u.getKategori().equals("Elektronik"))
    .filter(u -> u.getFiyat() > 1000)
    .map(u -> u.getAd().toUpperCase())
    .collect(Collectors.toList());
  • GERÇEK HAYAT: E-ticarette "en pahalı 3 ürünü bul", "tüm siparişlerin kalemlerini flatMap ile düzleştir", "toplam stok değerini reduce ile hesapla", 1 milyon kaydı paralel stream ile topla.

5. Optional Sınıfı

Bkz: OptionalOrnekleri.java

  • NEDİR? "Değer olabilir VEYA olmayabilir" durumunu temsil eden bir kapsayıcı (Optional<T>). Amaç: null yerine, "değer yok" durumunu tip seviyesinde açıkça belirtmek.
  • NEDEN GELDİ? NullPointerException (NPE), Java'nın en sık hatasıdır. Tony Hoare null'ı "milyar dolarlık hata" diye anar. Optional, bir metodun "değer döndürmeyebilirim" demesini imza seviyesinde gösterir.
  • NE İŞE YARAR: NPE riskini azaltır, niyeti netleştirir, varsayılan değer/ alternatif akış yönetimini kolaylaştırır.

ESKİ vs YENİ:

// ESKİ: null kontrolleri iç içe, unutulursa NPE
Kullanici k = bul(id);
if (k != null && k.getEmail() != null) {
    System.out.println(k.getEmail().toUpperCase());
}

// YENİ: zincirleme, güvenli
String email = bul(id)
    .map(Kullanici::getEmail)
    .map(String::toUpperCase)
    .orElse("EMAIL YOK");

Önemli metotlar: of, ofNullable, empty, isPresent, ifPresent, map, flatMap, filter, orElse, orElseGet, orElseThrow.

İyi pratik: Optional'ı dönüş tipi olarak kullanın; alan (field) veya metot parametresi olarak kullanmaktan kaçının.

  • GERÇEK HAYAT: Banka hesabı bulunamazsa orElse(0.0) ile güvenli bakiye; kullanıcının e-postası yoksa zincirin güvenle "EMAIL YOK" döndürmesi.

6. Default ve Static Metotlar

Bkz: DefaultStaticMetotlar.java

  • NEDİR? Arayüzlerin artık gövdeli metot içerebilmesi: default (varsayılan uygulama) ve static (arayüz yardımcısı).
  • NEDEN GELDİ? Arayüz evrimi (interface evolution) problemi. Bir arayüze yeni soyut metot eklersen, onu uygulayan tüm mevcut sınıflar bozulur. Asıl tetikleyici: Stream API. Collection arayüzüne stream(), forEach(), removeIf() eklenmesi gerekiyordu; bunlar normal metot olsaydı dünyadaki tüm Collection uygulamaları kırılırdı. Default metotlar sayesinde varsayılan gövdeyle eklendiler ve hiçbir mevcut kod bozulmadı.
  • NE İŞE YARAR: Kütüphane geliştiricilere, kullanıcıları kırmadan arayüze yetenek ekleme imkânı.

Diamond problem: İki arayüzde aynı isimli default metot varsa, uygulayan sınıf bunu override etmek zorundadır; A.super.metot() ile birini seçebilir.

  • GERÇEK HAYAT: OdemeYontemi arayüzünde tüm ödeme tiplerinin ücretsiz miras aldığı ortak logla() default metodu; komisyonHesapla() static yardımcısı.

7. Yeni Date/Time API (java.time)

Bkz: YeniTarihSaatApi.java

  • NEDİR? LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Duration, Period, Instant gibi modern tarih/saat sınıfları (JSR-310, Joda-Time'dan ilham).

  • NEDEN GELDİ? (Eski Date/Calendar'ın sorunları):

    1. Değiştirilebilir (mutable): Date thread-safe değildi.
    2. Kafa karıştırıcı API: Calendar'da ay 0'dan başlardı (Ocak = 0!), yıl 1900 tabanlıydı. Sayısız hataya yol açtı.
    3. Zayıf zaman dilimi yönetimi.
    4. Zahmetli tarih aritmetiği.

    java.time hepsini çözer: değişmez (immutable), thread-safe, akıcı.

ESKİ vs YENİ:

// ESKİ: ay 0-tabanlı! Bu aslında HAZIRAN demek, MAYIS değil
Calendar cal = Calendar.getInstance();
cal.set(2026, 5, 24);

// YENİ: insan mantığıyla, Haziran = 6
LocalDate tarih = LocalDate.of(2026, 6, 24);
LocalDate gelecek = tarih.plusDays(10).plusMonths(2); // immutable, yeni nesne
  • NE İŞE YARAR: Yaş hesaplama (Period), mesai süresi (Duration), zaman dilimi dönüşümü (ZonedDateTime), formatlama/parse (DateTimeFormatter).
  • GERÇEK HAYAT: Fatura kesim ve son ödeme tarihi hesaplama, doğum gününden yaş çıkarma, İstanbul–New York–Tokyo saat dönüşümü.

8. Nashorn JavaScript Engine

Bkz: NashornOrnekleri.java

  • NEDİR? JVM üzerinde JavaScript çalıştıran yüksek performanslı motor (javax.script / JSR-223 üzerinden).
  • NEDEN GELDİ? Eski "Rhino" motoru yavaş ve eskimişti. Nashorn, invokedynamic bytecode'unu kullanarak çok daha hızlı çalışır; uygulamalara gömülü scriptleme yeteneği kazandırır.
  • NE İŞE YARAR: Java'yı yeniden derlemeden dinamik kural/yapılandırma; kural motorları; jjs komut satırı aracı.

ÖNEMLİ: Nashorn Java 11'de deprecated, Java 15'te tamamen kaldırıldı. Modern projelerde GraalVM JavaScript tercih edilir. Bu örnek tarihsel/öğretici amaçlıdır ve Java 8–14 ile çalışır.

  • GERÇEK HAYAT: İndirim kuralının JavaScript ile tanımlanıp Java kodu değişmeden güncellenebildiği bir kural motoru.

9. Collectors ve Gruplama

Bkz: CollectorsGruplama.java

  • NEDİR? Stream'in collect() işleminde kullanılan hazır toplayıcı "reçeteleri": toList, toSet, toMap, groupingBy, partitioningBy, joining, counting, averagingDouble, summingDouble, mapping, summarizingDouble.
  • NEDEN GELDİ? "Kategoriye göre grupla" gibi işlemler için elle Map yönetip "anahtar var mı, yoksa yeni liste aç" mantığını tekrar tekrar yazmak gerekiyordu. Collectors bunu tek satıra indirger.

Örnek:

// Departman bazlı çalışan sayısı
Map<String, Long> sayilar = calisanlar.stream()
    .collect(Collectors.groupingBy(c -> c.getDepartman(), Collectors.counting()));

// 50.000 üstü / altı ikiye bölme
Map<Boolean, List<Calisan>> bolme = calisanlar.stream()
    .collect(Collectors.partitioningBy(c -> c.getMaas() >= 50000));
  • GERÇEK HAYAT: Departman bazlı maaş ortalaması, çok seviyeli gruplama (departman → 40 yaş üstü mü → isimler), müşteri segmentasyonu, raporlama.

10. CompletableFuture (Asenkron Programlama)

Bkz: CompletableFutureOrnekleri.java

  • NEDİR? "İleride tamamlanacak bir hesabın sonucunu" temsil eden sınıf. Asenkron işleri başlatır ve sonuçlarını callback'lerle zincirler.
  • NEDEN GELDİ? (Eski Future'ın sorunları): get() ile bloklanmak gerekiyordu; iki future birleştirilemiyordu; otomatik callback ve zincirleme hata yönetimi yoktu. CompletableFuture bunların hepsini çözer.
  • NE İŞE YARAR: Web servisi/DB/dosya gibi I/O işlerini paralel yürütüp sonuçları birleştirmek; bloklamadan akış kurmak.

Temel metotlar: supplyAsync, thenApply, thenCompose, thenCombine, thenAccept, exceptionally, allOf, anyOf.

CompletableFuture<Double> toplam = fiyatF.thenCombine(vergiF,
    (fiyat, vergi) -> fiyat + vergi);
  • GERÇEK HAYAT: 3 farklı mikroservisi aynı anda çağırıp (paralel) hepsi bitince sonuçları birleştirmek — toplam süre, en yavaş servis kadar olur (sıralı çağrının toplamı değil).

11. StringJoiner ve String.join

Bkz: StringJoinerOrnekleri.java

  • NEDİR? String.join(...) statik metodu ve StringJoiner sınıfı; parçaları ayırıcı (delimiter), önek (prefix) ve sonek (suffix) ile birleştirir.
  • NEDEN GELDİ? "a, b, c" gibi birleştirme çok yaygındı ama standart yolu yoktu. Geliştiriciler StringBuilder + "son elemana virgül koyma" mantığını elle yazıyordu — tekrar eden ve hataya açık (fazladan virgül) kod.

ESKİ vs YENİ:

// ESKİ
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
    sb.append(list.get(i));
    if (i < list.size() - 1) sb.append(", ");
}

// YENİ
String s = String.join(", ", list);
  • GERÇEK HAYAT: CSV satırı üretme, SQL IN (...) listesi, URL query string (?k=v&...), rapor etiket listeleri.

12. Repeatable & Type Annotations

Bkz: AnnotationOrnekleri.java

  • Repeatable Annotations:

    • NEDİR? Aynı anotasyonu aynı eleman üzerinde birden fazla kez kullanma.
    • NEDEN GELDİ? Önceden bir anotasyon bir elemana yalnızca bir kez konabilirdi; birden fazlası için elle "kapsayıcı (container)" dizi yazmak gerekiyordu (@Schedules({@Schedule(...), @Schedule(...)})). @Repeatable bunu temizler.
  • Type Annotations (TYPE_USE):

    • NEDİR? Anotasyonların artık tipin kullanıldığı her yere konabilmesi (@NonNull String, List<@NonNull String>).
    • NEDEN GELDİ? Daha güçlü statik analiz / null güvenliği araçları (örn. Checker Framework) için.
  • GERÇEK HAYAT: Bir görevin birden fazla çalışma zamanını deklaratif olarak tanımlamak (@Zamanlama anotasyonunu Pazartesi/Çarşamba/Cuma için tekrarlama) — cron benzeri zamanlama.


13. Arrays.parallelSort ve PermGen → Metaspace

Bkz: JvmVeYardimcilar.java

A) Arrays.parallelSort

  • NEDİR? Diziyi paralel (fork/join havuzu) ile sıralayan metot.
  • NEDEN GELDİ? Arrays.sort tek iş parçacığında çalışır; büyük dizilerde çok çekirdeği kullanmak için parallelSort eklendi.
  • DİKKAT: Küçük dizilerde ek yük nedeniyle normal sort daha hızlı olabilir.

B) PermGen Kaldırıldı → Metaspace Geldi (JVM değişikliği)

  • ESKİ (Java 7 ve öncesi): Sınıf metadata'sı sabit boyutlu PermGen'de tutulurdu. Çok sınıf yükleyen uygulamalar (uygulama sunucuları, sık deploy edenler) sıkça OutOfMemoryError: PermGen space alırdı. -XX:MaxPermSize ile boyut elle ayarlanırdı, ayarlamak zordu.
  • YENİ (Java 8): PermGen tamamen kaldırıldı, yerine Metaspace geldi.
    • Metaspace heap'te değil, native (yerel) bellekte tutulur.
    • Varsayılan olarak otomatik büyür (sınırı işletim sistemi belleği; -XX:MaxMetaspaceSize ile sınırlanabilir).
    • Sonuç: "PermGen space" hatası tarihe karıştı; metadata yönetimi daha esnek.

Java 8'e Geçiş: Avantajlar, Dezavantajlar, Riskler

Avantajlar

  • Daha az kod, daha okunabilir: Lambda + Stream ile boilerplate ciddi azalır.
  • Fonksiyonel paradigma: Deklaratif, niyet-odaklı kod.
  • NPE azalması: Optional ile daha güvenli API tasarımı.
  • Modern tarih/saat: java.time, eski Date/Calendar dertlerini bitirir.
  • Asenkron kolaylığı: CompletableFuture ile bloklamadan paralel akış.
  • JVM iyileştirmesi: Metaspace ile PermGen hataları yok; performans artışları.
  • Geniş ekosistem: En yaygın LTS olduğu için kütüphane/topluluk desteği güçlü.

Dezavantajlar / Öğrenme Eğrisi

  • Zihniyet değişimi: OOP'den fonksiyonele geçiş, ekipler için adaptasyon ister.
  • Aşırı kullanım riski: Her şeyi tek satır stream'e sıkıştırmak okunabilirliği bozabilir; karmaşık zincirler debug edilmesi zor olabilir.
  • Stack trace okunabilirliği: Lambda/stream hataları bazen kafa karıştırıcı stack trace üretir.

Riskler (Geriye Dönük Uyumluluk & Performans)

  • Paralel stream tuzağı: Küçük veride veya paylaşılan değişebilir durumda (shared mutable state) paralel stream yanlış sonuç veya yavaşlık doğurabilir. Ortak ForkJoinPool.commonPool'u tükettiği için dikkatli kullanılmalı.
  • Optional'ı yanlış kullanma: Field/parametre olarak kullanmak anti-pattern'dir.
  • Nashorn bağımlılığı: Nashorn'a bağlı kod, Java 15+'a geçişte kırılır (kaldırıldı). Yeni projelerde kaçınılmalı.
  • Geçiş uyumu: Java 8 kodu genelde yeni sürümlere taşınabilir; ancak iç API (sun.misc.*), eski tarih API alışkanlıkları ve PermGen flag'leri (-XX:MaxPermSize artık yok sayılır) gibi noktalarda dikkat gerekir.
  • Performans: Lambda ilk çağrıda invokedynamic ile bir miktar başlatma maliyeti getirir; sıcak yollarda (hot path) ölçüm yapmak önerilir.

Dosya Listesi ve Çalıştırma

DosyaKonu
LambdaOrnekleri.javaLambda ifadeleri
FonksiyonelArayuzler.javaFunction/Consumer/Supplier/Predicate/BiFunction + @FunctionalInterface
MethodReferences.javaMethod reference'ların 4 türü
StreamApiOrnekleri.javaStream API (map/filter/reduce/collect/flatMap/parallel)
OptionalOrnekleri.javaOptional ile NPE yönetimi
DefaultStaticMetotlar.javaInterface'lerde default/static metotlar, diamond problem
YeniTarihSaatApi.javajava.time (LocalDate, Duration, Period, ZonedDateTime)
NashornOrnekleri.javaNashorn JavaScript Engine
CollectorsGruplama.javagroupingBy/partitioningBy/joining ve toplayıcılar
CompletableFutureOrnekleri.javaAsenkron programlama
StringJoinerOrnekleri.javaStringJoiner ve String.join
AnnotationOrnekleri.javaRepeatable & Type Annotations
JvmVeYardimcilar.javaArrays.parallelSort ve PermGen→Metaspace

Derleme ve Çalıştırma (her dosya bağımsızdır)

# Tek bir dosyayı derleyip çalıştırma:
javac LambdaOrnekleri.java
java LambdaOrnekleri

# Tüm dosyaları derleme:
javac *.java

# Örnek çalıştırmalar:
java StreamApiOrnekleri
java YeniTarihSaatApi
java CollectorsGruplama

Not: NashornOrnekleri.java yalnızca Java 8–14 ile anlamlı çıktı verir (Nashorn Java 15'te kaldırıldı). Diğer tüm dosyalar Java 8 ve üzerinde çalışır.

Kod Örnekleri(13)

AnnotationOrnekleri

çalıştırılabilir
AnnotationOrnekleri.java
1import java.lang.annotation.ElementType;
2import java.lang.annotation.Repeatable;
3import java.lang.annotation.Retention;
4import java.lang.annotation.RetentionPolicy;
5import java.lang.annotation.Target;
6import java.util.ArrayList;
7import java.util.List;
8
9/**
10 * ============================================================================
11 *  REPEATABLE ANNOTATIONS ve TYPE ANNOTATIONS - Java 8
12 * ============================================================================
13 *
14 * 1) REPEATABLE ANNOTATIONS (Tekrarlanabilir Anotasyonlar)
15 *    NEDİR? Aynı anotasyonu aynı eleman üzerinde BİRDEN FAZLA kez kullanabilme.
16 *    NEDEN GELDİ? Java 8 öncesinde bir anotasyon bir elemana sadece BİR kez
17 *      konabilirdi. Birden fazla değer için elle bir "kapsayıcı (container)"
18 *      anotasyon dizisi yazmak gerekiyordu (örn: @Schedules({@Schedule(...),
19 *      @Schedule(...)})). Bu çirkin ve zahmetliydi. @Repeatable bunu temizler.
20 *
21 * 2) TYPE ANNOTATIONS (Tip Anotasyonları)
22 *    NEDİR? Anotasyonların artık SADECE bildirimlere değil, TİPİN KULLANILDIĞI
23 *      her yere konabilmesi (ElementType.TYPE_USE).
24 *    NEDEN GELDİ? Daha güçlü statik analiz / null güvenliği araçları (örn:
25 *      Checker Framework) için. Örn: @NonNull String, List<@NonNull String>.
26 */
27public class AnnotationOrnekleri {
28
29    public static void main(String[] args) throws Exception {
30
31        System.out.println("=== 1. REPEATABLE ANNOTATION ===\n");
32        // Gorev sinifindaki tekrarlanan @Zamanlama anotasyonlarini oku
33        Zamanlama[] zamanlamalar = Gorev.class.getAnnotationsByType(Zamanlama.class);
34        System.out.println("Gorev uzerindeki zamanlamalar:");
35        for (Zamanlama z : zamanlamalar) {
36            System.out.println("   gun=" + z.gun() + ", saat=" + z.saat());
37        }
38
39        System.out.println("\n=== 2. TYPE ANNOTATION (TYPE_USE) ===\n");
40        // @NotNull bir tip kullanımı üzerinde; çalışma zamanında özel bir etki
41        // yapmaz ama statik analiz araçları bunu okur. Burada sadece derlenip
42        // kod içinde kullanılabildiğini gösteriyoruz.
43        @NotNull String mesaj = "Bu deger null olmamali (statik analiz ipucu)";
44        List<@NotNull String> liste = new ArrayList<>();
45        liste.add("eleman1");
46        liste.add("eleman2");
47        System.out.println("TYPE_USE annotation ile tanimlanan deger: " + mesaj);
48        System.out.println("List<@NotNull String> icerigi: " + liste);
49
50        System.out.println("\n=== 3. GERÇEK HAYAT: Cron benzeri zamanlama ===\n");
51        // Tekrarlanabilir anotasyonlar, bir gorevin birden fazla calisma
52        // zamanini deklaratif olarak tanimlamak icin idealdir.
53        System.out.println("Rapor gorevi su zamanlarda calisacak:");
54        for (Zamanlama z : Gorev.class.getAnnotationsByType(Zamanlama.class)) {
55            System.out.println("   -> Her " + z.gun() + " gunu saat " + z.saat());
56        }
57    }
58
59    // --- Repeatable annotation tanımı ---
60    // @Repeatable, kapsayıcı (container) anotasyonu işaret eder.
61    @Repeatable(Zamanlamalar.class)
62    @Retention(RetentionPolicy.RUNTIME)
63    @Target(ElementType.TYPE)
64    @interface Zamanlama {
65        String gun();
66        String saat();
67    }
68
69    // Kapsayıcı (container) anotasyon - derleyici tekrarları buraya toplar
70    @Retention(RetentionPolicy.RUNTIME)
71    @Target(ElementType.TYPE)
72    @interface Zamanlamalar {
73        Zamanlama[] value();
74    }
75
76    // --- Type annotation tanımı (TYPE_USE) ---
77    @Retention(RetentionPolicy.RUNTIME)
78    @Target(ElementType.TYPE_USE)
79    @interface NotNull {
80    }
81
82    // Aynı anotasyonu birden fazla kez kullanıyoruz (Java 8 öncesi imkansızdı)
83    @Zamanlama(gun = "Pazartesi", saat = "09:00")
84    @Zamanlama(gun = "Carsamba", saat = "14:00")
85    @Zamanlama(gun = "Cuma", saat = "17:30")
86    static class Gorev {
87    }
88}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

CollectorsGruplama

çalıştırılabilir
CollectorsGruplama.java
1import java.util.Arrays;
2import java.util.DoubleSummaryStatistics;
3import java.util.List;
4import java.util.Map;
5import java.util.TreeMap;
6import java.util.stream.Collectors;
7
8/**
9 * ============================================================================
10 *  COLLECTORS ve GRUPLAMA (groupingBy / partitioningBy / joining) - Java 8
11 * ============================================================================
12 *
13 * NEDİR?
14 *   Collectors, Stream'in collect() terminal işleminde kullanılan HAZIR
15 *   "toplayıcı reçeteleridir". Bir akışın elemanlarını listeye, kümeye,
16 *   haritaya (Map) toplamayı; gruplamayı; saymayı; ortalama almayı; metne
17 *   birleştirmeyi sağlar.
18 *
19 * NEDEN GELDİ?
20 *   Eskiden "ürünleri kategoriye göre grupla" gibi bir iş için elle Map
21 *   oluşturup, her eleman için "anahtar var mı, yoksa yeni liste aç" mantığını
22 *   tekrar tekrar yazmak gerekiyordu. Çok tekrar eden, hataya açık koddu.
23 *   Collectors bunları tek satırlık deklaratif ifadelere indirger.
24 *
25 * NE İŞE YARAR / NEREDE: Raporlama, istatistik, kategorize etme, özetleme.
26 *   Örn: departman bazlı maaş ortalaması, kategori bazlı stok, müşteri
27 *   segmentasyonu.
28 */
29public class CollectorsGruplama {
30
31    public static void main(String[] args) {
32
33        List<Calisan> calisanlar = Arrays.asList(
34                new Calisan("Ahmet", "Yazilim", 45000, 28),
35                new Calisan("Zeynep", "Yazilim", 62000, 35),
36                new Calisan("Mehmet", "Pazarlama", 38000, 41),
37                new Calisan("Ayse", "Pazarlama", 52000, 30),
38                new Calisan("Burak", "Finans", 71000, 45),
39                new Calisan("Elif", "Yazilim", 48000, 26));
40
41        System.out.println("=== 1. toList / toSet / toMap ===\n");
42        List<String> isimler = calisanlar.stream()
43                .map(c -> c.isim)
44                .collect(Collectors.toList());
45        System.out.println("Isimler (List): " + isimler);
46
47        // toMap: isim -> maas haritası
48        Map<String, Double> isimMaas = calisanlar.stream()
49                .collect(Collectors.toMap(c -> c.isim, c -> c.maas));
50        System.out.println("Isim->Maas (Map): " + isimMaas);
51
52        System.out.println("\n=== 2. groupingBy : Anahtara göre gruplama ===\n");
53        // ESKİ YÖNTEM elle Map yönetmek gerekirdi; YENİ tek satır:
54        Map<String, List<Calisan>> departmana = calisanlar.stream()
55                .collect(Collectors.groupingBy(c -> c.departman));
56        departmana.forEach((dep, liste) -> {
57            System.out.println(dep + ":");
58            liste.forEach(c -> System.out.println("   " + c.isim));
59        });
60
61        System.out.println("\n=== 3. groupingBy + counting : Grup sayıları ===\n");
62        Map<String, Long> departmanSayilari = calisanlar.stream()
63                .collect(Collectors.groupingBy(c -> c.departman, Collectors.counting()));
64        System.out.println("Departman bazli calisan sayisi: " + departmanSayilari);
65
66        System.out.println("\n=== 4. groupingBy + averagingDouble : Grup ortalaması ===\n");
67        Map<String, Double> departmanMaasOrt = calisanlar.stream()
68                .collect(Collectors.groupingBy(c -> c.departman,
69                        Collectors.averagingDouble(c -> c.maas)));
70        departmanMaasOrt.forEach((dep, ort) ->
71                System.out.printf("%-12s ortalama maas: %.2f TL%n", dep, ort));
72
73        System.out.println("\n=== 5. groupingBy + summingDouble / mapping ===\n");
74        // Departman bazlı toplam maaş
75        Map<String, Double> toplamMaas = calisanlar.stream()
76                .collect(Collectors.groupingBy(c -> c.departman,
77                        Collectors.summingDouble(c -> c.maas)));
78        System.out.println("Departman toplam maas: " + toplamMaas);
79
80        // groupingBy + mapping: departmana göre SADECE isimleri topla
81        Map<String, List<String>> departmanIsimleri = calisanlar.stream()
82                .collect(Collectors.groupingBy(c -> c.departman,
83                        Collectors.mapping(c -> c.isim, Collectors.toList())));
84        System.out.println("Departman isimleri: " + departmanIsimleri);
85
86        System.out.println("\n=== 6. partitioningBy : İkiye bölme (true/false) ===\n");
87        // Maaşı 50000'den yüksek olanlar / olmayanlar
88        Map<Boolean, List<Calisan>> bolme = calisanlar.stream()
89                .collect(Collectors.partitioningBy(c -> c.maas >= 50000));
90        System.out.println("Yuksek maaslilar (>=50000):");
91        bolme.get(true).forEach(c -> System.out.println("   " + c.isim + " - " + c.maas));
92        System.out.println("Dusuk maaslilar (<50000):");
93        bolme.get(false).forEach(c -> System.out.println("   " + c.isim + " - " + c.maas));
94
95        System.out.println("\n=== 7. joining : Metinleri birleştirme ===\n");
96        String tumIsimler = calisanlar.stream()
97                .map(c -> c.isim)
98                .collect(Collectors.joining(", ", "[", "]")); // ayirici, onek, sonek
99        System.out.println("Birlestirilmis: " + tumIsimler);
100
101        System.out.println("\n=== 8. summarizingDouble : Tek seferde istatistik ===\n");
102        DoubleSummaryStatistics maasIstatistik = calisanlar.stream()
103                .collect(Collectors.summarizingDouble(c -> c.maas));
104        System.out.println("Calisan sayisi : " + maasIstatistik.getCount());
105        System.out.println("Toplam maas    : " + maasIstatistik.getSum());
106        System.out.println("Ortalama       : " + maasIstatistik.getAverage());
107        System.out.println("En yuksek      : " + maasIstatistik.getMax());
108        System.out.println("En dusuk       : " + maasIstatistik.getMin());
109
110        System.out.println("\n=== 9. GERÇEK HAYAT: Çok seviyeli gruplama ===\n");
111        // Departman -> (40 yaş üstü mü?) -> isimler
112        Map<String, Map<Boolean, List<String>>> cokSeviyeli = calisanlar.stream()
113                .collect(Collectors.groupingBy(
114                        c -> c.departman,
115                        Collectors.groupingBy(
116                                c -> c.yas >= 40,
117                                Collectors.mapping(c -> c.isim, Collectors.toList()))));
118        cokSeviyeli.forEach((dep, yasMap) -> {
119            System.out.println(dep + ":");
120            System.out.println("   40+ : " + yasMap.getOrDefault(true, Arrays.asList()));
121            System.out.println("   <40 : " + yasMap.getOrDefault(false, Arrays.asList()));
122        });
123
124        System.out.println("\n=== 10. Sıralı grup (TreeMap supplier) ===\n");
125        // groupingBy ile harita tipini TreeMap olarak belirleyip alfabetik sirala
126        Map<String, Long> sirali = calisanlar.stream()
127                .collect(Collectors.groupingBy(c -> c.departman,
128                        TreeMap::new, Collectors.counting()));
129        System.out.println("Alfabetik sirali departmanlar: " + sirali);
130    }
131
132    static class Calisan {
133        String isim, departman;
134        double maas;
135        int yas;
136
137        Calisan(String isim, String departman, double maas, int yas) {
138            this.isim = isim;
139            this.departman = departman;
140            this.maas = maas;
141            this.yas = yas;
142        }
143    }
144}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

CompletableFutureOrnekleri

çalıştırılabilir
CompletableFutureOrnekleri.java
1import java.util.Arrays;
2import java.util.List;
3import java.util.concurrent.CompletableFuture;
4import java.util.concurrent.ExecutionException;
5import java.util.concurrent.ExecutorService;
6import java.util.concurrent.Executors;
7import java.util.stream.Collectors;
8
9/**
10 * ============================================================================
11 *  CompletableFuture - ASENKRON PROGRAMLAMA - Java 8
12 * ============================================================================
13 *
14 * NEDİR?
15 *   CompletableFuture<T>, "ileride tamamlanacak bir hesabın sonucunu" temsil
16 *   eden bir sınıftır. Asenkron (eş zamanlı olmayan) işleri başlatır ve
17 *   sonuçlarını ZİNCİRLEYEREK (callback'lerle) işlemeyi sağlar.
18 *
19 * NEDEN GELDİ? (Eski Future'ın sorunları)
20 *   Java 5'teki java.util.concurrent.Future yetersizdi:
21 *     - Sonucu almak için get() ile BLOKLANMAK gerekiyordu (thread bekler).
22 *     - İki future'ı birleştirmek (combine) mümkün değildi.
23 *     - Tamamlandığında otomatik callback çalıştırılamıyordu.
24 *     - Hata yönetimi (exception handling) zincirleme yapılamıyordu.
25 *
26 *   CompletableFuture bunların hepsini çözer: bloklamadan, zincirleyerek,
27 *   hataları yöneterek asenkron akışlar kurarsın.
28 *
29 * NE İŞE YARAR: Web servis çağrıları, veritabanı sorguları, dosya okuma gibi
30 *   I/O ağırlıklı işleri paralel yürütüp sonuçları birleştirme.
31 *
32 * TEMEL METOTLAR:
33 *   supplyAsync  : asenkron başlat (değer üretir)
34 *   thenApply    : sonucu dönüştür (map gibi)
35 *   thenCompose  : başka bir future ile zincirle (flatMap gibi)
36 *   thenCombine  : iki future'ı birleştir
37 *   thenAccept   : sonucu tüket (Consumer)
38 *   exceptionally: hata olursa yedek değer
39 *   allOf/anyOf  : birden fazla future'ı bekle
40 */
41public class CompletableFutureOrnekleri {
42
43    public static void main(String[] args) throws Exception {
44
45        System.out.println("=== 1. supplyAsync + thenApply + thenAccept ===\n");
46        CompletableFuture<String> akis = CompletableFuture
47                .supplyAsync(() -> {
48                    uyut(300);
49                    return "ham veri";
50                })
51                .thenApply(veri -> veri.toUpperCase())     // dönüştür
52                .thenApply(veri -> "[islenmis] " + veri);  // tekrar dönüştür
53        // thenAccept ile sonucu tüket (geri dönüş yok)
54        akis.thenAccept(sonuc -> System.out.println("Sonuc: " + sonuc));
55        akis.join(); // demo için ana thread'in bitmesini beklet
56
57        System.out.println("\n=== 2. thenCombine : İki asenkron işi birleştir ===\n");
58        // Senaryo: Fiyat servisi ve vergi servisi paralel calisir, sonra toplanir
59        CompletableFuture<Double> fiyatF = CompletableFuture.supplyAsync(() -> {
60            uyut(200);
61            System.out.println("   Fiyat servisi tamam");
62            return 1000.0;
63        });
64        CompletableFuture<Double> vergiF = CompletableFuture.supplyAsync(() -> {
65            uyut(250);
66            System.out.println("   Vergi servisi tamam");
67            return 180.0;
68        });
69        CompletableFuture<Double> toplamF = fiyatF.thenCombine(vergiF,
70                (fiyat, vergi) -> fiyat + vergi);
71        System.out.println("Toplam (fiyat+vergi): " + toplamF.get() + " TL");
72
73        System.out.println("\n=== 3. thenCompose : Bağımlı asenkron zincir ===\n");
74        // Önce kullanıcı id'sini al, SONRA o id ile siparişleri çek (sıralı bağımlılık)
75        CompletableFuture<String> siparisler = kullaniciIdGetir("ahmet")
76                .thenCompose(id -> siparisleriGetir(id));
77        System.out.println(siparisler.get());
78
79        System.out.println("\n=== 4. exceptionally : Hata yönetimi ===\n");
80        CompletableFuture<Integer> hataliAkis = CompletableFuture
81                .<Integer>supplyAsync(() -> {
82                    throw new RuntimeException("Servis cokmesi!");
83                })
84                .exceptionally(ex -> {
85                    System.out.println("   Hata yakalandi: " + ex.getMessage());
86                    return -1; // yedek (fallback) deger
87                });
88        System.out.println("Hatadan sonra deger: " + hataliAkis.get());
89
90        System.out.println("\n=== 5. allOf : Birden fazla işi paralel bekle ===\n");
91        // 3 farklı servisi aynı anda çağır, hepsi bitince devam et
92        long baslangic = System.currentTimeMillis();
93        CompletableFuture<String> s1 = servisCagir("Servis-A", 300);
94        CompletableFuture<String> s2 = servisCagir("Servis-B", 200);
95        CompletableFuture<String> s3 = servisCagir("Servis-C", 400);
96
97        CompletableFuture<Void> hepsi = CompletableFuture.allOf(s1, s2, s3);
98        hepsi.join(); // hepsi bitene kadar bekle
99
100        // Sonuçları topla
101        List<String> sonuclar = Arrays.asList(s1, s2, s3).stream()
102                .map(CompletableFuture::join)
103                .collect(Collectors.toList());
104        long sure = System.currentTimeMillis() - baslangic;
105        System.out.println("Tum servisler tamam: " + sonuclar);
106        // 300+200+400=900ms degil; paralel oldugu icin ~400ms (en yavasi kadar)
107        System.out.println("Toplam sure (paralel): ~" + sure + " ms (sirali olsa ~900ms)");
108
109        System.out.println("\n=== 6. Özel ExecutorService ile ===\n");
110        ExecutorService havuz = Executors.newFixedThreadPool(2);
111        CompletableFuture<String> ozel = CompletableFuture.supplyAsync(() -> {
112            return "Ozel thread havuzunda calisti: " + Thread.currentThread().getName();
113        }, havuz);
114        System.out.println(ozel.get());
115        havuz.shutdown();
116    }
117
118    static CompletableFuture<String> kullaniciIdGetir(String kullaniciAdi) {
119        return CompletableFuture.supplyAsync(() -> {
120            uyut(150);
121            return "ID-" + kullaniciAdi.hashCode();
122        });
123    }
124
125    static CompletableFuture<String> siparisleriGetir(String id) {
126        return CompletableFuture.supplyAsync(() -> {
127            uyut(150);
128            return id + " icin siparisler: [S100, S101, S102]";
129        });
130    }
131
132    static CompletableFuture<String> servisCagir(String ad, int gecikme) {
133        return CompletableFuture.supplyAsync(() -> {
134            uyut(gecikme);
135            return ad + "(" + gecikme + "ms)";
136        });
137    }
138
139    static void uyut(long ms) {
140        try {
141            Thread.sleep(ms);
142        } catch (InterruptedException e) {
143            Thread.currentThread().interrupt();
144        }
145    }
146}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

DefaultStaticMetotlar

çalıştırılabilir
DefaultStaticMetotlar.java
1/**
2 * ============================================================================
3 *  INTERFACE'LERDE DEFAULT ve STATIC METOTLAR - Java 8
4 * ============================================================================
5 *
6 * NEDİR?
7 *   Java 8 ile arayüzler (interface), gövdesi olan iki yeni metot türü
8 *   içerebilir:
9 *     - default metot : Bir varsayılan uygulama (implementation) sağlar.
10 *                       Uygulayan sınıflar override etmek zorunda DEĞİLDİR.
11 *     - static metot  : Arayüze bağlı yardımcı (utility) metot. Örnek üzerinden
12 *                       değil, arayüz adıyla çağrılır.
13 *
14 * NEDEN GELDİ? (Hangi problemi çözdü?)
15 *   "Interface Evolution" (arayüz evrimi) problemi: Bir arayüzü yayınladıktan
16 *   sonra yeni bir metot eklersen, o arayüzü uygulayan TÜM mevcut sınıflar
17 *   derlenemez hale gelirdi (her birine metodu eklemek gerekirdi). Bu, geriye
18 *   dönük uyumluluğu bozardı.
19 *
20 *   Asıl tetikleyici: Java 8'in Stream API'si. List, Collection gibi mevcut
21 *   arayüzlere forEach(), stream(), removeIf() gibi metotlar eklenmesi
22 *   gerekiyordu. Eğer bunlar normal (soyut) metot olsaydı, dünyadaki tüm
23 *   Collection uygulamaları bozulurdu. DEFAULT metotlar sayesinde bu metotlar
24 *   varsayılan gövdeyle eklendi ve hiçbir mevcut kod bozulmadı.
25 *
26 * NE İŞE YARAR: Kütüphane geliştiricilerine, mevcut kullanıcıları kırmadan
27 *   arayüzlere yeni yetenek ekleme imkânı verir.
28 */
29public class DefaultStaticMetotlar {
30
31    public static void main(String[] args) {
32
33        System.out.println("=== 1. default Metot: Varsayılan Uygulama ===\n");
34        Arac araba = new Araba();
35        araba.calistir();        // Araba'nin kendi (override) uygulamasi
36        araba.kornaCal();        // arayuzden gelen DEFAULT uygulama
37
38        Arac bisiklet = new Bisiklet();
39        bisiklet.calistir();
40        bisiklet.kornaCal();     // Bisiklet bunu OVERRIDE etti
41
42        System.out.println("\n=== 2. static Metot: Arayüz Yardımcısı ===\n");
43        // Arayüz adıyla çağrılır, örnek gerekmez
44        System.out.println(Arac.aracTipiAciklamasi());
45
46        System.out.println("\n=== 3. GERÇEK HAYAT: Ödeme yöntemleri ===\n");
47        OdemeYontemi krediKarti = new KrediKarti();
48        OdemeYontemi havale = new Havale();
49
50        krediKarti.ode(1000);
51        // logla() -> arayuzden gelen ortak default davranis (her ikisi de kullanir)
52        krediKarti.logla(1000);
53        havale.ode(500);
54        havale.logla(500);
55
56        // static metot: komisyon hesaplama yardımcısı
57        System.out.println("1000 TL icin standart komisyon: "
58                + OdemeYontemi.komisyonHesapla(1000) + " TL");
59
60        System.out.println("\n=== 4. Çoklu Kalıtım Çakışması (Diamond Problem) ===\n");
61        // A ve B arayüzlerinin ikisinde de aynı isimli default metot var.
62        // C sınıfı bunu çözmek için override etmek ZORUNDADIR (derleyici zorlar).
63        C c = new C();
64        c.selam();  // C'nin cozumledigi versiyon
65    }
66
67    /** default ve static metot içeren arayüz. */
68    interface Arac {
69        // Soyut metot (uygulayan sınıf MUTLAKA yazmalı)
70        void calistir();
71
72        // DEFAULT metot: gövdeli, override zorunlu değil
73        default void kornaCal() {
74            System.out.println("   Bip bip! (varsayilan korna)");
75        }
76
77        // STATIC metot: arayuz duzeyinde yardimci
78        static String aracTipiAciklamasi() {
79            return "Bu bir kara tasiti arayuzudur.";
80        }
81    }
82
83    static class Araba implements Arac {
84        @Override
85        public void calistir() {
86            System.out.println("Araba motoru calisti (vroom).");
87        }
88        // kornaCal override edilmedi -> default kullanilir
89    }
90
91    static class Bisiklet implements Arac {
92        @Override
93        public void calistir() {
94            System.out.println("Bisiklet pedallari donuyor.");
95        }
96        @Override
97        public void kornaCal() {
98            System.out.println("   Cling cling! (bisiklet zili - override)");
99        }
100    }
101
102    interface OdemeYontemi {
103        void ode(double tutar);
104
105        // Ortak loglama davranışı - tüm ödeme yöntemleri ücretsiz miras alır
106        default void logla(double tutar) {
107            System.out.println("   [LOG] " + tutar + " TL odeme kaydedildi.");
108        }
109
110        static double komisyonHesapla(double tutar) {
111            return tutar * 0.015; // %1.5 standart komisyon
112        }
113    }
114
115    static class KrediKarti implements OdemeYontemi {
116        @Override
117        public void ode(double tutar) {
118            System.out.println("Kredi karti ile " + tutar + " TL odendi.");
119        }
120    }
121
122    static class Havale implements OdemeYontemi {
123        @Override
124        public void ode(double tutar) {
125            System.out.println("Havale/EFT ile " + tutar + " TL gonderildi.");
126        }
127    }
128
129    // --- Diamond problem örneği ---
130    interface A {
131        default void selam() { System.out.println("A'dan selam"); }
132    }
133    interface B {
134        default void selam() { System.out.println("B'den selam"); }
135    }
136    // Hem A hem B'yi uygular; iki default metot çakışır -> override ZORUNLU
137    static class C implements A, B {
138        @Override
139        public void selam() {
140            A.super.selam(); // istersek belirli birini çağırabiliriz
141            B.super.selam();
142            System.out.println("C, cakismayi cozumledi.");
143        }
144    }
145}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

FonksiyonelArayuzler

çalıştırılabilir
FonksiyonelArayuzler.java
1import java.util.Arrays;
2import java.util.List;
3import java.util.function.BiFunction;
4import java.util.function.BinaryOperator;
5import java.util.function.Consumer;
6import java.util.function.Function;
7import java.util.function.Predicate;
8import java.util.function.Supplier;
9import java.util.function.UnaryOperator;
10
11/**
12 * ============================================================================
13 *  FONKSİYONEL ARAYÜZLER (FUNCTIONAL INTERFACES) - Java 8
14 * ============================================================================
15 *
16 * NEDİR?
17 *   İçinde TEK BİR soyut metot bulunan arayüzdür (Single Abstract Method - SAM).
18 *   Lambda ifadeleri ve method reference'lar, ancak bir fonksiyonel arayüz
19 *   tipine atanabilir. Yani lambda'nın "tipi" daima bir fonksiyonel arayüzdür.
20 *
21 *   @FunctionalInterface anotasyonu derleyiciye "bu arayüzde tam olarak 1 soyut
22 *   metot olmalı" der; yanlışlıkla ikinci bir soyut metot eklenirse DERLEME
23 *   HATASI verir. (Zorunlu değildir ama tavsiye edilir.)
24 *
25 * NEDEN GELDİ?
26 *   Java zaten Runnable, Comparator, Callable gibi tek metotlu arayüzlere
27 *   sahipti ama her ihtiyaç için yeni arayüz tanımlamak zahmetliydi. Java 8,
28 *   java.util.function paketinde HAZIR, genel amaçlı fonksiyonel arayüzler
29 *   getirdi. Böylece kendi arayüzünü yazmadan davranış geçirebilirsin.
30 *
31 * EN ÇOK KULLANILANLAR:
32 *   - Function<T,R>      : T alır, R döndürür           (apply)
33 *   - Consumer<T>        : T alır, hiçbir şey döndürmez  (accept)
34 *   - Supplier<T>        : Parametre almaz, T üretir     (get)
35 *   - Predicate<T>       : T alır, boolean döndürür       (test)
36 *   - BiFunction<T,U,R>  : İki girdi (T,U), R döndürür    (apply)
37 *   - UnaryOperator<T>   : T alır, T döndürür             (Function özel hali)
38 *   - BinaryOperator<T>  : İki T alır, T döndürür         (BiFunction özel hali)
39 */
40public class FonksiyonelArayuzler {
41
42    public static void main(String[] args) {
43
44        System.out.println("=== 1. Function<T,R> : Dönüştürme ===\n");
45        // Bir String'i uzunluğuna dönüştür
46        Function<String, Integer> uzunluk = s -> s.length();
47        System.out.println("'Merhaba' uzunlugu: " + uzunluk.apply("Merhaba"));
48
49        // andThen / compose ile fonksiyon zincirleme
50        Function<Integer, Integer> ikiyleCarp = x -> x * 2;
51        Function<Integer, Integer> birEkle = x -> x + 1;
52        // önce çarp sonra ekle:  (5*2)+1 = 11
53        System.out.println("andThen (carp->ekle): " + ikiyleCarp.andThen(birEkle).apply(5));
54        // önce ekle sonra çarp:  (5+1)*2 = 12
55        System.out.println("compose (ekle->carp): " + ikiyleCarp.compose(birEkle).apply(5));
56
57        System.out.println("\n=== 2. Consumer<T> : Tüketme (yan etki) ===\n");
58        Consumer<String> yazdir = s -> System.out.println("Tuketildi: " + s);
59        yazdir.accept("Java 8");
60        // andThen ile iki consumer'ı sırayla çalıştır
61        Consumer<String> buyukYaz = s -> System.out.println("BUYUK: " + s.toUpperCase());
62        yazdir.andThen(buyukYaz).accept("zincir");
63
64        System.out.println("\n=== 3. Supplier<T> : Üretme ===\n");
65        Supplier<Double> rastgele = () -> Math.random();
66        System.out.println("Rastgele uretildi: " + rastgele.get());
67        // Lazy (tembel) değer üretiminde çok kullanışlıdır.
68
69        System.out.println("\n=== 4. Predicate<T> : Koşul testi ===\n");
70        Predicate<Integer> ciftMi = n -> n % 2 == 0;
71        Predicate<Integer> pozitifMi = n -> n > 0;
72        System.out.println("4 cift mi?          " + ciftMi.test(4));
73        // and / or / negate ile birleştirme
74        System.out.println("6 cift VE pozitif?  " + ciftMi.and(pozitifMi).test(6));
75        System.out.println("-3 cift DEGIL mi?   " + ciftMi.negate().test(-3));
76
77        System.out.println("\n=== 5. BiFunction<T,U,R> : İki girdi ===\n");
78        BiFunction<Integer, Integer, Integer> topla = (a, b) -> a + b;
79        System.out.println("3 + 4 = " + topla.apply(3, 4));
80        BiFunction<String, String, String> birlestir = (a, b) -> a + " " + b;
81        System.out.println(birlestir.apply("Merhaba", "Dunya"));
82
83        System.out.println("\n=== 6. UnaryOperator & BinaryOperator ===\n");
84        UnaryOperator<String> kucult = s -> s.toLowerCase();
85        System.out.println("Kucult: " + kucult.apply("JAVA"));
86        BinaryOperator<Integer> maks = (a, b) -> a > b ? a : b;
87        System.out.println("Maks(10, 25): " + maks.apply(10, 25));
88
89        System.out.println("\n=== 7. GERÇEK HAYAT: Ürün Filtreleme (E-ticaret) ===\n");
90        List<Urun> urunler = Arrays.asList(
91                new Urun("Telefon", 15000, true),
92                new Urun("Kulaklik", 800, false),
93                new Urun("Laptop", 32000, true),
94                new Urun("Mouse", 350, true));
95
96        // Predicate'leri yeniden kullanılabilir kurallar olarak tanımla
97        Predicate<Urun> stokta = u -> u.stoktaVar;
98        Predicate<Urun> pahali = u -> u.fiyat > 1000;
99
100        System.out.println("Stokta OLAN ve PAHALI urunler:");
101        for (Urun u : urunler) {
102            // İş kurallarını birleştir: stokta VE pahalı
103            if (stokta.and(pahali).test(u)) {
104                System.out.println("   " + u);
105            }
106        }
107
108        // Function ile vergi hesaplama kuralı
109        Function<Urun, Double> kdvliFiyat = u -> u.fiyat * 1.20;
110        System.out.println("\nKDV'li fiyatlar:");
111        urunler.forEach(u ->
112                System.out.printf("   %-10s : %.2f TL%n", u.ad, kdvliFiyat.apply(u)));
113
114        System.out.println("\n=== 8. ÖZEL @FunctionalInterface Tanımlama ===\n");
115        IndirimKurali yuzde10 = fiyat -> fiyat * 0.90;
116        IndirimKurali sabit50 = fiyat -> fiyat - 50;
117        System.out.println("1000 TL'ye %10 indirim: " + yuzde10.uygula(1000));
118        System.out.println("1000 TL'ye 50 TL indirim: " + sabit50.uygula(1000));
119    }
120
121    /**
122     * Kendi fonksiyonel arayüzümüz. @FunctionalInterface sayesinde ikinci bir
123     * soyut metot eklersek derleyici hata verir (güvenlik).
124     */
125    @FunctionalInterface
126    interface IndirimKurali {
127        double uygula(double fiyat);
128    }
129
130    static class Urun {
131        String ad;
132        double fiyat;
133        boolean stoktaVar;
134
135        Urun(String ad, double fiyat, boolean stoktaVar) {
136            this.ad = ad;
137            this.fiyat = fiyat;
138            this.stoktaVar = stoktaVar;
139        }
140
141        @Override
142        public String toString() {
143            return ad + " - " + fiyat + " TL (stok: " + (stoktaVar ? "var" : "yok") + ")";
144        }
145    }
146}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

JvmVeYardimcilar

çalıştırılabilir
JvmVeYardimcilar.java
1import java.util.Arrays;
2import java.util.Random;
3
4/**
5 * ============================================================================
6 *  JVM DEĞİŞİKLİKLERİ ve YARDIMCI YENİLİKLER - Java 8
7 * ============================================================================
8 *
9 * Bu dosya iki konuyu örnekler:
10 *   A) Arrays.parallelSort  : Büyük dizileri çoklu çekirdekte sıralama
11 *   B) PermGen -> Metaspace  : JVM bellek modeli değişikliği (anlatım + kanıt)
12 *
13 * ----------------------------------------------------------------------------
14 * A) Arrays.parallelSort
15 *   NEDİR? Diziyi paralel (fork/join havuzu ile) sıralayan metot.
16 *   NEDEN GELDİ? Arrays.sort tek iş parçacığında çalışır. Çok büyük dizilerde
17 *     çok çekirdekli işlemcileri kullanmak için parallelSort eklendi.
18 *   NE İŞE YARAR: Büyük veri kümelerinde sıralamayı hızlandırır.
19 *   DİKKAT: Küçük dizilerde ek yük nedeniyle normal sort daha hızlı olabilir.
20 *
21 * ----------------------------------------------------------------------------
22 * B) PermGen Kaldırıldı -> Metaspace Geldi (JVM iç değişikliği)
23 *   ESKİ (Java 7 ve öncesi): PermGen (Permanent Generation) adında SABİT
24 *     boyutlu bir bellek alanı vardı. Sınıf metadata'sı (yüklenen sınıf
25 *     bilgileri) burada tutulurdu. Çok sayıda sınıf yükleyen uygulamalarda
26 *     (özellikle uygulama sunucuları, sık deploy yapan sistemler) sıkça
27 *     "java.lang.OutOfMemoryError: PermGen space" hatası alınırdı.
28 *     -XX:MaxPermSize ile boyut elle ayarlanırdı; ayarlamak zordu.
29 *
30 *   YENİ (Java 8): PermGen tamamen KALDIRILDI. Yerine METASPACE geldi.
31 *     - Metaspace, JVM heap'inde DEĞİL, yerel (native) bellekte tutulur.
32 *     - Varsayılan olarak boyutu OTOMATİK büyür (sınırı yalnızca işletim
33 *       sistemi belleğidir; -XX:MaxMetaspaceSize ile sınırlanabilir).
34 *     - Sonuç: "PermGen space" hatası tarihe karıştı; sınıf metadata yönetimi
35 *       daha esnek ve daha az sorunlu hale geldi.
36 */
37public class JvmVeYardimcilar {
38
39    public static void main(String[] args) {
40
41        System.out.println("=== A) Arrays.parallelSort vs Arrays.sort ===\n");
42
43        int boyut = 2_000_000;
44        int[] dizi1 = rastgeleDizi(boyut);
45        int[] dizi2 = Arrays.copyOf(dizi1, dizi1.length); // ayni veri
46
47        // ESKİ/Klasik: tek iş parçacıklı sort
48        long t1 = System.currentTimeMillis();
49        Arrays.sort(dizi1);
50        long sureSort = System.currentTimeMillis() - t1;
51
52        // YENİ: paralel sort (fork/join)
53        long t2 = System.currentTimeMillis();
54        Arrays.parallelSort(dizi2);
55        long sureParalel = System.currentTimeMillis() - t2;
56
57        System.out.println("Dizi boyutu        : " + boyut);
58        System.out.println("Arrays.sort        : " + sureSort + " ms");
59        System.out.println("Arrays.parallelSort: " + sureParalel + " ms");
60        System.out.println("Cekirdek sayisi    : " + Runtime.getRuntime().availableProcessors());
61        System.out.println("Iki sonuc esit mi? : " + Arrays.equals(dizi1, dizi2));
62        System.out.println("(Not: kucuk dizilerde paralel daha YAVAS olabilir.)");
63
64        System.out.println("\n=== B) PermGen -> Metaspace (JVM bellek bilgisi) ===\n");
65        // Çalışılan JVM sürümü hakkında bilgi - Java 8'de PermGen yoktur
66        System.out.println("Java surumu        : " + System.getProperty("java.version"));
67        System.out.println("JVM adi            : " + System.getProperty("java.vm.name"));
68
69        long maxHeap = Runtime.getRuntime().maxMemory();
70        System.out.printf("Max heap (yaklasik): %.0f MB%n", maxHeap / (1024.0 * 1024.0));
71
72        System.out.println();
73        System.out.println("Java 7'de sinif metadata'si SABIT boyutlu PermGen'de tutulurdu");
74        System.out.println("ve 'OutOfMemoryError: PermGen space' sik gorulurdu.");
75        System.out.println("Java 8'de PermGen kaldirildi; metadata artik native bellekteki");
76        System.out.println("METASPACE'te tutulur ve otomatik buyur. Bu hata tarihe karisti.");
77    }
78
79    static int[] rastgeleDizi(int boyut) {
80        Random r = new Random(42); // sabit tohum -> tekrarlanabilir
81        int[] dizi = new int[boyut];
82        for (int i = 0; i < boyut; i++) {
83            dizi[i] = r.nextInt();
84        }
85        return dizi;
86    }
87}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

LambdaOrnekleri

çalıştırılabilir
LambdaOrnekleri.java
1import java.util.ArrayList;
2import java.util.Arrays;
3import java.util.Collections;
4import java.util.Comparator;
5import java.util.List;
6import java.util.function.Function;
7
8/**
9 * ============================================================================
10 *  LAMBDA İFADELERİ (LAMBDA EXPRESSIONS) - Java 8
11 * ============================================================================
12 *
13 * NEDİR?
14 *   Lambda ifadesi, isimsiz (anonim) bir fonksiyondur. Kısaca "bir metodu,
15 *   bir nesneye sarmadan, doğrudan davranış olarak geçirme" yöntemidir.
16 *
17 *   Sözdizimi:  (parametreler) -> { gövde }
18 *   Örnek:      (a, b) -> a + b
19 *
20 * NEDEN GELDİ? (Hangi problemi çözdü?)
21 *   Java 8 öncesinde bir davranışı (kod parçasını) bir metoda geçirmek için
22 *   ANONİM İÇ SINIF (anonymous inner class) yazmak zorundaydık. Bu çok
23 *   gürültülü (boilerplate) koda yol açıyordu. 1 satırlık iş için 5-6 satır
24 *   tören kodu yazmak gerekiyordu.
25 *
26 * NE İŞE YARAR / NEREDE KOLAYLIK SAĞLAR?
27 *   - Koleksiyonları sıralama (Comparator)
28 *   - Olay dinleyicileri (event listener)
29 *   - Stream API ile birlikte filtreleme/dönüştürme
30 *   - Çok kısa, okunabilir, fonksiyonel kod
31 */
32public class LambdaOrnekleri {
33
34    public static void main(String[] args) {
35
36        System.out.println("=== 1. ESKİ YÖNTEM vs YENİ YÖNTEM: Comparator ===\n");
37
38        List<String> isimler = new ArrayList<>(
39                Arrays.asList("Zeynep", "Ahmet", "Mehmet", "Ayse", "Burak"));
40
41        // ESKİ YÖNTEM (Java 7 ve öncesi): Anonim iç sınıf
42        // Sadece "iki ismi karşılaştır" demek için 5 satır boilerplate kod.
43        Collections.sort(isimler, new Comparator<String>() {
44            @Override
45            public int compare(String a, String b) {
46                return a.compareTo(b);
47            }
48        });
49        System.out.println("Eski yöntemle siralandi : " + isimler);
50
51        // YENİ YÖNTEM (Java 8): Lambda ifadesi - tek satır
52        Collections.sort(isimler, (a, b) -> b.compareTo(a)); // ters sıralama
53        System.out.println("Lambda ile siralandi    : " + isimler);
54
55        System.out.println("\n=== 2. Lambda Sözdizimi Çeşitleri ===\n");
56
57        // Parametresiz lambda
58        Runnable selam = () -> System.out.println("Merhaba Lambda dunyasi!");
59        selam.run();
60
61        // Tek parametreli (parantez opsiyonel)
62        Function<Integer, Integer> kare = x -> x * x;
63        System.out.println("5'in karesi: " + kare.apply(5));
64
65        // Çok parametreli + çok satırlı gövde (blok)
66        Hesaplama topla = (a, b) -> {
67            int sonuc = a + b;
68            System.out.println(a + " + " + b + " hesaplaniyor...");
69            return sonuc;
70        };
71        System.out.println("Sonuc: " + topla.hesapla(7, 8));
72
73        System.out.println("\n=== 3. GERÇEK HAYAT ÖRNEĞİ: Çalışan Listesi Sıralama ===\n");
74
75        List<Calisan> calisanlar = Arrays.asList(
76                new Calisan("Ahmet", "Yazilim", 45000),
77                new Calisan("Zeynep", "Pazarlama", 52000),
78                new Calisan("Mehmet", "Yazilim", 38000),
79                new Calisan("Ayse", "Finans", 61000));
80
81        // Maaşa göre artan sıralama (lambda + Comparator)
82        calisanlar.sort((c1, c2) -> Double.compare(c1.maas, c2.maas));
83        System.out.println("Maasa gore artan:");
84        calisanlar.forEach(c -> System.out.println("   " + c));
85
86        // Comparator.comparing ile daha da okunabilir (method reference önizleme)
87        calisanlar.sort(Comparator.comparing((Calisan c) -> c.departman)
88                .thenComparing(c -> c.isim));
89        System.out.println("\nDepartman, sonra isme gore:");
90        calisanlar.forEach(c -> System.out.println("   " + c));
91
92        System.out.println("\n=== 4. forEach ile Lambda (Iterable.forEach) ===\n");
93        // ESKİ YÖNTEM: for döngüsü
94        for (Calisan c : calisanlar) {
95            System.out.println("Eski for: " + c.isim);
96        }
97        // YENİ YÖNTEM: forEach + lambda
98        calisanlar.forEach(c -> System.out.println("Lambda forEach: " + c.isim));
99
100        System.out.println("\n=== 5. Effectively Final (Kapanış / Closure) ===\n");
101        // Lambda, dışarıdaki yerel değişkenleri kullanabilir; ancak bu değişkenler
102        // "effectively final" (etkin olarak değişmez) olmalıdır.
103        String onek = ">> "; // değiştirilmiyor -> effectively final
104        calisanlar.forEach(c -> System.out.println(onek + c.isim));
105    }
106
107    /** Lambda'nın hedefi olacak fonksiyonel arayüz (tek soyut metot). */
108    @FunctionalInterface
109    interface Hesaplama {
110        int hesapla(int a, int b);
111    }
112
113    /** Gerçek hayat domain sınıfı: Çalışan. */
114    static class Calisan {
115        String isim;
116        String departman;
117        double maas;
118
119        Calisan(String isim, String departman, double maas) {
120            this.isim = isim;
121            this.departman = departman;
122            this.maas = maas;
123        }
124
125        @Override
126        public String toString() {
127            return isim + " (" + departman + ", " + maas + " TL)";
128        }
129    }
130}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

MethodReferences

çalıştırılabilir
MethodReferences.java
1import java.util.Arrays;
2import java.util.Comparator;
3import java.util.List;
4import java.util.function.BiFunction;
5import java.util.function.Function;
6import java.util.function.Supplier;
7import java.util.stream.Collectors;
8
9/**
10 * ============================================================================
11 *  METHOD REFERENCES (Metot Referansları) - Java 8
12 * ============================================================================
13 *
14 * NEDİR?
15 *   Lambda ifadesinin daha da kısaltılmış halidir. Eğer bir lambda SADECE
16 *   var olan bir metodu çağırıyorsa, lambda yerine doğrudan o metodun
17 *   referansını verebiliriz. "::" operatörü kullanılır.
18 *
19 *   s -> System.out.println(s)   ==   System.out::println
20 *
21 * NEDEN GELDİ?
22 *   Lambda zaten kısaydı ama "x -> Sinif.metot(x)" gibi yalnızca bir metoda
23 *   delege eden lambdalar hâlâ gürültülüydü. Method reference bunu temizler;
24 *   niyeti ("şu metodu kullan") açıkça gösterir, okunabilirliği artırır.
25 *
26 * DÖRT TÜRÜ VARDIR:
27 *   1) Static metot referansı           ->  Sinif::statikMetot
28 *   2) Belirli bir nesnenin metodu       ->  nesne::ornekMetot
29 *   3) Rastgele bir nesnenin örnek metodu->  Sinif::ornekMetot
30 *   4) Yapıcı (constructor) referansı    ->  Sinif::new
31 */
32public class MethodReferences {
33
34    public static void main(String[] args) {
35
36        List<String> kelimeler = Arrays.asList("elma", "Armut", "kiraz", "Banana");
37
38        System.out.println("=== TÜR 1: Static Metot Referansı (Sinif::statikMetot) ===\n");
39        // LAMBDA:  s -> Integer.parseInt(s)
40        // REF   :  Integer::parseInt
41        List<String> sayilar = Arrays.asList("10", "25", "3", "47");
42        int toplam = sayilar.stream()
43                .mapToInt(Integer::parseInt)   // static metot referansı
44                .sum();
45        System.out.println("Stringlerin toplami: " + toplam);
46
47        // Kendi static metodumuza referans
48        Function<String, String> selamla = MethodReferences::selamVer;
49        System.out.println(selamla.apply("Ahmet"));
50
51        System.out.println("\n=== TÜR 2: Belirli Nesnenin Metodu (nesne::metot) ===\n");
52        // LAMBDA:  s -> System.out.println(s)
53        // REF   :  System.out::println   (System.out belirli bir nesnedir)
54        kelimeler.forEach(System.out::println);
55
56        // Belirli bir nesneye bağlı
57        String onek = "Onek:";
58        Function<String, Boolean> baslarMi = onek::startsWith; // 'onek' nesnesinin metodu
59        System.out.println("'Onek:Test'.startsWith uygulamasi -> " + baslarMi.apply("Onek"));
60
61        System.out.println("\n=== TÜR 3: Rastgele Nesnenin Örnek Metodu (Sinif::metot) ===\n");
62        // LAMBDA:  s -> s.toUpperCase()    (her eleman için kendi metodu)
63        // REF   :  String::toUpperCase
64        List<String> buyukler = kelimeler.stream()
65                .map(String::toUpperCase)   // her String'in kendi toUpperCase'i
66                .collect(Collectors.toList());
67        System.out.println("Buyuk harfli: " + buyukler);
68
69        // Sıralamada çok yaygın: String::compareToIgnoreCase
70        List<String> sirali = kelimeler.stream()
71                .sorted(String::compareToIgnoreCase)
72                .collect(Collectors.toList());
73        System.out.println("Buyuk/kucuk farketmeden sirali: " + sirali);
74
75        System.out.println("\n=== TÜR 4: Yapıcı (Constructor) Referansı (Sinif::new) ===\n");
76        // LAMBDA:  ad -> new Kullanici(ad)
77        // REF   :  Kullanici::new
78        Function<String, Kullanici> kullaniciUret = Kullanici::new;
79        Kullanici k = kullaniciUret.apply("Zeynep");
80        System.out.println("Olusturulan kullanici: " + k);
81
82        // Stream ile toplu nesne üretimi
83        List<Kullanici> kullanicilar = Arrays.asList("Ali", "Veli", "Deli").stream()
84                .map(Kullanici::new)    // her isim için yeni Kullanici
85                .collect(Collectors.toList());
86        System.out.println("Toplu uretim: " + kullanicilar);
87
88        // İki argümanlı constructor referansı (BiFunction)
89        BiFunction<String, Integer, Kullanici> yasliKullanici = Kullanici::new;
90        System.out.println("Yasli kullanici: " + yasliKullanici.apply("Hasan", 65));
91
92        // Supplier ile parametresiz constructor
93        Supplier<StringBuilder> sbUret = StringBuilder::new;
94        System.out.println("Bos StringBuilder uretildi, uzunluk: " + sbUret.get().length());
95
96        System.out.println("\n=== GERÇEK HAYAT: Çalışanları metot referanslarıyla işleme ===\n");
97        List<Calisan> calisanlar = Arrays.asList(
98                new Calisan("Mehmet", 42000),
99                new Calisan("Ayse", 55000),
100                new Calisan("Burak", 38000));
101
102        // Comparator.comparing + method reference
103        calisanlar.stream()
104                .sorted(Comparator.comparing(Calisan::getMaas))  // TÜR 3 referans
105                .map(Calisan::getIsim)                            // TÜR 3 referans
106                .forEach(System.out::println);                    // TÜR 2 referans
107    }
108
109    /** TÜR 1 örneği için static metot. */
110    static String selamVer(String isim) {
111        return "Merhaba " + isim + "!";
112    }
113
114    static class Kullanici {
115        String ad;
116        int yas;
117
118        Kullanici(String ad) {
119            this.ad = ad;
120            this.yas = 0;
121        }
122
123        Kullanici(String ad, int yas) {
124            this.ad = ad;
125            this.yas = yas;
126        }
127
128        @Override
129        public String toString() {
130            return ad + (yas > 0 ? "(" + yas + ")" : "");
131        }
132    }
133
134    static class Calisan {
135        private String isim;
136        private double maas;
137
138        Calisan(String isim, double maas) {
139            this.isim = isim;
140            this.maas = maas;
141        }
142
143        String getIsim() { return isim; }
144        double getMaas() { return maas; }
145    }
146}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

NashornOrnekleri

çalıştırılabilir
NashornOrnekleri.java
1import javax.script.Invocable;
2import javax.script.ScriptEngine;
3import javax.script.ScriptEngineManager;
4
5/**
6 * ============================================================================
7 *  NASHORN JAVASCRIPT ENGINE - Java 8
8 * ============================================================================
9 *
10 * NEDİR?
11 *   Nashorn, Java 8 ile gelen, JVM üzerinde JavaScript kodu çalıştıran yüksek
12 *   performanslı bir motordur. Java'dan JavaScript çağırabilir, JavaScript'ten
13 *   Java nesnelerine erişebilirsin. javax.script (JSR-223) API'si üzerinden
14 *   kullanılır.
15 *
16 * NEDEN GELDİ?
17 *   Java 6'daki eski "Rhino" JavaScript motoru yavaş ve eskimişti. Nashorn,
18 *   modern invokedynamic bytecode'unu kullanarak çok daha hızlı çalışır.
19 *   Uygulamalara gömülü (embedded) scriptleme yeteneği kazandırmak için geldi:
20 *   kullanıcıların iş kurallarını JavaScript ile tanımlaması, dinamik
21 *   yapılandırma, kural motorları gibi senaryolar.
22 *
23 * NE İŞE YARAR: Java uygulamasını yeniden derlemeden dinamik script
24 *   çalıştırma; kural motorları; şablon işleme; komut satırı (jjs aracı).
25 *
26 * ÖNEMLİ NOT (Geleceği):
27 *   Nashorn, Java 11'de DEPRECATED (kullanımdan kaldırılması planlandı)
28 *   edildi ve Java 15'te TAMAMEN KALDIRILDI. Bu yüzden modern projelerde
29 *   GraalVM JavaScript gibi alternatifler tercih edilir. Bu örnek tarihsel
30 *   ve Java 8 bağlamında öğretici amaçlıdır.
31 *
32 * DİKKAT: getEngineByName("nashorn") Java 8-14 arasında çalışır. Daha yeni
33 *   JDK'larda engine null dönebilir; kod bu durumu nazikçe ele alır.
34 */
35public class NashornOrnekleri {
36
37    public static void main(String[] args) throws Exception {
38
39        ScriptEngineManager manager = new ScriptEngineManager();
40        ScriptEngine motor = manager.getEngineByName("nashorn");
41
42        if (motor == null) {
43            System.out.println("Nashorn motoru bu JDK surumunde mevcut DEGIL.");
44            System.out.println("(Nashorn Java 15'te kaldirildi. Java 8-14 ile calistirin.)");
45            return;
46        }
47
48        System.out.println("=== 1. Basit JavaScript çalıştırma ===\n");
49        motor.eval("print('JavaScript icinden merhaba!')");
50        Object sonuc = motor.eval("10 + 20 * 2");
51        System.out.println("JS ifadesinin sonucu: " + sonuc);
52
53        System.out.println("\n=== 2. Java'dan JS'e değişken geçirme ===\n");
54        motor.put("isim", "Ahmet");
55        motor.put("yas", 30);
56        motor.eval("print('Ad: ' + isim + ', Yas: ' + yas)");
57
58        System.out.println("\n=== 3. JS fonksiyonu tanımlayıp Java'dan çağırma ===\n");
59        motor.eval("function topla(a, b) { return a + b; }");
60        Invocable cagrilabilir = (Invocable) motor;
61        Object toplam = cagrilabilir.invokeFunction("topla", 15, 27);
62        System.out.println("JS topla(15, 27) = " + toplam);
63
64        System.out.println("\n=== 4. GERÇEK HAYAT: Dinamik iş kuralı (kural motoru) ===\n");
65        // Senaryo: Indirim kurali JavaScript ile tanimlanir; Java kodu
66        // yeniden derlenmeden kural degistirilebilir.
67        String indirimKurali =
68                "function indirim(tutar) {" +
69                "  if (tutar > 1000) return tutar * 0.85;" + // %15 indirim
70                "  else if (tutar > 500) return tutar * 0.95;" + // %5 indirim
71                "  else return tutar;" +
72                "}";
73        motor.eval(indirimKurali);
74
75        double[] sepetler = {300, 700, 1500};
76        for (double sepet : sepetler) {
77            Object indirimli = cagrilabilir.invokeFunction("indirim", sepet);
78            System.out.printf("Sepet %.0f TL -> indirimli %.2f TL%n", sepet,
79                    ((Number) indirimli).doubleValue());
80        }
81
82        System.out.println("\n=== 5. JS icinden Java sınıfına erişim ===\n");
83        // Nashorn, Java.type ile Java siniflarini kullanabilir
84        motor.eval(
85                "var ArrayList = Java.type('java.util.ArrayList');" +
86                "var liste = new ArrayList();" +
87                "liste.add('Elma'); liste.add('Armut');" +
88                "print('JS icinde olusturulan Java listesi: ' + liste);");
89    }
90}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

OptionalOrnekleri

çalıştırılabilir
OptionalOrnekleri.java
1import java.util.Arrays;
2import java.util.List;
3import java.util.Optional;
4
5/**
6 * ============================================================================
7 *  OPTIONAL SINIFI - Java 8
8 * ============================================================================
9 *
10 * NEDİR?
11 *   Optional<T>, "bir değer olabilir VEYA olmayabilir" durumunu temsil eden
12 *   bir KAPSAYICIDIR (container). İçinde ya bir değer vardır ya da boştur.
13 *   Amaç: null yerine, "değer yok" durumunu TİP SEVİYESİNDE açıkça belirtmek.
14 *
15 * NEDEN GELDİ? (Hangi problemi çözdü?)
16 *   NullPointerException (NPE), Java'nın en sık görülen hatasıdır. Bir metot
17 *   null döndürdüğünde, çağıran taraf bunu kontrol etmeyi unutursa program
18 *   çöker. Tony Hoare null'ı "milyar dolarlık hata" diye adlandırmıştır.
19 *   Optional, bir metodun "değer döndürmeyebilirim" demesini imza seviyesinde
20 *   zorunlu kılar; böylece derleme zamanında dikkatli olmaya iter.
21 *
22 * NE İŞE YARAR: NPE riskini azaltır, kodun niyetini netleştirir, varsayılan
23 *   değer / alternatif akış yönetimini kolaylaştırır.
24 *
25 * DİKKAT: Optional alan (field) olarak veya metot parametresi olarak
26 *   kullanılması TAVSİYE EDİLMEZ. Esasen DÖNÜŞ TİPİ olarak tasarlanmıştır.
27 */
28public class OptionalOrnekleri {
29
30    static List<Kullanici> kullanicilar = Arrays.asList(
31            new Kullanici(1, "Ahmet", "ahmet@mail.com"),
32            new Kullanici(2, "Zeynep", null),          // e-postası yok
33            new Kullanici(3, "Mehmet", "mehmet@mail.com"));
34
35    public static void main(String[] args) {
36
37        System.out.println("=== 1. ESKİ YÖNTEM: null kontrolü ===\n");
38        // ESKİ YÖNTEM: metot null döndürür, çağıran null kontrolü yapmak zorunda
39        Kullanici k = kullaniciBulEski(2);
40        if (k != null) {                       // unutulursa -> NPE
41            if (k.email != null) {             // iç içe null kontrolleri
42                System.out.println("Email: " + k.email.toUpperCase());
43            } else {
44                System.out.println("Email yok (eski yontem)");
45            }
46        }
47
48        System.out.println("\n=== 2. YENİ YÖNTEM: Optional ile ===\n");
49        Optional<Kullanici> opt = kullaniciBul(2);
50        // isPresent / get (eski tarz, tavsiye edilmez ama mümkün)
51        if (opt.isPresent()) {
52            System.out.println("Bulundu: " + opt.get().isim);
53        }
54
55        System.out.println("\n=== 3. ifPresent : Değer varsa çalıştır ===\n");
56        kullaniciBul(1).ifPresent(u -> System.out.println("ifPresent -> " + u.isim));
57        kullaniciBul(99).ifPresent(u -> System.out.println("Bu satir CALISMAZ"));
58        System.out.println("99 numarali kullanici icin ifPresent calismadi (deger yok).");
59
60        System.out.println("\n=== 4. orElse / orElseGet / orElseThrow ===\n");
61        // orElse: değer yoksa varsayılan döndür
62        Kullanici varsayilan = kullaniciBul(99)
63                .orElse(new Kullanici(0, "Misafir", "yok@mail.com"));
64        System.out.println("orElse -> " + varsayilan.isim);
65
66        // orElseGet: varsayılanı LAZY üret (yalnızca gerekirse hesaplanır)
67        Kullanici lazy = kullaniciBul(99)
68                .orElseGet(() -> {
69                    System.out.println("   (pahali varsayilan uretiliyor...)");
70                    return new Kullanici(0, "Uretildi", null);
71                });
72        System.out.println("orElseGet -> " + lazy.isim);
73
74        // orElseThrow: değer yoksa istisna fırlat
75        try {
76            kullaniciBul(99).orElseThrow(() ->
77                    new RuntimeException("Kullanici bulunamadi!"));
78        } catch (RuntimeException e) {
79            System.out.println("orElseThrow -> " + e.getMessage());
80        }
81
82        System.out.println("\n=== 5. map / flatMap : Zincirleme dönüşüm ===\n");
83        // E-postayı güvenle büyük harfe çevir; yoksa varsayılan yaz
84        String email = kullaniciBul(1)
85                .map(u -> u.email)             // Optional<String>
86                .map(String::toUpperCase)      // değer varsa dönüştür
87                .orElse("EMAIL YOK");
88        System.out.println("Kullanici 1 email: " + email);
89
90        // E-postası olmayan kullanıcı (Zeynep) -> zincir güvenle "EMAIL YOK"
91        String email2 = kullaniciBul(2)
92                .map(u -> u.email)
93                .map(String::toUpperCase)
94                .orElse("EMAIL YOK");
95        System.out.println("Kullanici 2 email: " + email2 + "  (null guvenle ele alindi)");
96
97        System.out.println("\n=== 6. filter ile koşullu Optional ===\n");
98        // Sadece mail.com uzantılı e-postaları kabul et
99        String gecerli = kullaniciBul(3)
100                .map(u -> u.email)
101                .filter(e -> e != null && e.endsWith("mail.com"))
102                .orElse("GECERSIZ");
103        System.out.println("Gecerli email kontrolu: " + gecerli);
104
105        System.out.println("\n=== 7. Optional Oluşturma Yöntemleri ===\n");
106        Optional<String> dolu = Optional.of("deger");          // null OLAMAZ
107        Optional<String> bos = Optional.empty();               // bilerek boş
108        Optional<String> belkiNull = Optional.ofNullable(null);// null olabilir
109        System.out.println("of()         -> " + dolu);
110        System.out.println("empty()      -> " + bos);
111        System.out.println("ofNullable() -> " + belkiNull);
112
113        System.out.println("\n=== 8. GERÇEK HAYAT: Banka hesap bakiyesi ===\n");
114        // Hesap bulunamazsa 0 bakiye varsay, varsa bakiyeyi formatla
115        double bakiye = hesapBul("TR0001")
116                .map(h -> h.bakiye)
117                .orElse(0.0);
118        System.out.printf("TR0001 bakiye: %.2f TL%n", bakiye);
119
120        double bakiye2 = hesapBul("YOK")
121                .map(h -> h.bakiye)
122                .orElse(0.0);
123        System.out.printf("YOK   bakiye: %.2f TL (hesap yok, guvenli varsayilan)%n", bakiye2);
124    }
125
126    /** ESKİ tarz: null döndürebilen metot (riskli). */
127    static Kullanici kullaniciBulEski(int id) {
128        for (Kullanici u : kullanicilar) {
129            if (u.id == id) return u;
130        }
131        return null; // çağıran null kontrolü yapmazsa NPE riski
132    }
133
134    /** YENİ tarz: Optional döndüren metot (güvenli, niyet açık). */
135    static Optional<Kullanici> kullaniciBul(int id) {
136        return kullanicilar.stream()
137                .filter(u -> u.id == id)
138                .findFirst();   // Stream zaten Optional döndürür
139    }
140
141    static Optional<Hesap> hesapBul(String iban) {
142        if (iban.equals("TR0001")) {
143            return Optional.of(new Hesap("TR0001", 12500.75));
144        }
145        return Optional.empty();
146    }
147
148    static class Kullanici {
149        int id;
150        String isim;
151        String email; // null olabilir
152
153        Kullanici(int id, String isim, String email) {
154            this.id = id;
155            this.isim = isim;
156            this.email = email;
157        }
158    }
159
160    static class Hesap {
161        String iban;
162        double bakiye;
163
164        Hesap(String iban, double bakiye) {
165            this.iban = iban;
166            this.bakiye = bakiye;
167        }
168    }
169}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

StreamApiOrnekleri

çalıştırılabilir
StreamApiOrnekleri.java
1import java.util.ArrayList;
2import java.util.Arrays;
3import java.util.IntSummaryStatistics;
4import java.util.List;
5import java.util.Optional;
6import java.util.stream.Collectors;
7import java.util.stream.IntStream;
8import java.util.stream.Stream;
9
10/**
11 * ============================================================================
12 *  STREAM API - Java 8
13 * ============================================================================
14 *
15 * NEDİR?
16 *   Koleksiyonlar (List, Set vb.) üzerinde DEKLARATİF (ne istediğini söyle,
17 *   nasıl yapılacağını değil) tarzda işlem yapmayı sağlayan bir API'dir.
18 *   Bir "veri akışı" boru hattı kurarsın: kaynak -> ara işlemler -> sonlandırıcı.
19 *
20 *   Yapısı:
21 *     kaynak.stream()
22 *           .filter(...)   // ara işlem (lazy)
23 *           .map(...)      // ara işlem (lazy)
24 *           .collect(...)  // sonlandırıcı işlem (terminal) -> akışı tetikler
25 *
26 * NEDEN GELDİ? (Hangi problemi çözdü?)
27 *   Eskiden koleksiyon işlemek için elle for döngüleri yazıyorduk. Filtrele +
28 *   dönüştür + topla gibi işlemler iç içe döngüler ve geçici listelerle dolu,
29 *   hata yapmaya açık, okunması zor kodlar üretiyordu. Stream API bu işlemleri
30 *   tek bir akıcı (fluent) zincire indirger.
31 *
32 * ÖNEMLİ KAVRAMLAR:
33 *   - LAZY (tembel): Ara işlemler, terminal işlem çağrılana kadar çalışmaz.
34 *   - Stream tek kullanımlıktır: Tüketildikten sonra tekrar kullanılamaz.
35 *   - PARALEL stream: .parallelStream() ile işlemler çoklu çekirdekte dağıtılır.
36 *
37 * NE İŞE YARAR / NEREDE KOLAYLIK: Filtreleme, dönüştürme, gruplama, toplama,
38 *   istatistik çıkarma, raporlama -> e-ticaret, banka, log analizi vb.
39 */
40public class StreamApiOrnekleri {
41
42    public static void main(String[] args) {
43
44        List<Urun> urunler = Arrays.asList(
45                new Urun("Telefon", "Elektronik", 15000, 12),
46                new Urun("Laptop", "Elektronik", 32000, 5),
47                new Urun("Kulaklik", "Elektronik", 800, 40),
48                new Urun("Kitap", "Yayin", 120, 200),
49                new Urun("Defter", "Yayin", 35, 500),
50                new Urun("Kahve", "Gida", 250, 80));
51
52        System.out.println("=== 1. ESKİ YÖNTEM vs filter+map+collect ===\n");
53
54        // ESKİ YÖNTEM (Java 7): elle döngü + geçici liste
55        List<String> pahaliElektronikEski = new ArrayList<>();
56        for (Urun u : urunler) {
57            if (u.kategori.equals("Elektronik") && u.fiyat > 1000) {
58                pahaliElektronikEski.add(u.ad.toUpperCase());
59            }
60        }
61        System.out.println("Eski yontem : " + pahaliElektronikEski);
62
63        // YENİ YÖNTEM (Java 8 Stream): okunabilir, deklaratif
64        List<String> pahaliElektronikYeni = urunler.stream()
65                .filter(u -> u.kategori.equals("Elektronik"))
66                .filter(u -> u.fiyat > 1000)
67                .map(u -> u.ad.toUpperCase())
68                .collect(Collectors.toList());
69        System.out.println("Stream API  : " + pahaliElektronikYeni);
70
71        System.out.println("\n=== 2. map : Dönüştürme ===\n");
72        List<Double> kdvliFiyatlar = urunler.stream()
73                .map(u -> u.fiyat * 1.20)
74                .collect(Collectors.toList());
75        System.out.println("KDV'li fiyatlar: " + kdvliFiyatlar);
76
77        System.out.println("\n=== 3. reduce : İndirgeme (tek değere düşürme) ===\n");
78        // Tüm stoğun toplam değeri (fiyat * adet)
79        double toplamStokDegeri = urunler.stream()
80                .map(u -> u.fiyat * u.stokAdedi)
81                .reduce(0.0, Double::sum);   // başlangıç 0, ardışık topla
82        System.out.println("Toplam stok degeri: " + toplamStokDegeri + " TL");
83
84        // reduce ile en pahalı ürünü bulma
85        Optional<Urun> enPahali = urunler.stream()
86                .reduce((u1, u2) -> u1.fiyat > u2.fiyat ? u1 : u2);
87        enPahali.ifPresent(u -> System.out.println("En pahali urun: " + u.ad));
88
89        System.out.println("\n=== 4. flatMap : İç içe yapıları düzleştirme ===\n");
90        // Her siparişin birden fazla ürünü var; tüm ürünleri tek listede topla
91        List<Siparis> siparisler = Arrays.asList(
92                new Siparis("S1", Arrays.asList("Telefon", "Kulaklik")),
93                new Siparis("S2", Arrays.asList("Laptop")),
94                new Siparis("S3", Arrays.asList("Kitap", "Defter", "Kahve")));
95
96        List<String> tumUrunler = siparisler.stream()
97                .flatMap(s -> s.urunler.stream())   // List<List<String>> -> Stream<String>
98                .collect(Collectors.toList());
99        System.out.println("Tum siparis kalemleri: " + tumUrunler);
100
101        // Benzersiz ürünler
102        List<String> benzersiz = siparisler.stream()
103                .flatMap(s -> s.urunler.stream())
104                .distinct()
105                .sorted()
106                .collect(Collectors.toList());
107        System.out.println("Benzersiz (sirali)   : " + benzersiz);
108
109        System.out.println("\n=== 5. Sayısal Stream'ler ve İstatistik ===\n");
110        IntSummaryStatistics istatistik = urunler.stream()
111                .mapToInt(u -> u.stokAdedi)
112                .summaryStatistics();
113        System.out.println("Toplam stok adedi : " + istatistik.getSum());
114        System.out.println("Ortalama stok     : " + istatistik.getAverage());
115        System.out.println("En cok stok       : " + istatistik.getMax());
116        System.out.println("En az stok        : " + istatistik.getMin());
117
118        System.out.println("\n=== 6. limit / skip / sorted / count ===\n");
119        // En pahalı 3 ürün
120        System.out.println("En pahali 3 urun:");
121        urunler.stream()
122                .sorted((a, b) -> Double.compare(b.fiyat, a.fiyat))
123                .limit(3)
124                .forEach(u -> System.out.println("   " + u.ad + " - " + u.fiyat));
125
126        long elektronikSayisi = urunler.stream()
127                .filter(u -> u.kategori.equals("Elektronik"))
128                .count();
129        System.out.println("Elektronik urun sayisi: " + elektronikSayisi);
130
131        System.out.println("\n=== 7. anyMatch / allMatch / noneMatch ===\n");
132        boolean herseyStokta = urunler.stream().allMatch(u -> u.stokAdedi > 0);
133        boolean pahaliVar = urunler.stream().anyMatch(u -> u.fiyat > 30000);
134        boolean bedavaYok = urunler.stream().noneMatch(u -> u.fiyat == 0);
135        System.out.println("Hepsi stokta mi?  " + herseyStokta);
136        System.out.println("30000 ustu var mi? " + pahaliVar);
137        System.out.println("Bedava urun yok mu? " + bedavaYok);
138
139        System.out.println("\n=== 8. Stream.iterate / generate / IntStream.range ===\n");
140        // İlk 5 çift sayı
141        List<Integer> ciftler = Stream.iterate(0, n -> n + 2)
142                .limit(5)
143                .collect(Collectors.toList());
144        System.out.println("Ilk 5 cift sayi: " + ciftler);
145
146        // IntStream.range ile toplam
147        int kareToplam = IntStream.rangeClosed(1, 5)
148                .map(n -> n * n)
149                .sum();
150        System.out.println("1..5 karelerinin toplami: " + kareToplam);
151
152        System.out.println("\n=== 9. PARALEL STREAM (çoklu çekirdek) ===\n");
153        // Büyük veri için işlem çekirdeklere dağıtılır. DİKKAT: küçük veride
154        // ek yük (overhead) nedeniyle daha yavaş olabilir.
155        long buyukToplam = IntStream.rangeClosed(1, 1_000_000)
156                .parallel()
157                .mapToLong(i -> (long) i)
158                .sum();
159        System.out.println("1..1.000.000 paralel toplam: " + buyukToplam);
160        System.out.println("Kullanilan cekirdek sayisi : "
161                + Runtime.getRuntime().availableProcessors());
162    }
163
164    static class Urun {
165        String ad, kategori;
166        double fiyat;
167        int stokAdedi;
168
169        Urun(String ad, String kategori, double fiyat, int stokAdedi) {
170            this.ad = ad;
171            this.kategori = kategori;
172            this.fiyat = fiyat;
173            this.stokAdedi = stokAdedi;
174        }
175
176        @Override
177        public String toString() {
178            return ad + " [" + kategori + "] " + fiyat + " TL x" + stokAdedi;
179        }
180    }
181
182    static class Siparis {
183        String no;
184        List<String> urunler;
185
186        Siparis(String no, List<String> urunler) {
187            this.no = no;
188            this.urunler = urunler;
189        }
190    }
191}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

StringJoinerOrnekleri

çalıştırılabilir
StringJoinerOrnekleri.java
1import java.util.Arrays;
2import java.util.List;
3import java.util.StringJoiner;
4import java.util.stream.Collectors;
5
6/**
7 * ============================================================================
8 *  StringJoiner ve String.join - Java 8
9 * ============================================================================
10 *
11 * NEDİR?
12 *   - String.join(...)   : Statik bir yardımcı metot. Birden fazla parçayı
13 *                          belirli bir ayırıcıyla (delimiter) birleştirir.
14 *   - StringJoiner       : Ayırıcı (delimiter), önek (prefix) ve sonek (suffix)
15 *                          ile metin parçalarını biriktirip birleştiren sınıf.
16 *
17 * NEDEN GELDİ? (Hangi problemi çözdü?)
18 *   Bir listeyi "a, b, c" gibi virgülle birleştirmek çok yaygın bir ihtiyaçtı
19 *   ama Java'da standart bir yolu yoktu. Geliştiriciler genelde döngü içinde
20 *   StringBuilder kullanıp "SON elemana virgül koymama" mantığını elle
21 *   yazıyordu. Bu, tekrar eden ve hata yapmaya açık (sondaki fazladan virgül
22 *   gibi) bir koddu. Java 8 bunu standartlaştırdı.
23 *
24 * NE İŞE YARAR: CSV satırı üretme, SQL IN listesi, log mesajları, URL query
25 *   parametreleri, raporlarda "etiket listesi" gibi yerlerde.
26 */
27public class StringJoinerOrnekleri {
28
29    public static void main(String[] args) {
30
31        List<String> sehirler = Arrays.asList("Istanbul", "Ankara", "Izmir", "Bursa");
32
33        System.out.println("=== 1. ESKİ YÖNTEM: StringBuilder + manuel ayırıcı ===\n");
34        // ESKİ YÖNTEM: son virgülü engellemek için ekstra mantık gerekir
35        StringBuilder sb = new StringBuilder();
36        for (int i = 0; i < sehirler.size(); i++) {
37            sb.append(sehirler.get(i));
38            if (i < sehirler.size() - 1) {   // son elemana virgül KOYMA kontrolü
39                sb.append(", ");
40            }
41        }
42        System.out.println("StringBuilder ile: " + sb.toString());
43
44        System.out.println("\n=== 2. YENİ YÖNTEM: String.join ===\n");
45        // YENİ YÖNTEM: tek satır, hata riski yok
46        String birlesik = String.join(", ", sehirler);
47        System.out.println("String.join ile  : " + birlesik);
48        // Varargs ile de calisir
49        System.out.println("Varargs ile      : " + String.join(" - ", "A", "B", "C"));
50
51        System.out.println("\n=== 3. StringJoiner: ayırıcı + önek + sonek ===\n");
52        StringJoiner sj = new StringJoiner(", ", "[", "]");
53        sehirler.forEach(sj::add);
54        System.out.println("StringJoiner: " + sj.toString());
55
56        System.out.println("\n=== 4. Boş durumda 'emptyValue' ===\n");
57        StringJoiner bos = new StringJoiner(", ", "{", "}");
58        bos.setEmptyValue("(liste bos)");   // hic add edilmezse bu yazi cikar
59        System.out.println("Bos joiner: " + bos.toString());
60
61        System.out.println("\n=== 5. Stream + Collectors.joining (en pratiği) ===\n");
62        List<Calisan> calisanlar = Arrays.asList(
63                new Calisan("Ahmet", "Yazilim"),
64                new Calisan("Zeynep", "Pazarlama"),
65                new Calisan("Mehmet", "Finans"));
66
67        // İsimleri tek satırda birleştir
68        String isimSatiri = calisanlar.stream()
69                .map(c -> c.isim)
70                .collect(Collectors.joining(", ", "Calisanlar: ", "."));
71        System.out.println(isimSatiri);
72
73        System.out.println("\n=== 6. GERÇEK HAYAT: CSV satırı üretme ===\n");
74        for (Calisan c : calisanlar) {
75            String csvSatir = new StringJoiner(";")
76                    .add(c.isim)
77                    .add(c.departman)
78                    .toString();
79            System.out.println(csvSatir);
80        }
81
82        System.out.println("\n=== 7. GERÇEK HAYAT: SQL IN listesi ===\n");
83        List<Integer> idler = Arrays.asList(101, 102, 103, 205);
84        String inListesi = idler.stream()
85                .map(String::valueOf)
86                .collect(Collectors.joining(", ", "WHERE id IN (", ")"));
87        System.out.println(inListesi);
88
89        System.out.println("\n=== 8. GERÇEK HAYAT: URL query string ===\n");
90        StringJoiner query = new StringJoiner("&", "?", "");
91        query.add("kategori=elektronik");
92        query.add("siralama=fiyat");
93        query.add("sayfa=1");
94        System.out.println("https://magaza.com/urunler" + query);
95    }
96
97    static class Calisan {
98        String isim, departman;
99
100        Calisan(String isim, String departman) {
101            this.isim = isim;
102            this.departman = departman;
103        }
104    }
105}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

YeniTarihSaatApi

çalıştırılabilir
YeniTarihSaatApi.java
1import java.time.DayOfWeek;
2import java.time.Duration;
3import java.time.Instant;
4import java.time.LocalDate;
5import java.time.LocalDateTime;
6import java.time.LocalTime;
7import java.time.Month;
8import java.time.Period;
9import java.time.ZoneId;
10import java.time.ZonedDateTime;
11import java.time.format.DateTimeFormatter;
12import java.time.temporal.ChronoUnit;
13import java.util.Calendar;
14import java.util.Date;
15
16/**
17 * ============================================================================
18 *  YENİ TARİH/SAAT API (java.time) - Java 8
19 * ============================================================================
20 *
21 * NEDİR?
22 *   java.time paketi; LocalDate, LocalTime, LocalDateTime, ZonedDateTime,
23 *   Duration, Period, Instant gibi MODERN tarih/saat sınıfları getirir.
24 *   (JSR-310, Joda-Time kütüphanesinden ilham alınarak tasarlandı.)
25 *
26 * NEDEN GELDİ? (Eski Date/Calendar'ın sorunları neydi?)
27 *   1) DEĞİŞTİRİLEBİLİR (mutable): java.util.Date nesnesi sonradan değişebilir
28 *      -> çoklu iş parçacığında (thread) güvensiz, beklenmedik hatalar.
29 *   2) KAFA KARIŞTIRAN API: Calendar'da AY 0'DAN BAŞLAR (Ocak = 0!). Yıl
30 *      1900 tabanlıydı. Çok sayıda hataya yol açtı.
31 *   3) Zaman dilimi (timezone) yönetimi zayıf ve hataya açıktı.
32 *   4) Tarih aritmetiği (3 gün ekle, iki tarih farkı) çok zahmetliydi.
33 *
34 *   java.time bunların hepsini çözer: DEĞİŞMEZ (immutable), thread-safe,
35 *   akıcı (fluent) ve insan-okunabilir API.
36 *
37 * NE İŞE YARAR: Doğum günü hesaplama, fatura kesim tarihleri, rezervasyon
38 *   süreleri, zaman dilimi dönüşümleri, yaş hesaplama vb.
39 */
40public class YeniTarihSaatApi {
41
42    public static void main(String[] args) {
43
44        System.out.println("=== 1. ESKİ Date/Calendar SORUNLARI ===\n");
45        // ESKİ YÖNTEM: 24 Haziran 2026 olusturmak istiyoruz
46        Calendar cal = Calendar.getInstance();
47        cal.set(2026, 5, 24); // DİKKAT: ay 5 = HAZIRAN (0'dan basliyor!) -> kafa karistirici
48        Date eskiTarih = cal.getTime();
49        System.out.println("Eski Calendar (ay 0-tabanli!): " + eskiTarih);
50        System.out.println("   -> 'cal.set(2026, 5, 24)' aslinda HAZIRAN demek, MAYIS degil!");
51
52        System.out.println("\n=== 2. YENİ YÖNTEM: LocalDate ===\n");
53        // YENİ YÖNTEM: ay numaralari INSAN MANTIGIYLA (Haziran = 6)
54        LocalDate bugun = LocalDate.of(2026, 6, 24);
55        System.out.println("LocalDate.of(2026, 6, 24): " + bugun + "  (Haziran = 6, net!)");
56        System.out.println("Su anki tarih (now)      : " + LocalDate.now());
57        System.out.println("Ay        : " + bugun.getMonth() + " (" + bugun.getMonthValue() + ")");
58        System.out.println("Haftanin gunu: " + bugun.getDayOfWeek());
59        System.out.println("Yilin kacinci gunu: " + bugun.getDayOfYear());
60        System.out.println("Arti gecmis mi (leap year)? " + bugun.isLeapYear());
61
62        System.out.println("\n=== 3. Tarih Aritmetiği (immutable - yeni nesne döner) ===\n");
63        LocalDate gelecek = bugun.plusDays(10).plusMonths(2).minusYears(1);
64        System.out.println("bugun + 10 gun + 2 ay - 1 yil = " + gelecek);
65        System.out.println("Orijinal degismedi (immutable): " + bugun);
66
67        System.out.println("\n=== 4. LocalTime ve LocalDateTime ===\n");
68        LocalTime saat = LocalTime.of(14, 30, 0);
69        System.out.println("Saat: " + saat);
70        LocalDateTime randevu = LocalDateTime.of(2026, Month.JULY, 1, 9, 15);
71        System.out.println("Randevu: " + randevu);
72
73        System.out.println("\n=== 5. Period : İki TARİH arası fark (yıl/ay/gün) ===\n");
74        LocalDate dogumGunu = LocalDate.of(1990, 3, 15);
75        Period yas = Period.between(dogumGunu, bugun);
76        System.out.printf("Yas: %d yil, %d ay, %d gun%n",
77                yas.getYears(), yas.getMonths(), yas.getDays());
78        long toplamGun = ChronoUnit.DAYS.between(dogumGunu, bugun);
79        System.out.println("Dogumdan bu yana toplam gun: " + toplamGun);
80
81        System.out.println("\n=== 6. Duration : İki ZAMAN arası fark (saat/dakika/sn) ===\n");
82        LocalTime baslangic = LocalTime.of(9, 0);
83        LocalTime bitis = LocalTime.of(17, 30);
84        Duration mesai = Duration.between(baslangic, bitis);
85        System.out.println("Mesai suresi: " + mesai.toHours() + " saat "
86                + (mesai.toMinutes() % 60) + " dakika");
87
88        System.out.println("\n=== 7. ZonedDateTime : Zaman Dilimi Yönetimi ===\n");
89        ZonedDateTime istanbul = ZonedDateTime.now(ZoneId.of("Europe/Istanbul"));
90        ZonedDateTime newYork = istanbul.withZoneSameInstant(ZoneId.of("America/New_York"));
91        ZonedDateTime tokyo = istanbul.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
92        System.out.println("Istanbul: " + istanbul);
93        System.out.println("New York: " + newYork);
94        System.out.println("Tokyo   : " + tokyo);
95
96        System.out.println("\n=== 8. Formatlama (DateTimeFormatter) ===\n");
97        DateTimeFormatter trFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
98        System.out.println("Formatli: " + randevu.format(trFormat));
99        // Metinden tarihe çevirme (parse)
100        LocalDate parsed = LocalDate.parse("2026-12-31");
101        System.out.println("Parse edildi: " + parsed);
102
103        System.out.println("\n=== 9. Instant : Makine zamanı (epoch) ===\n");
104        Instant simdi = Instant.now();
105        System.out.println("Instant (UTC): " + simdi);
106        System.out.println("Epoch saniye : " + simdi.getEpochSecond());
107
108        System.out.println("\n=== 10. GERÇEK HAYAT: Fatura kesim ve vade ===\n");
109        LocalDate faturaTarihi = LocalDate.of(2026, 6, 1);
110        LocalDate sonOdeme = faturaTarihi.plusDays(30);
111        System.out.println("Fatura tarihi : " + faturaTarihi);
112        System.out.println("Son odeme     : " + sonOdeme);
113        long kalanGun = ChronoUnit.DAYS.between(bugun, sonOdeme);
114        System.out.println("Bugune gore kalan gun: " + kalanGun);
115
116        // Bir sonraki Pazartesi (TemporalAdjuster mantığı)
117        LocalDate sonrakiPazartesi = bugun;
118        do {
119            sonrakiPazartesi = sonrakiPazartesi.plusDays(1);
120        } while (sonrakiPazartesi.getDayOfWeek() != DayOfWeek.MONDAY);
121        System.out.println("Bir sonraki Pazartesi: " + sonrakiPazartesi);
122    }
123}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.