Java Programlama
Java programlama dilini sözdizimi, türler, kontrol akışı, metotlar, nesne yönelimli programlama, diziler, istisnalar ve dosya G/Ç’den generics, koleksiyonlar, eşzamanlılık, JVM, JDBC ve üretim davranışına kadar örnek kodlarla ele alan kapsamlı ders notu.
Java, kaynak kodun doğrudan belirli bir işlemci mimarisine bağlanmasından çok, dil belirtimi ile sanal makine modelinin ayrıldığı bir çalışma düzeni sunar. Bu ayrım taşınabilirliğin temelidir; ancak “bir kez yaz, her yerde çalıştır” ifadesi donanım, işletim sistemi, yerel kütüphane, karakter kodlaması ve dış servis bağımlılıklarının ortadan kalktığı anlamına gelmez. Sağlam bir Java programı için dil kurallarını, Java Sanal Makinesi'ni (JVM), standart kütüphaneyi ve çalışma zamanı davranışını birlikte değerlendirmek gerekir.
Java programlama; problem çözme, veri türleri, giriş/çıkış, denetim akışı, metot, sınıf, dizi, kalıtım, istisna ve dosya G/Ç temellerinden JVM, koleksiyonlar, genel türler, eşzamanlılık, veri erişimi ve üretim davranışına uzanan tek bir bütün olarak ele alınmalıdır. Böylece amaç yalnız sözdizimini ezberlemek değil, kaynak koddan çalışma zamanına kadar davranışın nedenini açıklayabilmektir.
Ünite 1: Derleme, Bytecode ve JVM
.java kaynak dosyası javac ile derlendiğinde platformdan bağımsız JVM komutlarını taşıyan .class dosyaları oluşur. JVM sınıf dosyasını yükler, doğrular, bağlar ve çalıştırır. Sık yürütülen kod bölgeleri çalışma sırasında JIT derleyicisi tarafından yerel makine koduna çevrilebilir. Bu nedenle Java ne yalnız yorumlanan ne de yalnız geleneksel anlamda önceden yerel koda derlenen bir dil olarak sınıflandırılmalıdır.
JDK geliştirme araçlarını, derleyiciyi ve çalışma zamanını kapsar. Güncel Java dağıtımlarında eski “ayrı JRE kurulumu” yaklaşımı pratikte merkezi değildir; uygulama çalışma zamanı jlink gibi araçlarla da özelleştirilebilir. Sürüm bağımlı özellikler kullanılırken kaynak sürümü, hedef çalışma zamanı ve kullanılan kütüphaneler birlikte yönetilmelidir.
15 Eylül 2026 itibarıyla JDK 27 en yeni özellik sürümü, JDK 25 ise güncel LTS çizgisidir. Üretim için sürüm seçimi yalnız “en yeni” olmasına göre değil destek ömrü, bağımlılık uyumluluğu, güvenlik güncellemeleri ve kurumun yükseltme politikasına göre yapılmalıdır.
JVM çalışma zamanı veri alanlarının temel ayrımı şunlardır:
- Her iş parçacığına ait program sayacı ve JVM yığını,
- Nesnelerin yer aldığı heap,
- Sınıf üst verisı ve çalışma zamanı sabit havuzu,
- Yerel yöntemler için gerekli çalışma zamanı yapıları.
Java dilindeki yerel değişken, nesne ve referans kavramlarını fiziksel RAM adresleriyle bire bir özdeşleştirmek hatalıdır. JVM gerçekleştirmesi kaçış analizi, scalar replacement veya başka optimizasyonlarla kaynak kodda görülen nesne yaşamını farklı biçimde gerçekleştirebilir.
İlk program, derleme ve çalıştırma
Java programının yürütme giriş noktası public static void main(String[] args) metodudur. main özel bir JVM talimatı değil, uygulama başlatıcısının belirli imzayla aradığı static metottur. Java büyük/küçük harfe duyarlıdır; toplam, Toplam ve TOPLAM farklı tanımlayıcılardır.
public class MerhabaJava {
public static void main(String[] args) {
System.out.println("Merhaba Java");
}
}Kaynak dosyanın adı MerhabaJava.java ise temel komut zinciri şöyledir:
javac MerhabaJava.java
java MerhabaJavaimport, C/C++ anlamında bir önişlemci direktifi değildir. Başka paketlerdeki türlerin adlarını kaynak kodda nitelemesiz kullanabilmek için derleme zamanı ad çözümlemesine katılır. java.lang paketindeki türler örtük olarak kullanılabilir; diğer paketler için tam nitelikli ad veya import gerekir. Wildcard import (java.util.*) alt paketleri içeri almaz.
Açıklama satırları //, /* ... */ ve API belgelemesi için /** ... */ biçimlerinde yazılabilir. Kaynak biçimlendirme çalışma zamanının semantiğini değiştirmez; ancak blok yapısını görünür kılan tutarlı girintileme bakım maliyetini doğrudan etkiler.
Ünite 2: Tür Sistemi, Değişkenler ve İfadeler
Java statik ve güçlü tür denetimi uygular. Temel türler byte, short, int, long, float, double, char ve booleandır. Bunların nesne karşılıkları sarmalayıcı sınıflardır; autoboxing kullanım kolaylığı sağlasa da ek nesne üretimi, null davranışı ve karşılaştırma semantiği göz ardı edilmemelidir.
Java'da tüm argüman aktarımı değer ile yapılır. Bir nesne “referansla geçirilmez”; nesneyi gösteren referansın değeri kopyalanır. Metot bu referans üzerinden nesnenin durumunu değiştirebilir, fakat çağıranın referans değişkenini başka bir nesneye bağlayamaz.
Sayısal dönüşümlerde genişletici dönüşümler çoğunlukla örtük, daraltıcı dönüşümler açık cast gerektirir. Tam sayı taşması varsayılan olarak istisna üretmez; kritik hesaplarda Math.addExact, Math.multiplyExact gibi denetimli işlemler veya uygun yüksek kesinlikli türler kullanılabilir. Parasal hesaplarda ikili kayan nokta yerine gereken durumda BigDecimal tercih edilir; BigDecimal ölçeği ve yuvarlama kuralı da iş sözleşmesinin parçasıdır.
String değişmezdir. Birleştirme işlemlerinin derleyici ve çalışma zamanı tarafından nasıl optimize edildiği bağlama göre değişse de döngü içinde çok sayıda dinamik birleştirme için StringBuilder daha öngörülebilir bir seçimdir. == referans kimliğini, equals ise sınıfın tanımladığı mantıksal eşitliği sınar.
Değişkenler, sabitler, operatörler ve dönüşümler
Bir değişken kullanılmadan önce türüyle tanımlanır. Yerel değişkenler otomatik olarak başlangıç değeri almaz; kesin atama kuralları derleyici tarafından denetlenir. Nesne alanları ise türlerinin varsayılan değerleriyle başlatılır. final bir değişkene yeniden atamayı sınırlar; nesnenin kendisini otomatik olarak değişmez yapmaz.
Aritmetik işlemlerde tam sayı bölmesi, taşma ve tür yükseltme kuralları önemlidir. 5 / 2 sonucu 2, 5.0 / 2 sonucu 2.5 olur. % kalan işlemini üretir. ++ ve -- işleçlerinin prefix/postfix farkı, ifadenin değerinin aynı deyimde kullanıldığı durumda görünür hale gelir; okunabilirlik açısından yan etkili karmaşık ifadelerden kaçınmak daha güvenlidir.
public class TurVeDonusum {
public static void main(String[] args) {
int adet = 7;
double birimFiyat = 12.50;
double toplam = adet * birimFiyat;
int yuvarlanmis = (int) Math.round(toplam);
int guvenliToplam = Math.addExact(1_500_000_000, 500_000_000);
System.out.println(toplam);
System.out.println(yuvarlanmis);
System.out.println(guvenliToplam);
}
}Konsol G/Ç ve Scanner
System.out.print satır sonu eklemez; System.out.println ekler. Konsoldan biçimlendirilmiş veri okumada Scanner öğretici ve pratik bir araçtır. nextInt() gibi token tabanlı okumalar ile nextLine() satır tabanlı okuma farklı davranır; ikisini birlikte kullanırken satır sonunun tamponda kalabileceği bilinmelidir.
import java.util.Scanner;
public class KonsolGiris {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
System.out.print("Ad: ");
String ad = scanner.nextLine().trim();
System.out.print("Birinci sayı: ");
double a = Double.parseDouble(scanner.nextLine());
System.out.print("İkinci sayı: ");
double b = Double.parseDouble(scanner.nextLine());
System.out.printf("%s için toplam = %.2f%n", ad, a + b);
}
}
}String: içerik ile kimliği ayırmak
String bir temel tür değil sınıftır ve değişmezdir. length, charAt, substring, indexOf, startsWith, endsWith, replace, trim/strip, toLowerCase ve toUpperCase gibi işlemler yeni sonuçlar üretir. Metin karşılaştırmasında içerik için equals/equalsIgnoreCase, sözlüksel sıralama için compareTo kullanılır; == referans kimliğini sınar.
public class MetinIsleme {
public static void main(String[] args) {
String ham = " Java Programlama ";
String temiz = ham.strip();
String kucuk = temiz.toLowerCase();
StringBuilder builder = new StringBuilder(32);
builder.append(kucuk).append(" | uzunluk=").append(temiz.length());
System.out.println(builder);
System.out.println("java programlama".equalsIgnoreCase(temiz));
}
}Ünite 3: Karar Yapıları ve Döngüler
Kontrol akışı bir algoritmanın hangi deyimleri hangi sırada çalıştıracağını belirler. if/else genel koşullar, switch ayrık değer kümeleri, koşullu işleç (?:) ise kısa değer seçimi için kullanılabilir. Modern switch expression, her dalın bir değer üretmesini sağlayarak bazı hata sınıflarını azaltır.
public class KararYapisi {
static String harfNotu(int puan) {
if (puan < 0 || puan > 100) {
throw new IllegalArgumentException("Puan 0..100 aralığında olmalı");
}
int grup = puan / 10;
return switch (grup) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> "C";
case 6 -> "D";
default -> "F";
};
}
public static void main(String[] args) {
System.out.println(harfNotu(86));
}
}while koşulu başta, do-while sonda denetler. En az bir kez çalışması gereken giriş döngülerinde do-while anlamlı olabilir. for başlangıç, koşul ve güncellemeyi aynı yapıda toplar; enhanced for diziler ve Iterable yapıları üzerinde indeks gerektirmeyen dolaşım sağlar. break döngüyü sonlandırır, continue bir sonraki yinelemeye geçer.
Sentinel değeriyle veri toplama, giriş sayısının önceden bilinmediği klasik bir akıştır:
import java.util.Scanner;
public class SentinelOrtalama {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
long toplam = 0;
int adet = 0;
while (true) {
int deger = scanner.nextInt();
if (deger == -999) {
break;
}
toplam += deger;
adet++;
}
if (adet > 0) {
System.out.println((double) toplam / adet);
}
}
}
}Rastgele sayı üretilirken test edilebilirlik önemlidir. Basit tek-iş parçacığı örneklerinde Random, eşzamanlı kodda ThreadLocalRandom, güvenlik amacıyla öngörülemez değer gerektiğinde SecureRandom farklı ihtiyaçlara karşılık gelir; Math.random() her kullanım için evrensel tercih değildir.
Ünite 4: Metotlar, static, Math ve Overloading
Metot bir davranışı adlandırır, parametrelerle veri alabilir ve void değilse sonuç döndürebilir. Java'da argümanlar daima değer ile aktarılır. Bir nesne parametresinde kopyalanan değer referanstır; bu nedenle metot nesnenin durumunu değiştirebilir fakat çağıranın değişkenini başka nesneye yeniden bağlayamaz.
public class MetotOrnegi {
static int maksimum(int a, int b) {
return a >= b ? a : b;
}
static double ortalama(int... degerler) {
if (degerler.length == 0) {
throw new IllegalArgumentException("En az bir değer gerekli");
}
long toplam = 0;
for (int deger : degerler) {
toplam += deger;
}
return (double) toplam / degerler.length;
}
public static void main(String[] args) {
System.out.println(maksimum(7, 11));
System.out.println(ortalama(10, 20, 30, 40));
}
}static üye sınıfa aittir; örnek üyeye erişmek için nesne gerekir. Math.sqrt, Math.pow, Math.abs, Math.min ve Math.max gibi yardımcılar statik metotlardır. Sarmalayıcı sınıflar (Integer, Long, Double, Character vb.) primitive değerleri nesne API'leriyle buluşturur. Integer.parseInt("42") gibi dönüşümler metin girdisini sayıya çevirmede yaygındır.
Overloading aynı metot adını farklı parametre listeleriyle kullanır ve derleme zamanında çözülür:
public class OverloadOrnegi {
static long alan(int kenar) {
return (long) kenar * kenar;
}
static long alan(int genislik, int yukseklik) {
return (long) genislik * yukseklik;
}
static double alan(double yaricap) {
return Math.PI * yaricap * yaricap;
}
public static void main(String[] args) {
System.out.println(alan(5));
System.out.println(alan(4, 7));
System.out.println(alan(2.5));
}
}Ünite 5: Sınıflar ve Nesne Yönelimli Programlama
Java sınıfları alan, metot, kurucu, iç içe tür ve başlatma blokları içerebilir. Nesne yönelimli tasarımın dört yaygın kavramı soyutlama, kapsülleme, kalıtım ve çok biçimliliktir; ancak iyi tasarım bunları mekanik olarak kullanmak değil, değişim sınırlarını doğru kurmaktır.
Erişim denetimi private, paket erişimi, protected ve public düzeyleriyle yapılır. En geniş erişimi varsayılan tercih olarak kullanmak kapsüllemeyi zayıflatır. Değişmez nesnelerde durum kurucuda doğrulanır, dışarıya değiştirilebilir iç temsil verilmez ve nesne yaşamı boyunca gözlenebilir durum değiştirilmez.
Metot overloading aynı adın farklı parametre listeleriyle derleme zamanında çözümlenmesidir. Overriding ise alt sınıfın üst sınıftan gelen örnek metodun davranışını değiştirmesidir ve çalışma zamanı çok biçimliliğinin temelidir. static, private ve kurucuların davranışı overriding ile karıştırılmamalıdır.
Soyut sınıf durum ve gerçekleşmiş davranış taşıyabilir. Arayüz bir sözleşme tanımlar; modern Java arayüzleri default, static ve özel yardımcı metotlar da içerebilir. Bir sınıf tek bir sınıftan kalıtım alırken birden çok arayüzü gerçekleştirebilir. Kalıtım yalnız “is-a” ilişkisi gerçekten korunuyorsa kullanılmalı; davranışın bileşimle kurulabildiği durumlarda composition çoğu zaman daha düşük bağımlılık üretir.
record veri taşıyan türlerde bileşenler, erişim metotları, equals, hashCode ve toString için standart bir model sağlar. enum yalnız sayısal sabit değildir; alan ve metot içerebilen sınıf benzeri bir türdür. Sealed sınıflar bir tür hiyerarşisinin hangi alt türlere izin verdiğini dil düzeyinde sınırlar.
Kapsülleme, kurucular ve this
Kaynak sunumdaki public/private, getter/setter ve kurucu örneklerinin kalıcı fikri şudur: sınıfın iç durumu doğrudan alan erişimiyle değil, sınıfın koruduğu invariants üzerinden yönetilmelidir. Setter yazmak kapsülleme için zorunlu değildir; değişmez veya kontrollü değişen sınıflarda yalnız gerekli operasyonların açılması daha güçlü bir sözleşme verir.
public class HesapOrnegi {
static final class BankaHesabi {
private final String iban;
private long bakiyeKurus;
BankaHesabi(String iban, long ilkBakiyeKurus) {
if (iban == null || iban.isBlank()) {
throw new IllegalArgumentException("IBAN boş olamaz");
}
if (ilkBakiyeKurus < 0) {
throw new IllegalArgumentException("Bakiye negatif olamaz");
}
this.iban = iban;
this.bakiyeKurus = ilkBakiyeKurus;
}
void yatir(long tutarKurus) {
if (tutarKurus <= 0) {
throw new IllegalArgumentException("Tutar pozitif olmalı");
}
bakiyeKurus = Math.addExact(bakiyeKurus, tutarKurus);
}
long bakiyeKurus() {
return bakiyeKurus;
}
String iban() {
return iban;
}
}
public static void main(String[] args) {
BankaHesabi hesap = new BankaHesabi("TR0001", 10_000);
hesap.yatir(2_500);
System.out.println(hesap.iban() + " " + hesap.bakiyeKurus());
}
}Bir sınıfta hiç kurucu yazılmazsa derleyici parametresiz varsayılan kurucuyu üretir. Programcı herhangi bir kurucu tanımladığında bu otomatik parametresiz kurucu artık üretilmez. this(...) aynı sınıftaki başka bir kurucuyu, super(...) üst sınıf kurucusunu çağırır ve kurucu gövdesinin ilk deyimi olmalıdır.
Kalıtım, soyut sınıf, arayüz ve çok biçimlilik
Kalıtımda extends, arayüz gerçekleştirmede implements kullanılır. Overriding sırasında parametre sözleşmesi korunur ve dinamik dispatch, çağrılacak örnek metodu çalışma zamanındaki gerçek nesne türüne göre seçer. @Override anotasyonu derleyicinin niyet denetimine yardım eder.
public class CokBicimlilikOrnegi {
interface EnerjiTuketir {
double tuketimKWh(double km);
}
static abstract class Tasit {
private final String marka;
Tasit(String marka) {
this.marka = marka;
}
String marka() {
return marka;
}
abstract String tur();
}
static final class ElektrikliOtomobil extends Tasit implements EnerjiTuketir {
ElektrikliOtomobil(String marka) {
super(marka);
}
@Override
String tur() {
return "Elektrikli otomobil";
}
@Override
public double tuketimKWh(double km) {
return km * 0.17;
}
}
public static void main(String[] args) {
Tasit tasit = new ElektrikliOtomobil("Örnek");
System.out.println(tasit.marka() + " - " + tasit.tur());
}
}Üst sınıfın private alanları alt sınıfa görünmez; erişim sınıfın sunduğu sözleşme üzerinden yapılmalıdır. final metot override edilemez, final sınıftan kalıtım alınamaz. Kalıtım hiyerarşisini yalnız kod tekrarını azaltmak için kurmak kırılgan tasarım üretir; substitutability korunmuyorsa composition tercih edilmelidir.
Ünite 6: Object Sözleşmesi, Eşitlik ve Kimlik
Object, sınıf hiyerarşisinin ortak temelidir. equals mantıksal eşitliği tanımlıyorsa hashCode ile sözleşmesi korunmalıdır: eşit iki nesnenin aynı hash kodunu üretmesi gerekir. Tersi zorunlu değildir; hash çakışmaları mümkündür.
Hash tabanlı koleksiyonda anahtar olarak kullanılan nesnenin eşitliği veya hash değeri koleksiyondayken değişirse kayıt erişilemezmiş gibi görünebilir. Bu nedenle anahtarların değişmez olması veya en azından eşitlikte kullanılan alanların değiştirilmemesi güvenli yaklaşımdır.
toString tanılama açısından yararlıdır fakat kalıcı veri biçimi olarak görülmemelidir. Parola, token, kimlik numarası ve kişisel veri gibi hassas alanlar otomatik toString, log veya hata mesajlarına taşınmamalıdır.
String değişmezdir. Çok sayıda ardışık metin birleştirmede StringBuilder tek iş parçacığı içinde değiştirilebilir bir tampon sağlar. StringBuffer benzer tarihsel API'yi senkronize metotlarla sunar; senkronizasyon gerekmeyen yeni kodda yalnız iş parçacığı-safe olduğu için otomatik tercih edilmemelidir.
equals, hashCode ve toString için somut örnek
== nesne kimliğiyle, equals ise tanımlanan mantıksal eşitlikle ilişkilidir. Üretim kodunda mantıksal eşitlik tanımlanıyorsa hashCode da aynı alanlarla uyumlu olmalıdır.
import java.util.Objects;
public final class Kesir {
private final int pay;
private final int payda;
public Kesir(int pay, int payda) {
if (payda == 0) {
throw new IllegalArgumentException("Payda sıfır olamaz");
}
int gcd = gcd(Math.abs(pay), Math.abs(payda));
int isaret = payda < 0 ? -1 : 1;
this.pay = isaret * pay / gcd;
this.payda = isaret * payda / gcd;
}
private static int gcd(int a, int b) {
while (b != 0) {
int t = a % b;
a = b;
b = t;
}
return a == 0 ? 1 : a;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Kesir other)) return false;
return pay == other.pay && payda == other.payda;
}
@Override
public int hashCode() {
return Objects.hash(pay, payda);
}
@Override
public String toString() {
return pay + "/" + payda;
}
}Ünite 7: İstisnalar ve Kaynak Yönetimi
Java istisnaları Throwable hiyerarşisi altında Error ve İstisna ana dallarına ayrılır. Checked istisna ile unchecked istisna arasındaki ayrım derleyicinin yakalama/bildirme zorunluluğuyla ilgilidir; “checked her zaman kurtarılabilir, unchecked her zaman programlama hatasıdır” gibi genellemeler güvenilir değildir.
İstisna sınırı katman sorumluluğuna göre seçilmelidir. Düşük seviyeli bir istisnayı anlamsız bir genel istisnaya çevirmek kök nedeni kaybettirir; buna karşılık alt sistem ayrıntısını dış API'ye sızdırmak da soyutlamayı bozar. Yeniden fırlatmada cause zincirinin korunması hata teşhisini kolaylaştırır.
Dosya, soket ve JDBC nesnelerinde try-with-resources, AutoCloseable kaynaklarını istisna olsa bile kapatır. finally hâlâ genel temizleme davranışı için kullanılabilir; ancak kaynak yönetiminde try-with-resources daha güvenlidir.
final, finally ve tarihsel finalize() kavramları farklıdır. finalize() güvenilir bir kaynak kapatma mekanizması değildir ve modern Java'da kullanımdan kaldırılmış yaklaşım olarak ele alınmalıdır. Kaynak yaşamı deterministik olarak kapatılmalıdır.
Sık karşılaşılan çalışma zamanı istisnalarının adları hatanın kök nedenini tarif eder: NullPointerİstisna null referans kullanımını, Arithmeticİstisna belirli aritmetik hataları, NumberFormatİstisna geçersiz metin-sayı dönüşümünü, ClassCastİstisna geçersiz tür dönüşümünü ve ArrayIndexOutOfBoundsİstisna dizi sınırı ihlalini gösterebilir. Interruptedİstisna ise yalnız “bir hata” değildir; iş parçacığı'in kesilme isteğini taşıdığı için iptal ve kapanış protokolünün bir parçası olarak ele alınmalıdır. Runtimeİstisna ailesini topluca yakalayıp yutmak bu ayrımı kaybettirir.
Özel istisna ve çoklu yakalama
İstisnalar yalnız beklenmeyen teknik hataları değil, API sözleşmesinin ihlalini de temsil edebilir. İstisna türü anlamlı seçilmeli ve mümkün olduğunca doğru soyutlama sınırında ele alınmalıdır.
public class IstisnaOrnegi {
static final class GecersizBolmeException extends Exception {
GecersizBolmeException(String mesaj) {
super(mesaj);
}
}
static int bol(int bolunen, int bolen) throws GecersizBolmeException {
if (bolen == 0) {
throw new GecersizBolmeException("Bölen sıfır olamaz");
}
return bolunen / bolen;
}
public static void main(String[] args) {
try {
System.out.println(bol(10, 2));
} catch (GecersizBolmeException e) {
System.err.println(e.getMessage());
}
}
}Bir try birden çok catch ile kullanılabilir; alt türler üst türlerden önce yakalanmalıdır. Multi-catch (catch (IOİstisna | SQLİstisna e)) ortak işlem uygulanacak ilgisiz istisna türlerinde tekrarları azaltabilir. İç içe try-catch yapısı mümkündür ancak kontrol akışını zorlaştırıyorsa metot sınırlarını yeniden tasarlamak daha temizdir.
Ünite 8: Genel türler ve Tür Güvenliği
Genel türler, koleksiyon ve API'lerde çalışma zamanı cast ihtiyacını azaltıp tür hatalarını derleme zamanına taşır. Java genel türler gerçekleştirmesi büyük ölçüde type erasure kullanır; bu nedenle List<String> ile List<Integer> çalışma zamanında ayrı reified türler değildir.
? extends T üretici gibi okunan yapılarda güvenli okuma, ? super T tüketici gibi kullanılan yapılarda güvenli yazma sağlar. “PECS: producer extends, consumer super” sezgisi yararlıdır; ancak API'nin gerçek varyans gereksinimi ayrıca değerlendirilmelidir.
Raw type kullanımı eski kodla uyumluluk dışında kaçınılması gereken bir tür güvenliği açığıdır. Generic varargs, unchecked cast ve reflection ile genel türler bir araya geldiğinde heap pollution riski ortaya çıkabilir.
Diziler ve çok boyutlu diziler
Dizi sabit uzunluklu, aynı bileşen türünden değerleri indeksle erişilebilir biçimde tutar. length alanı eleman sayısını verir. Bir dizi değişkeni nesne referansı taşıdığı için b = a elemanları kopyalamaz; iki değişkeni aynı diziye bağlar. Gerçek kopya için Arrays.copyOf, clone veya açık eleman kopyalama gerekir.
import java.util.Arrays;
public class DiziOrnegi {
static double ortalama(int[] degerler) {
if (degerler.length == 0) {
return Double.NaN;
}
long toplam = 0;
for (int deger : degerler) {
toplam += deger;
}
return (double) toplam / degerler.length;
}
public static void main(String[] args) {
int[] notlar = {72, 91, 64, 88, 79};
int[] kopya = Arrays.copyOf(notlar, notlar.length);
Arrays.sort(kopya);
int[][] matris = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println(ortalama(notlar));
System.out.println(Arrays.toString(kopya));
System.out.println(matris[1][2]);
}
}Java'da int[][] gibi çok boyutlu görünen diziler gerçekte dizi referansları dizisidir; satırların aynı uzunlukta olması zorunlu değildir. Bu özellik düzensiz (jagged) yapı kurmaya izin verir. Büyük sayısal matrislerde ise nesne/dizi düzeni, locality ve bellek maliyeti ayrıca değerlendirilmelidir.
Basit generic metot örneği
import java.util.List;
public class GenericOrnegi {
static <T> T ilk(List<T> liste) {
if (liste.isEmpty()) {
throw new IllegalArgumentException("Liste boş");
}
return liste.get(0);
}
public static void main(String[] args) {
System.out.println(ilk(List.of("Java", "JVM")));
System.out.println(ilk(List.of(10, 20, 30)));
}
}Ünite 9: Koleksiyonlar
Collection hiyerarşisi liste, küme ve kuyruk türlerini; Map ise anahtar-değer yapısını temsil eder. Veri yapısı seçiminde yalnız API kolaylığı değil, işlem karmaşıklığı, bellek yerleşimi, sıralama gereksinimi ve eşzamanlı erişim biçimi dikkate alınır.
ArrayList: rastgele erişim güçlü, sona ekleme amortize sabit zamanlı; ortadan ekleme/silme eleman kaydırabilir.LinkedList: çift bağlı listedir veDequesözleşmesini de uygular; indeksle erişim doğrusal maliyetlidir.HashSet/HashMap: doğru hash dağılımıyla genel amaçlı üyelik ve anahtar erişimi için uygundur; mantıksal sıralama garantisi vermez.LinkedHashMap: ekleme veya erişim sırasını koruyabilir.TreeSet/TreeMap: sıralı ağaç yapısıyla karşılaştırmaya dayalı düzen sağlar.ArrayDeque: çoğu yığın ve çift uçlu kuyruk kullanımında eskiStacksınıfına göre daha uygun genel tercihtir.PriorityQueue: öncelik sırasına göre baş elemanı sunar; bütün elemanların her an sıralı bir liste olarak tutulduğu varsayılmamalıdır.
HashMap iş parçacığı-safe değildir. Hashtable tarihsel senkronize bir sınıftır; çok iş parçacıklı yeni tasarımlarda erişim modeline göre ConcurrentHashMap veya dış senkronizasyon değerlendirilmelidir. ConcurrentHashMap, bütün tabloyu tek kilitle yönetiyormuş gibi düşünülmemelidir.
Iterator davranışı koleksiyon türüne göre değişebilir. Iterator tek yönlü dolaşımı, ListIterator liste üzerinde iki yönlü dolaşım ve konumsal güncellemeleri destekler. Birçok klasik koleksiyon fail-fast kontrol uygular; eşzamanlı koleksiyonların bazıları weakly consistent dolaşım sağlar. ConcurrentModificationİstisna bir eşzamanlılık güvenliği mekanizması değildir.
Daha özel koleksiyonlar da gerçek kullanım amacıyla seçilmelidir: LinkedHashSet hash tabanlı tekilliği öngörülebilir dolaşım sırasıyla birleştirir; EnumSet enum değerleri için kompakt bir küme gerçekler; NavigableMap sıralı anahtar uzayında alt/üst komşu sorgularını destekler; IdentityHashMap normal equals yerine referans kimliği kullanır ve genel amaçlı map yerine özel kimlik-semantikli problemler içindir. CopyOnWriteArrayList, okumanın çok, yazmanın seyrek olduğu ve kopyalama maliyetinin kabul edilebildiği eşzamanlı senaryolarda anlamlıdır; sık yazılan büyük listelerde pahalıdır.
Koleksiyon seçimini kod üzerinde görmek
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class KoleksiyonOrnegi {
public static void main(String[] args) {
List<String> diller = List.of("Java", "C", "C++", "C#");
Map<String, Integer> uzunluk = new HashMap<>();
for (String dil : diller) {
uzunluk.put(dil, dil.length());
}
ArrayDeque<String> kuyruk = new ArrayDeque<>();
kuyruk.addLast("birinci");
kuyruk.addLast("ikinci");
System.out.println(uzunluk.get("Java"));
System.out.println(kuyruk.removeFirst());
}
}Ünite 10: Lambda, Fonksiyonel Arayüzler ve Stream API
Lambda ifadeleri tek soyut metoda sahip fonksiyonel arayüzlere hedeflenir. @FunctionalInterface anotasyonu bu niyeti derleyici tarafından denetlenebilir kılar; anotasyon bulunmasa da tek soyut metot şartını sağlayan arayüz fonksiyonel olabilir. Predicate, Function, Consumer, Supplier ve bunların ilkel tür özelleştirmeleri yaygın örneklerdir. Metot referansı, uygun imzalı mevcut metodun fonksiyonel arayüz bağlamında kullanılmasını sağlar.
Stream bir veri deposu değildir; bir kaynak üzerinde ara ve son işlemlerden oluşan değerlendirme hattıdır. Ara işlemler çoğunlukla lazy'dir. map, filter, flatMap, sorted, distinct gibi işlemler son işlem çağrılıncaya kadar çalışmayabilir.
Parallel stream, CPU çekirdeği bulunduğu için otomatik olarak daha hızlı değildir. İş bölme maliyeti, ortak havuz kullanımı, veri miktarı, işlem başına maliyet, yan etkiler ve sıralama gereksinimleri ölçülmelidir. G/Ç ağırlıklı veya ortak kaynakta kilitlenen işlemler için kör paralelleştirme gecikmeyi artırabilir.
Stream örneği
import java.util.List;
public class StreamOrnegi {
public static void main(String[] args) {
List<Integer> degerler = List.of(3, 8, 11, 14, 21, 30);
int toplam = degerler.stream()
.filter(x -> x % 2 == 0)
.mapToInt(Integer::intValue)
.sum();
System.out.println(toplam);
}
}Bu kodun aynı işini basit bir for döngüsü de yapabilir. Stream kullanımının gerekçesi yalnız daha kısa görünmesi değil, dönüşüm zincirinin veri akışı olarak okunabilir olmasıdır; sıcak performans yolunda tahsis, boxing ve paralellik maliyetleri ayrıca ölçülmelidir.
Ünite 11: Giriş/Çıkış, NIO ve Serileştirme
java.io akış temelli klasik API'leri; java.nio ve java.nio.file buffer, channel, charset ve modern dosya sistemi erişimlerini sağlar. FileInputStream/FileOutputStream bayt, FileReader/FileWriter karakter akışına tarihsel örneklerdir; BufferedReader küçük okumaları tamponlayabilir, PrintStream ve PrintWriter biçimlendirilmiş metin çıktısı sağlar. Bu sınıfların adı karakter kodlaması problemini çözmez; metin okurken/yazarken charset açıkça yönetilmelidir. Bayt dizisini varsayılan platform charset'i ile metne çevirmek farklı ortamlarda sessiz veri bozulmasına yol açabilir.
XML işleme tarafında SAX olay/akış tabanlı ayrıştırma yaklaşımıdır ve tüm belge ağacını bellekte tutmak zorunda değildir. DOM ise ağaç modeli sağlar. Seçim belge boyutu, rastgele erişim gereksinimi ve işleme biçimine göre yapılmalıdır.
Path ve Files, dosya sistemi işlemleri için temel modern API'lerdir. Büyük dosyada bütün içeriği belleğe almak yerine akış, channel veya parça tabanlı işleme daha güvenli olabilir. Dosya yazımında kalıcı çıktının görünürlüğü gerekiyorsa geçici dosya + atomik taşıma gibi yayınlama desenleri düşünülmelidir.
Java yerleşik nesne serileştirmesi tarihsel sistemlerde bulunur; güvenilmeyen verinin deserialization işlemi ciddi güvenlik riski doğurabilir. Yeni dış veri sözleşmelerinde açık şema ve sınırlandırılmış veri biçimleri tercih edilmelidir.
Metin dosyası okuma/yazma: klasik akımdan modern Files API'sine
Sunum PrintWriter, BufferedReader, FileReader ve File sınıflarıyla metin dosyası G/Ç'sini öğretir. Bu sınıflar hâlâ geçerlidir. Modern kodda Path ve Files API'leri dosya sistemi işlemlerini daha açık bir modelle sunar; karakter kümesi de örtük platform varsayımına bırakılmamalıdır.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class DosyaOrnegi {
public static void main(String[] args) throws Exception {
Path yol = Path.of("ornek.txt");
try (BufferedWriter writer = Files.newBufferedWriter(yol, StandardCharsets.UTF_8)) {
writer.write("Java");
writer.newLine();
writer.write("JVM");
}
try (BufferedReader reader = Files.newBufferedReader(yol, StandardCharsets.UTF_8)) {
String satir;
while ((satir = reader.readLine()) != null) {
System.out.println(satir);
}
}
}
}File tarihsel API'de bir yol/dosya soyutlamasıdır; Path/Files daha zengin hata ve dosya sistemi semantiği sunar. İkili veri için InputStream/OutputStream, karakter verisi için Reader/Writer ayrımı temel kalır. Büyük dosyalarda tüm içeriği tek seferde belleğe almak yerine akış, kanal veya parça tabanlı yaklaşım gerekir.
Ünite 12: Annotation, Reflection ve Modüller
Annotation kaynak, class dosyası veya çalışma zamanı saklama politikasına sahip üst veri'dır. Framework'ler annotation'ları yapılandırma, eşleme ve doğrulama için yoğun kullanabilir. Annotation'ın varlığı tek başına davranış üretmez; onu işleyen derleyici, annotation processor, framework veya reflection kodu gerekir.
Reflection çalışma zamanında tür, alan, metot ve annotation bilgisine erişir. Esneklik sağlasa da statik tür güvenliği, kapsülleme, performans ve native/AOT uyumluluğu üzerinde maliyet oluşturabilir. Çalışma zamanı keşfi yerine açık kayıt veya üretim zamanı üst veri'sı bazı sistemlerde daha öngörülebilirdir.
Java Platform Module System, module-info.java üzerinden modül bağımlılıklarını ve dışarı açılan paketleri tanımlar. Modül, package ve JAR aynı kavram değildir.
@Deprecated, bir API'nin kullanımının artık önerilmediğini ve çoğu durumda alternatif/migrasyon bilgisinin incelenmesi gerektiğini belirtir; API'nin o anda çalışmadığı anlamına gelmez. Lombok gibi harici araçlar derleme zamanı kod üretimi yapabilir; örneğin @SuperBuilder Lombok'a özgüdür, Java veya Jakarta standardı değildir. Bu tür anotasyonların kaynağı ve ürettiği kod özellikle IDE, annotation processing ve build zincirinde açıkça bilinmelidir.
Ünite 13: Eşzamanlılık ve Java Bellek Modeli
Thread oluşturmak yalnız paralellik sağlamak değildir; görünürlük, atomiklik ve sıralama problemlerini de getirir. Java Bellek Modeli, bir iş parçacığındaki yazmanın başka bir iş parçacığı tarafından hangi koşullarda görüleceğini tanımlar. happens-before ilişkisi bu modelin merkezindedir.
synchronized karşılıklı dışlama yanında monitör giriş/çıkışları üzerinden bellek görünürlüğü sağlar. volatile bir değişkenin okumaları ve yazmaları arasında görünürlük/sıralama garantileri getirir; fakat count++ gibi bileşik read-modify-write işlemini atomik yapmaz. Atomik sayaçta AtomicInteger, yüksek çekişmeli istatistik sayaçlarında LongAdder gibi araçlar uygun olabilir.
Lock ailesi zaman aşımı, interruptible kilit alma ve birden çok Condition gibi ek yetenekler sunar. Kilit sayısı arttıkça global sıra kuralı, minimum kilit süresi ve bloklayan G/Ç'nin kilit altında tutulmaması deadkilit riskini azaltır.
ExecutorService, görev sunma ile iş parçacığı yaşam döngüsünü ayırır. Havuz boyutunu “CPU sayısı kadar” veya “ne kadar büyük o kadar iyi” biçiminde tek formülle belirlemek doğru değildir; görevlerin CPU/G/Ç oranı, dış kaynak kapasiteleri, kuyruk ve hedef gecikme birlikte değerlendirilir.
CompletableFuture asenkron iş akışlarını bileştirebilir. Hangi executor üzerinde çalıştığı, istisna propagation ve timeout davranışı açıkça yönetilmelidir. Virtual iş parçacığı'ler çok sayıda bloklayan görevin iş parçacığı başına düşük maliyetle modellenmesini kolaylaştırır; ancak veritabanı bağlantısı, uzak servis kotası veya kilit gibi gerçek kıt kaynakların kapasitesini artırmaz.
Structured concurrency gibi bazı yeni eşzamanlılık API'leri Java sürümüne göre preview olabilir. Preview özelliği kalıcı API sözleşmesi gibi kullanılmamalı; kullanılan JDK sürümünün belirtimi kontrol edilmelidir.
Atomik sayaç ve virtual iş parçacığı örneği
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
public class EszamanlilikOrnegi {
public static void main(String[] args) throws Exception {
AtomicInteger sayac = new AtomicInteger();
List<Future<?>> gorevler = new ArrayList<>();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000; i++) {
gorevler.add(executor.submit(sayac::incrementAndGet));
}
for (Future<?> gorev : gorevler) {
gorev.get();
}
}
System.out.println(sayac.get());
}
}Örnekte atomik sayaç veri yarışını önler. Virtual iş parçacığı, görevin temsil maliyetini düşürür; aynı anda 1.000 veritabanı sorgusunun güvenli olduğu anlamına gelmez. Dış kaynak kapasitesi ayrıca sınırlandırılmalıdır.
Ünite 14: JVM Belleği, Çöp Toplama ve JIT
Heap'te ulaşılabilir olmayan nesneler çöp toplama için aday olur; “değişkeni null yapmak nesneyi hemen siler” ifadesi doğru değildir. GC algoritması, heap boyutu, tahsis oranı, canlı veri miktarı ve gecikme hedefi birlikte değerlendirilir.
Bellek sızıntısı Java'da da mümkündür: bir nesne artık iş açısından gerekli olmadığı hâlde güçlü referans zinciriyle erişilebilir kalıyorsa GC onu toplayamaz. Sınırsız önbellek, listener kaydı, ThreadLocal yaşamı ve uzun ömürlü koleksiyonlar yaygın nedenlerdir.
JIT optimizasyonları ısınma davranışı oluşturur. Bu nedenle mikro benchmark System.nanoTime() çevresinde birkaç kez metot çağırmaktan ibaret olmamalıdır. Dead-code elimination, constant folding, tiered compilation ve GC etkileri hesaba katılmalı; Java mikro ölçümlerinde JMH gibi bu sorunları modelleyen araçlar kullanılmalıdır.
Eski JVM kaynaklarında görülen PermGen (Permanent Generation) güncel HotSpot bellek düzeninin bir parçası değildir; Java 8 ile sınıf üst veri'sı için native bellek tabanlı Metaspace yaklaşımına geçilmiştir. Eski PermGen space hata ve ayarlarını güncel JVM'e aynen taşımak sürüm hatasıdır.
Ünite 15: JDBC ve Veri Erişimi
JDBC sürücüden bağımsız bir Java veri tabanı API'sidir. Connection, PreparedStatement ve ResultSet temel yapı taşlarıdır. CallableStatement, saklı yordam/fonksiyon çağrılarında kullanılan JDBC sözleşmesidir; ürün bağımlı prosedürel davranışın taşınabilir SQL ile karıştırılmaması gerekir. Parametreli SQL, hem veri türü bağlama hem SQL injection riskini azaltma açısından string birleştirmeden üstündür.
Transaction sınırı iş atomikliğini temsil etmelidir. Auto-commit davranışı bilinmeden yapılan çok adımlı yazmalar kısmi sonuç üretebilir. Bağlantı havuzu bağlantı yaratma maliyetini amorti eder; havuz kapasitesi veritabanının gerçek eşzamanlı işlem kapasitesinden bağımsız düşünülemez.
ORM/JPA, SQL ve transaction bilgisinin yerine geçmez. Hibernate gibi sağlayıcılarda SessionFactory/Session ve HQL gibi sağlayıcıya özgü kavramlar bulunurken JPA taşınabilir tarafta EntityManager, JPQL ve standart eşleme anotasyonlarını tanımlar. @ManyToOne, @OneToMany, @GeneratedValue ve FetchType.LAZY/EAGER gibi ayarlar veri erişim planını etkiler. Persistence context kapandıktan sonra lazy ilişkiye erişim Hibernate tarafında LazyInitializationİstisna gibi hatalar üretebilir; çözüm her ilişkiyi eager yapmak değildir. N+1 sorgu, gereksiz entity yükleme, geniş transaction, hatalı fetch stratejisi ve sınırsız sonuç kümesi uygulama tarafında ciddi maliyet oluşturabilir. Bu konunun performans tarafı Java Tabanlı Veri Sistemlerinde Yüksek Başarım notunda ayrıntılıdır.
Eski JDBC örneklerinde sürücüyü dinamik yüklemek için Class.forName(...) ve sorgu yürütmek için Statement/PreparedStatement.executeQuery(...) kalıpları görülebilir. Modern JDBC sürücüleri çoğunlukla service-provider mekanizmasıyla otomatik keşfedilir; Class.forName her uygulamada zorunlu başlangıç adımı değildir. executeQuery sonuç kümesi döndüren sorgular için kullanılırken güncelleme ve DDL gibi işlemlerin farklı yürütme kontratları vardır.
PreparedStatement ile parametreli sorgu
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcOrnegi {
static String kullaniciAdi(Connection connection, long id) throws SQLException {
String sql = "select ad from kullanici where id = ?";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setLong(1, id);
try (ResultSet rs = ps.executeQuery()) {
return rs.next() ? rs.getString(1) : null;
}
}
}
}Parametre bağlama yalnız SQL injection azaltımı değildir; tür dönüşümü ve sorgu sözleşmesini de belirginleştirir. Transaction ve timeout yönetimi bu örneğin dışında bırakılmıştır; gerçek uygulamada bağlantı yaşam döngüsü ve iş atomikliği ayrıca tanımlanmalıdır.
Ünite 16: Ağ, HTTP ve Kurumsal Java Ekosistemi
Java standart kütüphanesi soket ve HTTP istemci API'leri sunar. TCP akış, UDP datagram modelidir; uygulama protokolü tasarlarken mesaj sınırı, framing, timeout, yeniden deneme ve kimlik doğrulama ayrıca belirlenir.
Servlet/JSP, Java web uygulamalarının tarihsel ve hâlen bazı sistemlerde kullanılan temel teknolojileridir. HttpServlet HTTP odaklı servlet tabanını; ServletConfig tek servlet'e, ServletContext ise web uygulaması kapsamına ilişkin bağlamı temsil eder. JSP tarafında JSTL gibi etiket kütüphaneleri görünüm mantığını Java scriptlet kodundan ayırmak için kullanılmıştır. Güncel Jakarta EE terminolojisinde bunların karşılıkları Jakarta Servlet ve Jakarta Pages belirtimleridir; eski javax.* paketleriyle güncel jakarta.* paketleri aynı namespace değildir. Modern servislerde Spring MVC, Spring Boot, Jakarta REST veya benzeri çerçeveler daha yüksek seviyeli programlama modeli sağlar. Framework bilgisi Java dil bilgisinin yerine geçmez; class loading, istisna, iş parçacığı, collection ve I/O davranışı üretim problemlerinde doğrudan görünür hâle gelir.
Masaüstü arayüz tarafında AWT ve Swing Java SE içinde yer alan köklü araç takımlarıdır. JavaFX ise modern sürümlerde JDK'dan ayrı geliştirilen OpenJFX projesi olarak kullanılabilir; Scene, layout ve control bileşenleriyle grafik arayüz oluşturur. Eski bir Swing/JavaFX uygulamasının varlığı, web veya servis mimarisine taşınması gerektiği anlamına gelmez; arayüz teknolojisi dağıtım, bakım ve kullanıcı etkileşimi gereksinimine göre seçilmelidir.
Spring ekosistemi ayrı olarak Spring Boot, protokol ve tarayıcı tarafı ise Web Programlama notunda ele alınır.
Servlet tabanlı eski Java web uygulamalarında web.xml dağıtım descriptor; servlet, filter, listener ve mapping gibi yapılandırmaları XML ile tanımlamak için merkezi bir mekanizmaydı. Anotasyon tabanlı yapılandırma birçok kullanım alanını azaltmış olsa da web.xml mevcut Jakarta Servlet/JSP sistemlerinde ve belirli container ayarlarında hâlâ karşılaşılabilen tarihsel/uyumlu bir yapılandırma biçimidir.
Standart HttpClient örneği
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpOrnegi {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build();
HttpRequest request = HttpRequest.newBuilder(URI.create("https://example.com"))
.timeout(Duration.ofSeconds(3))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.statusCode());
}
}Gerçek servis istemcisinde timeout yalnız bağlantı için değil istek için de tanımlanmalı; yeniden deneme yalnız idempotency ve hata sınıfı bilinerek uygulanmalıdır. HTTP istemci örneği ağ programlama ilkelerinin yerine geçmez; bağlantı havuzu, DNS, TLS, proxy ve sunucu kapasitesi uçtan uca davranışın parçasıdır.
Ünite 17: Derleme, Bağımlılık ve Test
Maven ve Gradle yalnız “kütüphane indiren araçlar” değildir; bağımlılık çözümleme, derleme yaşam döngüsü, test, paketleme ve eklenti yürütme gibi görevleri yönetir. Transitive dependency sürümleri kontrolsüz bırakıldığında farklı ortamlarda davranış değişikliği veya güvenlik açığı oluşabilir.
JUnit birim ve entegrasyon testlerinin temel araçlarından biridir. Mockito gibi mock kütüphaneleri dış bağımlılığın davranışını kontrollü biçimde taklit etmek için kullanılabilir; her sınıfı mock'lamak iyi test tasarımı anlamına gelmez. Saf hesaplama kodunda gerçek nesneler daha düşük bakım maliyetine sahiptir.
Git kaynak sürüm kontrolünü dağıtık modelle sağlar. Commit, branch ve merge; derlenmiş çıktı veya veri tabanı yedeği yerine kaynak geçmişini ve değişim bağlamını yönetmelidir. CI/CD otomasyonu test ve paketleme sürecini tekrarlanabilir hâle getirir, fakat hatalı test veya hatalı dağıtım politikasını kendiliğinden düzeltmez.
Klasik JavaFX kodunda VBox dikey yerleşim kapsayıcısı, ScrollBar kaydırma denetimi ve NumberConverter sayısal değerlerle metin arasındaki dönüşümlerde kullanılan converter ailesinin bir parçasıdır. Bunlar JavaFX API örnekleridir; Java dil özelliği değildir. Benzer şekilde belirli widget sınıfını bilmek, layout/iş parçacığıing/lifecycle modelini anlamanın yerine geçmez.
Ünite 18: Güvenli ve Ölçülebilir Java
Güvenli Java kodu yalnız istisna yakalamakla sağlanmaz. Girdi doğrulama, yetkilendirme, sorgu parametreleme, güvenli deserialization, kriptografik API'lerin doğru kullanımı, gizli bilgilerin loglanmaması ve bağımlılık yönetimi aynı güvenlik sınırının parçalarıdır.
Performans çalışmasında önce sistem hedefi tanımlanmalıdır: gecikme mi, işlem hacmi mi, CPU mu, bellek mi, GC pause mu, dış servis kapasitesi mi? Bir ArrayList yerine başka koleksiyon seçmenin etkisi, yanlış SQL veya kilit altında uzak çağrı gibi makro darboğazların yanında önemsiz kalabilir. Ölçüm profilleyici, JFR, GC çıktısı, iş parçacığı dump ve sistem metrikleriyle gerçek darboğazı göstermelidir.
Java'nın dili, JVM'i ve ekosistemi ayrı katmanlardır. Dil kuralını framework davranışıyla, framework varsayımını JVM garantisiyle karıştırmamak üretim sorunlarını anlamada temel disiplindir.
Kaynakça
- Oracle. Java Platform, Standard Edition Documentation. https://docs.oracle.com/en/java/javase/
- Oracle. Java Language Specification. https://docs.oracle.com/javase/specs/
- Oracle. Java Virtual Machine Specification. https://docs.oracle.com/javase/specs/
- Oracle. Java SE API Documentation. https://docs.oracle.com/en/java/javase/27/docs/api/
- Brian Goetz et al. Java Concurrency in Practice. Addison-Wesley, 2006.
- Joshua Bloch. Effective Java, 3rd ed. Addison-Wesley, 2018.
- OpenJFX. JavaFX Documentation. https://openjfx.io/
- Eclipse Foundation. Jakarta Servlet Specification. https://jakarta.ee/specifications/servlet/
- Eclipse Foundation. Jakarta Pages Specification. https://jakarta.ee/specifications/pages/
- OpenJDK. JEP 444: Virtual Threads. https://openjdk.org/jeps/444
- OpenJDK. Java Microbenchmark Harness (JMH). https://openjdk.org/projects/code-tools/jmh/
- Eclipse Foundation. Standard Widget Toolkit (SWT). https://www.eclipse.org/swt/
- A. Yazıcı, E. Doğdu, M. Özbayoğlu, M. Erten, O. Ergin. Java Bilgisayar Programlamaya Giriş.
- Walter Savitch, Frank M. Carrano. Java: An Introduction to Problem Solving & Programming, 5th ed. Pearson Education.
- P. Deitel, H. Deitel. Java How to Program. Pearson Education.