Kapsamlı Java Eğitim Seti
Java Kapsamlı Eğitim
1.1 Java Nedir? Tarihçe, Kullanım Alanları
1.1 Java'ya Merhaba! Programlamanın Süper Gücü
Java, 1995 yılında James Gosling ve ekibi tarafından geliştirilmiş, güçlü ve popüler bir programlama dilidir. Onu bu kadar özel yapan şey, sihirli prensibi olan WORA dır:
WORA: "Write Once, Run Anywhere" (Bir Kere Yaz, Her Yerde Çalıştır!)
Bu, yazdığınız kodun Windows'tan Android telefona kadar farklı cihaz ve işletim sistemlerinde sorunsuz çalışabilmesi demektir. Java özellikle büyük kurumsal sistemlerde, Android mobil uygulamalarda ve web sunucularında yaygın olarak kullanılır.
1.2 JDK, JRE, JVM Farkları: Java'nın 3 Başlangıç Kutusu
Java'nın çalışma ortamı 3 ana bileşenden oluşur. Bunlar olmadan Java kodu ne yazılabilir ne de çalıştırılabilir:
1. JDK (Java Development Kit - Geliştirme Kiti)
- Ne işe yarar? Java programları yazmak ve derlemek (compile) için gerekli olan araç setidir.
- İçindekiler: JRE'yi, derleyiciyi (`javac`), hata ayıklayıcıyı (debugger) ve diğer geliştirme araçlarını içerir.
- Kim Kullanır? Sadece yazılımcılar kullanır.
2. JRE (Java Runtime Environment - Çalışma Ortamı)
- Ne işe yarar? Yazılmış bir Java programını çalıştırmak için gereken ortamdır.
- İçindekiler: JVM'i ve programların çalışması için gerekli olan çekirdek Java kütüphanelerini içerir.
- Kim Kullanır? Hem yazılımcılar hem de son kullanıcılar kullanır.
3. JVM (Java Virtual Machine - Sanal Makine)
- Ne işe yarar? Java'nın sihirli WORA özelliğini sağlayan soyut makinedir. Java kodunu makine diline çevirip çalıştıran asıl motor budur.
- İşleyiş: Yazdığınız Java kodu derlenir ve Bytecode adı verilen özel bir koda dönüştürülür. JVM, bu Bytecode'u alır ve çalıştığı platforma (Windows/Mac/Linux) uygun makine koduna çevirir.
1.3 İlk Kodun İskeleti: Class, main metodu ve Syntax
Java'da her şey, bir kutu gibi düşünebileceğin Sınıf (Class) içinde başlar ve biter. Programın çalışmaya başladığı yer ise özel bir metottur.
1. Sınıf (Class) Tanımı
public class Oyun { ... }: Tüm kodunu içine koyduğun ana yapı. Dosya adı (`Oyun.java`) ile sınıf adı aynı olmalıdır.
2. main metodu (Başlangıç Çizgisi)
public static void main(String[] args) { ... }: Programın koşmaya başladığı başlangıç çizgisidir. JVM, ilk olarak bu metodu arar ve çalıştırır.
3. Syntax (Söz Dizimi) Kuralları
- Noktalı Virgül (`;`): Her komut satırının sonuna mutlaka konulmalıdır. Python gibi dillerden ana farkıdır.
- Süslü Parantez (`{}`): Kod bloklarını (sınıflar, metotlar, döngüler, karar yapıları) gruplamak için kullanılır.
- Büyük/Küçük Harf Duyarlılığı (Case Sensitivity): `int sayi` ve `int Sayi` tamamen farklı iki değişkendir. Metot, sınıf ve değişken isimlerinde bu kurala uymak zorunludur.
Örnek Yapı:
public class MerhabaDunya {
public static void main(String[] args) {
System.out.println("Komut 1 bitti");
int yas = 10;
System.out.println("Yaş: " + yas);
}
}
1.4 Değişkenler ve Veri Tipleri: Veri Kutularını Yönetme
Değişkenler, bilgisayarın hafızasında bilgi sakladığımız isimli kutucuklardır. Her kutunun içine ne tür bir bilgi koyabileceğimizi veri tipi belirler.
Temel (Primitive) Veri Tipleri (Küçük Kutular)
Bu tipler, doğrudan değeri hafızada tutar ve hızlıdırlar. Toplam 8 tanedir:
- Tam Sayılar:
byte,short,int(en yaygın),long. - Ondalıklı Sayılar:
float(daha az hassas),double(daha yaygın ve hassas). - Tek Karakter:
char(Tek bir harf veya sembol). - Doğru/Yanlış:
boolean(`true` veya `false` tutar).
Referans (Reference) Veri Tipleri (Büyük Kutular)
Bunlar doğrudan değeri değil, değerin hafızadaki adresinin(referansını) tutar.
String: Metinler için kullanılır (Örn: "Java dersi"). Primitif gibi görünse de aslında bir sınıftır.- Diziler (Arrays), Sınıflar (Classes), Interface'ler.
Değişken Tanımlama:
int hiz = 100; // int tipinde hiz değişkeni yarat ve 100 değerini ata
boolean durum = true;
String isim = "Can";
1.5 Operatörler: Hesaplama ve Karşılaştırma Araçları
Programlamada değerler üzerinde işlem yapmak için kullanılan sembollerdir.
1. Aritmetik Operatörler:
- `+`, `-`, `*`, `/`: Standart dört işlem.
- `%` (Modulus): Kalanı bulur. Örn: `10 % 3 = 1`.
- `++` / `--`: Değeri 1 artırma / 1 azaltma.
2. Karşılaştırma Operatörleri (Cevap hep boolean döner):
- `==`: Eşit mi?
- `!=`: Eşit değil mi?
- `>`, `<`, `>=`, `<=`: Büyüklük ve küçüklük.
3. Mantıksal Operatörler (Birden çok koşulu birleştirir):
- `&&` (VE): Her iki koşul da doğruysa `true` döner.
- `||` (VEYA): Koşullardan en az biri doğruysa `true` döner.
- `!` (DEĞİL): Koşulun tersini alır.
1.6 Klavyeden Veri Alma (Scanner)
Programın kullanıcı ile etkileşime girmesi için klavyeden veri almak gerekir. Bunun için Java'nın hazır Scanner sınıfı kullanılır.
Kullanım Adımları:
- İçe Aktarma (Import): Sınıfı kullanabilmek için dosyanın en başına `import java.util.Scanner;` yazılmalıdır.
- Nesne Oluşturma: `Scanner klavye = new Scanner(System.in);` komutu ile bir Scanner nesnesi yaratılır.
- Veri Okuma: Kullanılacak veri tipine uygun metot çağrılır.
Önemli Okuma Metotları:
- `klavye.nextLine()`: Tam metin satırını okur.
- `klavye.next()`: Bir sonraki kelimeyi okur (boşluğa kadar).
- `klavye.nextInt()`: Bir tam sayıyı okur.
- `klavye.nextDouble()`: Bir ondalıklı sayıyı okur.
Örnek Kod:
import java.util.Scanner;
public class VeriAlma {
public static void main(String[] args) {
Scanner klavye = new Scanner(System.in);
System.out.print("Adınızı girin: ");
String kullaniciAdi = klavye.nextLine();
System.out.println("Hoş geldin, " + kullaniciAdi);
}
}
1.7 Karar Yapıları: Kodun Akışını Yönlendirme
Programın belirli koşullara bağlı olarak farklı yollar izlemesini sağlar.
if-else if-else Yapısı
En temel karar verme aracıdır. Koşul doğruysa (`true`) ilgili kod bloğu çalışır.
int puan = 85;
if (puan >= 90) {
System.out.println("A+ aldın!");
} else if (puan >= 80) {
System.out.println("A aldın!");
} else {
System.out.println("Diğer not.");
}
switch Yapısı
Tek bir değişkenin birden fazla olası sabit değere karşı kontrol edilmesi gerektiğinde kullanılır. `if` yapısına göre daha temiz ve okunur olabilir.
- `case`: Kontrol edilen değişkenin olası değerlerinden biridir.
- `break`: İlgili kod bloğu çalıştıktan sonra `switch` yapısından çıkılmasını sağlar. (Kullanılmazsa alttaki `case`'ler de çalışır!).
- `default`: Hiçbir `case` eşleşmezse çalışacak bloktur.
1.8 Döngüler: Tekrarlayan Komutları Otomatikleştirme
Aynı kod bloğunu defalarca, belirli bir koşul sağlanana kadar çalıştırmamızı sağlar.
1. for Döngüsü (En çok kullanılan)
Kaç kere döneceğini **başlangıçta bildiğimiz** durumlarda idealdir. 3 bölümden oluşur: Başlangıç, Koşul, Artış/Azalış.
for (int i = 0; i < 5; i++) {
System.out.println("Sayı: " + i); // 0'dan 4'e kadar yazar
}
2. while Döngüsü
Bir koşul doğru olduğu sürece döner. Kaç kere döneceği önceden bilinmeyebilir (Örn: Kullanıcı "çıkış" yazana kadar). Koşul başta kontrol edilir.
3. do-while Döngüsü
Koşul ne olursa olsun, döngünün en az bir kere çalışacağını garanti eder. Koşul sonda kontrol edilir.
break ve continue
- `break`: Döngüyü tamamen durdurur ve döngüden çıkar.
- `continue`: Mevcut döngü adımını atlayıp, bir sonraki adıma geçer.
1.9 Diziler (Arrays): Düzenli Veri Rafları
Aynı veri tipinden birden fazla veriyi (Örn: öğrenci isimleri) topluca, ardışık bellek alanında saklayan yapılardır.
Dizilerin Önemli Özellikleri:
- Sabit Boyut: Bir kere tanımlandıktan sonra boyutu değiştirilemez.
- Sıralı Erişim: Elemanlara 0'dan başlayan bir indeks numarası ile erişilir.
- Tek Tip: Sadece tanımlandığı tipte (int, String vb.) veri saklayabilir.
Tanımlama ve Kullanım:
String[] isimler = new String[3]; // 3 elemanlı dizi yarat
isimler[0] = "Can"; // İlk eleman
isimler[1] = "Ece";
System.out.println(isimler[0]); // Çıktı: Can
İki Boyutlu Diziler (Matrisler)
Satır ve sütunlardan oluşan tablolar gibi düşünülebilir. Her elemanına iki indeksle erişilir: `dizi[satır][sütun]`. Matris işlemleri için kullanılır.
1.10 Metotlar (Fonksiyonlar): Tekrar Eden İşleri Paketleme
Belirli bir görevi yerine getiren, tekrar tekrar çağırılabilen ve programı daha düzenli hale getiren kod bloklarıdır. Metotlar, girdileri (parametre) alıp, bir çıktı (geri dönüş değeri) verebilir.
Metot Yapısı:
[erişim] [static/final] [geri_dönüş_tipi] metotAdi(parametreler) { ... }
- Geri Dönüş Tipi: Metot bir değer döndürmeyecekse `void`, tam sayı döndürecekse `int`, metin döndürecekse `String` yazılır.
- `return`: Eğer metot `void` değilse, mutlaka bir değer döndürmek zorundadır (`return deger;`).
Örnekler:
// Geri dönüşsüz (void) metot
public static void selamVer(String ad) {
System.out.println("Merhaba " + ad);
}
// Geri dönüşlü (int) metot
public static int topla(int s1, int s2) {
return s1 + s2;
}
1.11 Scope (Kapsam): Değişkenlerin Yaşam Alanı
Bir değişkenin programın hangi kısımlarından erişilebilir olduğunu belirler. Bir değişkenin kapsamı bittiğinde, o değişken bellekten silinir.
Temel Kapsam Türleri:
- Sınıf Kapsamı (Class Scope): Sınıf içinde, metotların dışında tanımlanan değişkenlerdir. O sınıftaki tüm metotlar tarafından erişilebilir.
- Metot Kapsamı (Method Scope): Bir metodun içinde tanımlanan değişkenlerdir. Sadece o metodun içinden erişilebilir.
- Blok Kapsamı (Block Scope): Bir `if`, `for` veya `while` bloğu (`{}`) içinde tanımlanan değişkenlerdir. Sadece o blok içinden erişilebilir.
Kural: Bir değişkeni, tanımlandığı süslü parantez (`{}`) bloğunun dışından çağıramazsınız.
Örnek: `for (int i=0; ...)` döngüsündeki `i` değişkeni, sadece döngü bittiğinde yok olur ve döngü dışında kullanılamaz.
1.12 String ve Math Sınıfı Metotları
String (Metin) Sınıfı
Metinler üzerinde işlem yapmamızı sağlayan hazır metotlar sunar (String, referans bir tiptir ve değiştirilemez - Immutable).
- `length()`: Metnin uzunluğunu verir.
- `toUpperCase()` / `toLowerCase()`: Harfleri büyük/küçük yapar.
- `indexOf(char/String)`: Bir karakterin/metnin ilk nerede geçtiğini bulur.
- `equals(String)`: İki metnin tam olarak aynı olup olmadığını kontrol eder (Sadece `==` kullanmak yanlıştır!).
- `substring(int baslangic, int bitis)`: Metnin bir kısmını keser.
Math Sınıfı
Hazır matematiksel işlemler için kullanılır. Metotların çoğu `static` olduğu için nesne oluşturmaya gerek yoktur (`Math.metotAdı()`).
- `Math.sqrt(sayi)`: Karekök alır.
- `Math.pow(taban, us)`: Üs alma.
- `Math.random()`: 0.0 ile 1.0 arasında rastgele ondalıklı sayı üretir.
- `Math.max(s1, s2)` / `Math.min(s1, s2)`: İki sayıdan büyük/küçük olanı bulur.
2.1 Sınıflar (Classes) ve Nesneler (Objects)
Nesne Yönelimli Programlama (OOP), gerçek dünyadaki varlıkları (Örn: Araba, Köpek, Hesap) programlamaya uyarlamaktır.
Sınıf (Class)
Sınıf, bir nesnenin özelliklerini (fields / değişkenler) ve yeteneklerini (methods / metotlar) tanımlayan bir taslak veya kalıptır. Soyut bir kavramdır. (Örn: Bir otomobilin planı).
Nesne (Object)
Nesne, sınıfa göre oluşturulmuş somut bir örnektir. Bellekte yer kaplar ve `new` anahtar kelimesi kullanılarak oluşturulur. (Örn: Kırmızı renkli, 2023 model X marka otomobil).
Örnek Oluşturma:
// Sınıf: Araba
Araba xAraba = new Araba(); // Nesne (Object) yaratıldı
xAraba.renk = "Kırmızı"; // Özellik (Field) tanımlandı
xAraba.hizlan(); // Yetenek (Method) çağrıldı
2.2 Yapıcılar (Constructors)
Yapıcılar (Constructor), bir nesne yaratıldığı anda (`new` komutu verildiğinde) otomatik olarak çalışan, başlangıç ayarlarını yapan özel metotlardır.
Yapıcının Özellikleri:
- Sınıf adıyla aynı isme sahip olmalıdır.
- Geri dönüş tipi yoktur (ne `void` ne de başka bir tip).
- Eğer siz hiç yazmazsanız, Java arka planda parametresiz bir varsayılan (default) yapıcı oluşturur.
Yapıcı Aşırı Yükleme (Overloading)
Aynı sınıfta, farklı sayıda veya farklı tipte parametre alan birden fazla yapıcı tanımlanabilir. Bu, nesnenin farklı başlangıç değerleriyle oluşturulabilmesini sağlar.
Örnek Yapıcı:
public class Ogrenci {
String isim;
int numara;
public Ogrenci(String isim, int numara) { // Yapıcı
this.isim = isim;
this.numara = numara;
}
}
2.3 this ve super Anahtar Kelimeleri
this Anahtar Kelimesi
`this`, içinde bulunduğumuz mevcut nesneyi işaret eder. En yaygın 3 kullanımı:
- Alan/Parametre Farkı: Metot parametresi ile sınıf alanının adı aynıysa (Örn: `int sayi`), alanın kendisini belirtmek için kullanılır: `this.sayi = sayi;`
- Yapıcı Zinciri: Bir yapıcının içinden o sınıfın başka bir yapıcısını çağırmak için kullanılır: `this(parametreler);`
- Metot/Alan Erişimi: Nesnenin kendi metotlarına veya alanlarına erişmek için kullanılır.
super Anahtar Kelimesi (Kalıtımda kullanılır)
`super`, kalıtım alınan üst sınıfı işaret eder. Alt sınıflar için üst sınıfa erişim sağlar.
- Üst Sınıf Yapıcısı: Alt sınıf yapıcısının en başında, üst sınıfın yapıcısını çağırmak için kullanılır: `super(parametreler);`
- **Üst Sınıf Metotları:** Üst sınıftaki, alt sınıfta ezilmiş (override edilmiş) bir metodu çağırmak için kullanılır: `super.metotAdi();`
2.4 Erişim Belirleyiciler (Access Modifiers)
Sınıf, metot veya değişkenlerin (alanların) programın diğer bölümlerinden ne kadar erişilebilir olduğunu kontrol eder. Veri güvenliği ve kapsülleme için kritik öneme sahiptir.
4 Temel Seviye:
- `public` (Herkese Açık): Kısıtlama yok. Her yerden erişilebilir.
- `private` (Gizli): Sadece kendi sınıfının içinden erişilebilir. Dışarıdan doğrudan erişilemez. Kapsüllemenin temelidir.
- `protected`: Aynı paketin içinden VE farklı pakette olsalar bile alt sınıflardan erişilebilir.
- `default` (Hiçbir şey yazılmazsa):** Sadece aynı paketin içindeki sınıflardan erişilebilir.
2.5 Kapsülleme (Encapsulation)
Kapsülleme (Sarmalama), veriyi (alanları) metotlar aracılığıyla gizleyerek dışarıdan doğrudan erişimi engelleme ve verinin kontrollü bir şekilde değiştirilmesini sağlama prensibidir.
Nasıl Uygulanır?
- Sınıf içindeki tüm alanlar (`field`) `private` olarak tanımlanır. (Veri Gizleme).
- Bu alanlara erişim için `public` metotlar (`Getter` ve `Setter`) oluşturulur.
Getter ve Setter Metotları
- Getter (Erişimci): Alanın değerini okumak için kullanılır. (Örn: `public String getAd()`)
- Setter (Ayarlayıcı): Alanın değerini değiştirmek için kullanılır. Setter içinde, gelen verinin doğru olup olmadığı kontrol edilebilir. (Örn: `public void setAd(String ad)`)
2.6 Kalıtım (Inheritance)
Kalıtım, bir sınıfın (Alt Sınıf / Child Class), başka bir sınıftaki (Üst Sınıf / Parent Class) tüm özellikleri ve metotları **tekrar yazmadan** miras almasıdır.
Temel Kurallar:
- Java'da bir sınıf, sadece tek bir sınıftan miras alabilir (`extends`).
- Özel (private) üyeler miras alınır, ancak doğrudan erişilemez (dolaylı yoldan `public` metotlarla erişilebilir).
- Kod Tekrarını Önler: Ortak özellikler tek bir sınıfta toplanır, alt sınıflar sadece kendilerine özel detayları yazar.
Kullanım:
class Hayvan { ... } // Üst Sınıf
class Kopek extends Hayvan { ... } // Alt Sınıf
class Kedi extends Hayvan { ... } // Alt Sınıf
2.7 Çok Biçimlilik (Polymorphism)
Polymorphism, bir metotun veya nesnenin, çağrıldığı bağlama göre farklı biçimlerde (davranışlarda) bulunabilmesidir. (Çok Biçimlilik = Bir arayüz, birden çok uygulama).
1. Metot Aşırı Yükleme (Overloading - Derleme Zamanı)
Aynı sınıfta, aynı isme sahip ancak farklı parametre listesi (sayı, tip veya sıra) olan birden fazla metot tanımlanmasıdır. Hangi metodun çalışacağına program daha derlenirken karar verilir.
2. Metot Ezme (Overriding - Çalışma Zamanı)
Alt sınıfın, üst sınıftan miras aldığı bir metodu alıp, kendi ihtiyacına göre yeniden yazmasıdır. Çalışma zamanında (runtime) nesnenin gerçek tipine bakılarak hangi metodun çalışacağına karar verilir.
- `@Override`: Ezme işlemini yaptığınızı belirten bir etikettir (isteğe bağlı ama önerilir).
- Kural: Üst sınıfın referansı, alt sınıfın nesnesini tutabilir: `Hayvan kopek = new Kopek();`
2.8 Soyutlama ve Abstract Sınıflar
Soyutlama (Abstraction), bir nesnenin karmaşık detaylarını gizleyip, sadece temel işlevlerini (neyi yaptığını, nasıl yaptığını değil) gösterme prensibidir. Müşterinin sadece düğmeye basması, makinenin içini bilmesine gerek yoktur.
Abstract (Soyut) Sınıflar
- `abstract` anahtar kelimesi ile tanımlanır.
- Bu sınıfların nesnesi oluşturulamaz (Örn: `new AbstractSinif()` yapılamaz). Sadece miras alınabilir.
- Hem **normal** hem de **soyut (`abstract`)** metotlar içerebilir.
Abstract (Soyut) Metotlar
Bu metotların **gövdesi yoktur** (içinde `{}` blokları boş kalır ve sonunda noktalı virgül konur). Miras alan alt sınıflar, bu soyut metotları zorunlu olarak kendi içlerinde uygulamak zorundadır.
2.9 Interface’ler (Arayüzler)
Interface'ler, bir sınıfın sahip olması gereken yetenekleri (metotları) belirten tamamen soyut bir davranış sözleşmesidir.
Temel Farklar ve Kurallar:
- Çoklu Uygulama: Bir sınıf, birden fazla Interface'i `implements` (uygulayabilir) edebilir (Java'da çoklu kalıtımın yoludur).
- Metotlar: Java 8 öncesinde tüm metotlar otomatik olarak `public abstract` idi (gövdesizdi). Java 8'den sonra `default` ve `static` gövdeli metotlar da eklenebilir hale geldi.
- Alanlar: Tüm alanlar otomatik olarak `public static final` (sabit) kabul edilir.
Abstract Sınıf vs Interface:
- Abstract Sınıf: "Bu benim bir parçamdır" (is-a) ilişkisi için ve ortak kod paylaşımı için kullanılır.
- Interface: "Bu yeteneğe sahiptir" (has-a/can-do) ilişkisi için ve davranış sözleşmesi için kullanılır.
2.10 final ve static Anahtar Kelimeleri
Bu iki anahtar kelime, üyelerin (değişken, metot, sınıf) davranışlarını değiştirir.
final (Son / Değişmez)
- Değişken: Değeri bir kere atandıktan sonra değiştirilemez (Sabit). Genellikle `static final` ile birlikte kullanılır.
- Metot: Alt sınıflarda ezilemez (override edilemez).
- Sınıf: Alt sınıflar tarafından miras alınamaz. (Örn: Java'daki `String` sınıfı `final`'dır).
static (Sınıfa Ait / Paylaşılan)
- Alan (Değişken): Bu alanın kopyası, sınıfın tüm nesneleri arasında paylaşılır. Nesneye değil, doğrudan sınıfa aittir.
- Metot: Nesne oluşturmaya gerek kalmadan, doğrudan sınıf adıyla çağrılabilir (`SinifAdi.metot()`). `static` metotlar, `static` olmayan alanlara erişemezler.
- Kullanım: Yardımcı (utility) metotlar (`Math.random()`) ve sabitler (`PI` gibi) için idealdir.
2.11 Enum Yapısı (Sabit Değer Kümeleri)
Enum (Numaralandırma), birbiriyle ilişkili, değişmez (sabit) değerler kümesini tanımlamak için kullanılır. Kodun okunabilirliğini ve tip güvenliğini artırır.
Kullanım Alanları:
Haftanın günleri, aylar, renkler, sabit durum kodları gibi sınırlı sayıda olası değerin olduğu durumlarda kullanılır.
Örnek Tanımlama:
public enum HaftaninGunleri {
PAZARTESI, SALI, CARSAMBA, PERSEMBE, CUMA, CUMARTESI, PAZAR
}
// Kullanım:
HaftaninGunleri bugun = HaftaninGunleri.CUMA;
Avantajı: Yanlışlıkla `String gun = "Pazartesi"` yerine `String gun = "PazarTesi"` gibi bir yazım hatası yapmanızı engeller, çünkü sadece tanımlanmış değerleri kabul eder.
2.12 İç (Inner) Sınıflar
Bir sınıfın, başka bir sınıfın içinde tanımlanması durumudur. İç sınıflar, dış sınıfla mantıksal olarak çok yakın ilişki içindeki işlevleri gruplamak için kullanılır.
En Önemli Özellik:
İç sınıflar, dış sınıflarının **`private`** üyelerine bile doğrudan erişebilirler.
3 Temel İç Sınıf Türü:
- Normal İç Sınıf (Member Inner Class): Dış sınıfa bağlıdır. Dış sınıfın nesnesi olmadan oluşturulamaz.
- Yerel İç Sınıf (Local Inner Class): Bir metodun içinde tanımlanır. Sadece o metot içinden erişilebilir.
- Anonim İç Sınıf (Anonymous Inner Class): Sınıf adı olmayan, tek bir defalık kullanım için anında oluşturulan sınıflardır (Genellikle Interface veya Abstract sınıfları uygulamak için kullanılır).
Ayrıca, dış sınıftan bağımsız olarak oluşturulabilen Static İç Sınıflar da mevcuttur.
3.1 ArrayList ve LinkedList (Listeler)
Dizilerin (Arrays) aksine, **dinamik olarak büyüyüp küçülebilen** veri yapılarıdır. Her ikisi de `List` Interface'ini uygular ve verileri **sırayla** tutar (index'e göre erişim mümkündür).
ArrayList (Dinlenik Dizi)
- Yapı: Arka planda dinamik boyutlu bir dizi kullanır.
- Performans: Index'e göre rastgele erişim (`get(index)`) çok hızlıdır ($O(1)$).
- Dezavantaj: Ortasına veya başına eleman ekleme/silme yavaştır ($O(n)$), çünkü tüm elemanların kaydırılması gerekir.
- Ne zaman kullanılır? Verileri sık sık okuyorsanız, ancak nadiren eklerseniz veya silerseniz.
LinkedList (Bağlantılı Liste)
- Yapı: Her eleman (node), kendinden önceki ve sonraki elemanın adresini (bağlantısını) tutar (Çift Bağlantılı Liste).
- Performans: Başından veya sonundan eleman ekleme/silme çok hızlıdır ($O(1)$). Sadece bağlantıları değiştirmek yeterlidir.
- Dezavantaj: Index'e göre erişim yavaştır ($O(n)$), çünkü istenen elemana ulaşana kadar tüm listede gezinmek gerekir.
- Ne zaman kullanılır? Verilere sık sık liste başından veya sonundan ekleme/silme yapılıyorsa.
// Tanımlama (Generics ile tip belirtmek zorunludur!)
ArrayList<String> isimler = new ArrayList<>();
isimler.add("Ahmet");
isimler.add(0, "Ayşe"); // Ortaya ekleme
System.out.println(isimler.get(1)); // Ahmet
3.2 HashMap ve TreeMap (Haritalar/Map)
Map, verileri anahtar (Key) ve değer (Value) çiftleri halinde saklayan koleksiyonlardır. Tıpkı bir sözlük gibi; bir kelimeye (anahtar) bakıp anlamını (değer) bulursunuz. Anahtarlar benzersiz olmalıdır.
HashMap (En Hızlı Harita)
- Sıralama: Veriyi sırasız olarak tutar. Hangi sırada eklediğiniz önemli değildir.
- Performans: Ekleme, silme ve arama işlemleri inanılmaz hızlıdır ($O(1)$) (genellikle). Bunu sağlamak için hash kodlarını kullanır.
- Özellik: Bir tane null anahtar kabul eder ve birden fazla null değer kabul eder.
HashMap<Integer, String> ogrenciListesi = new HashMap<>();
ogrenciListesi.put(101, "Ali"); // Anahtar: 101, Değer: "Ali"
ogrenciListesi.put(102, "Veli");
System.out.println(ogrenciListesi.get(101)); // Ali
TreeMap (Sıralı Harita)
- Sıralama: Veriyi, anahtarlarına göre doğal sıralı (alfabetik veya sayısal olarak) veya bir `Comparator` ile belirlenen sıraya göre tutar.
- Performans: İşlemler biraz daha yavaştır ($O(\log n)$), çünkü sürekli sıralamayı korumak için Kırmızı-Siyah Ağaç (Red-Black Tree) veri yapısını kullanır.
- Özellik:mNull anahtar kabul etmez. Sıralama için anahtarın her zaman bir değere sahip olması gerekir.
Ne zaman kullanılır? Eğer verileri eklediğiniz sırada değil, belirli bir anahtar sırasına göre işlemek zorundaysanız `TreeMap` kullanın. Aksi halde, hız için `HashMap` tercih edilir.
3.3 HashSet ve TreeSet (Kümeler/Set)
Set, matematikteki küme mantığına benzer. En önemli özelliği, yinelenen (duplicate) elemanlara izin vermemesidir. Sadece benzersiz elemanları saklar. Listelerdeki gibi index'e göre erişim (sıralama) garantisi yoktur.
HashSet (Hızlı ve Sırasız Küme)
- Sıralama: Veriyi sırasız tutar. Ekleme sırası korunmaz.
- Performans: Eleman ekleme, silme ve arama işlemleri en hızlısıdır ($O(1)$). Tıpkı `HashMap` gibi hash kodlarını kullanır.
- Ne zaman kullanılır? Bir veri kümesindeki benzersiz elemanları hızlıca toplamanız gerektiğinde.
TreeSet (Sıralı Küme)
- Sıralama: Elemanları doğal sıralı veya özel bir `Comparator` sırasına göre tutar (Örn: Alfabetik veya sayısal küçükten büyüğe).
- Performans: İşlemler yavaştır ($O(\log n)$), çünkü her eklemede sıralamayı korumak için Kırmızı-Siyah Ağaç kullanır.
- Ne zaman kullanılır? Hem benzersiz elemanlara ihtiyacınız varsa hem de bu elemanların her zaman sıralı olmasını istiyorsanız.
HashSet<String> plakalar = new HashSet<>();
plakalar.add("34"); // Eklendi
plakalar.add("06"); // Eklendi
plakalar.add("34"); // Tekrar eklenmez! Plaka sayısı 2 kalır.
System.out.println(plakalar.size()); // 2
3.4 Queue ve Deque (Kuyruklar)
Queue (Kuyruk), elemanları genellikle FIFO (First-In, First-Out - İlk Giren, İlk Çıkar) prensibine göre işlemek için tasarlanmış yapılardır. Gerçek hayattaki banka kuyruğu gibidir.
Queue (Standart Kuyruk)
- Temel Metotlar:
- `add()` / `offer()`: Eleman ekler.
- `remove()` / `poll()`: Kuyruğun başındaki elemanı alır ve kuyruktan çıkarır.
- `element()` / `peek()`: Kuyruğun başındaki elemana bakar, ama kuyruktan çıkarmaz.
- Uygulaması: Genellikle `LinkedList` veya `PriorityQueue` sınıfları kullanılarak uygulanır.
Deque (Double Ended Queue - Çift Uçlu Kuyruk)
Deque (okunuşu "deck"), hem kuyruk hem de yığın (Stack - LIFO) gibi davranabilen daha esnek bir yapıdır. Yani, elemanları hem başından hem de sonundan ekleyip çıkarabilirsiniz.
- Kullanım Alanı: Hem FIFO (Kuyruk) hem de LIFO (Stack) işlemlerini tek bir yapıda yapmak istendiğinde kullanılır. (Örn: Geri alma/ileri alma (undo/redo) sistemleri).
- Uygulaması: Genellikle `ArrayDeque` veya `LinkedList` sınıfları kullanılır.
Örnek (Kuyruk):
Queue<String> isSirasi = new LinkedList<>();
isSirasi.add("A"); // A girdi
isSirasi.add("B"); // B girdi
isSirasi.poll(); // A çıktı (İlk Giren, İlk Çıkar)
3.5 Iterator ve ListIterator
Iterator (Tekrarlayıcı), bir koleksiyonun (List, Set veya Map) elemanları arasında güvenli bir şekilde gezinmek ve gerektiğinde elemanları silmek için kullanılan bir arayüzdür. Koleksiyonların iç yapısını bilmeden onlara erişim sağlar.
1. Iterator
- Yön: Sadece ileri doğru (tek yönlü) hareket edebilir.
- Kullanım: Tüm `Collection` tiplerinde (List, Set, Queue) kullanılır.
- Güvenlik: Gezinme sırasında eleman silmek için güvenli bir yöntem sağlar (`iterator.remove()`). Eğer bir döngüde (örneğin `for-each` döngüsünde) eleman silmeye çalışırsanız `ConcurrentModificationException` hatası alabilirsiniz.
List<String> veriler = new ArrayList<>();
// ... verileri doldur ...
Iterator<String> it = veriler.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("silinecek")) {
it.remove(); // Güvenli silme
}
}
2. ListIterator
`ListIterator`, `Iterator`'a göre daha gelişmiştir ve sadece `List` (ArrayList, LinkedList vb.) sınıflarında kullanılır.
- Yön: Hem ileri (`hasNext()`, `next()`) hem de geri (`hasPrevious()`, `previous()`) doğru hareket edebilir.
- Ek Özellik: Gezinme sırasında elemanları ekleyebilir (`add()`) ve değiştirebilir (`set()`).
3.6 Comparable ve Comparator (Sıralama Mekanizmaları)
Koleksiyonlardaki özel nesnelerimizi (Örn: `Ogrenci`, `Urun`) Java'nın otomatik olarak sıralayabilmesi için bu iki arayüz kullanılır.
1. Comparable (Doğal Sıralama)
Eğer bir sınıf, kendi doğal sıralama kuralını (Örn: Öğrenci numarasını küçükten büyüğe) tanımlamak istiyorsa bu interface'i `implements` etmelidir. Tek bir metot içerir:
compareTo(T obj):- Negatif sayı dönerse: Bu nesne, karşıdaki nesneden önce gelir.
- Sıfır dönerse: İkisi eşittir.
- Pozitif sayı dönerse: Bu nesne, karşıdaki nesneden sonra gelir.
2. Comparator (Özel/Alternatif Sıralama)
Eğer bir nesneyi doğal sıralama dışında (Örn: Öğrenci adı alfabetik, puanı büyükten küçüğe) veya kaynak koda erişemediğimiz bir sınıfı sıralamak istiyorsak, ayrı bir `Comparator` sınıfı yazarız. Birden fazla sıralama kuralı tanımlamak için idealdir.
compare(T obj1, T obj2): İki nesneyi karşılaştırır ve sıralama kuralına göre negatif, sıfır veya pozitif bir sayı döner.
Kullanım: `Collections.sort(liste, new ComparatorAdi());` şeklinde veya `TreeMap/TreeSet` tanımlarken kullanılır.
3.7 Generics (Jenerikler)
Generics, bir sınıfı, interface'i veya metodu, üzerinde işlem yapacağı veri tipini belirtmeden tasarlamamızı sağlar. Bu, kodu daha esnek, tekrar kullanılabilir ve en önemlisi tip güvenli yapar.
Neden Generics Kullanmalıyız? (Tip Güvenliği)
Java'nın eski koleksiyonları her tür nesneyi saklayabilirdi (`List list = new ArrayList();`). Ancak bu, çalışma zamanında (runtime) yanlış tipte bir nesne eklenirse programın çökme riskini doğuruyordu (Bkz: `ClassCastException`).
Generics, bu kontrolü derleme zamanına (compile time) taşır. Yani yanlış tipte bir şey eklemeye çalışırsanız, programı çalıştırmadan önce hata alırsınız.
Örnek:
// Generics KULLANILMAZSA (Güvensiz)
List list = new ArrayList();
list.add("Merhaba");
list.add(123); // Hata yok, ama riskli
String s = (String) list.get(1); // RUNTIME (Çalışma anı) Hatası: ClassCastException
// Generics KULLANILIRSA (Güvenli)
List<String> list = new ArrayList<>(); // Sadece String kabul et
// list.add(123); // DERLEME HATASI! (Compiler seni durdurur)
Generic Sınıf Tanımlama
Kendi sınıflarınızı tasarlarken de Generic tip parametreleri (`
public class Kutu<T> { // T: Tip (Type) anlamında
private T icerik;
public void icerigiAyarla(T yeniIcerik) {
this.icerik = yeniIcerik;
}
public T icerigiGetir() {
return icerik;
}
}
Artık bu `Kutu` sınıfını, oluştururken istediğiniz tipte kullanabilirsiniz (Örn: `Kutu
4.1 Exception Handling (Hata Yönetimi)
Programın normal akışını bozan, çalışma zamanında (runtime) ortaya çıkan olaylara Exception (İstisna) denir. Hata yönetimi, programın bu tür olaylarda çökmesini engeller.
Try-Catch Bloğu
- `try`: Hata verebilecek kod buraya yazılır.
- `catch(Exception e)`: `try` bloğunda bir hata oluşursa, bu blok çalışır. Program bu hatayı yakalar ve ne yapması gerektiğini bilir.
- `finally`: Hata olsun veya olmasın, her zaman çalıştırılması gereken kodlar buraya yazılır (Örn: Açılan bir dosyayı veya veritabanı bağlantısını kapatmak).
throw ve throws
- `throw`: Bir geliştiricinin, metot içerisinde kural dışı bir durum oluştuğunda bilinçli olarak yeni bir istisna fırlatmasını sağlar. (Örn: `if (yas < 18) throw new IllegalArgumentException();`)
- `throws`: Bir metodun imzasında (tanımında), bu metodun çalışırken bir istisna fırlatabileceğini bildirir. (Derleyici, bu istisnaların `try-catch` ile yakalanmasını veya daha yukarıya atılmasını zorunlu kılar).
4.2 Dosya İşlemleri (File I/O)
Dosyalardan veri okumak (Input) veya dosyalara veri yazmak (Output) için kullanılır. Java'da bu işlemler `Stream` yapısı üzerinden yapılır.
Dosya Okuma (Input):
- `FileReader`: Dosyadan karakter bazında okuma yapar.
- `BufferedReader`: Daha hızlı okuma için `FileReader`'ı sarar (Buffer'a alır). Genellikle satır satır okumak için kullanılır (`readLine()`).
Dosya Yazma (Output):
- `FileWriter`: Dosyaya karakter bazında yazma yapar.
- `PrintWriter/BufferedWriter`: Daha hızlı ve kolay yazma metotları sağlar.
Önemli Not: Bu işlemler `Checked Exception` (Kontrollü İstisna) fırlatır, yani mutlaka `try-catch` veya `throws` ile yönetilmelidir.
Try-with-Resources: Java 7 ile gelen bu yapı, kaynakları (dosya/bağlantı) otomatik olarak kapatmayı garanti eder.
try (BufferedReader br = new BufferedReader(new FileReader("veri.txt"))) {
String satir = br.readLine();
System.out.println(satir);
} catch (IOException e) {
e.printStackTrace();
}
4.3 Stream API (Java 8 ve Sonrası)
**Stream API**, koleksiyonlardaki (List, Set vb.) verileri, veritabanı sorgularına benzer şekilde, bildirimsel (declarative) bir yöntemle işlemek için tasarlanmıştır. Programın neyi yapacağını söyleriz, nasıl yapacağını değil.
Temel Özellikler:
- Fonksiyonel: Lambda ifadeleri ile birlikte kullanılır.
- Immutable (Değişmez): Orijinal veri kaynağını (koleksiyonu) değiştirmez. Her zaman yeni bir sonuç döndürür.
- Lazy (Tembel): İşlemler sadece nihai bir metot çağrıldığında (Örn: `collect`, `forEach`) çalıştırılır.
Akış (Stream) Aşamaları:
- Oluşturma: Koleksiyondan akış yaratma (`list.stream()`).
- Ara İşlemler (Intermediate Ops): Filtreleme (`filter`), eşleme (`map`), sıralama (`sorted`). Bu işlemler akışı değiştirir, ancak nihai sonuç vermez. Zincirleme yapılabilir.
- Nihai İşlemler (Terminal Ops): Akışı sonlandırır ve bir sonuç üretir (`forEach`, `collect`, `count`, `reduce`).
list.stream()
.filter(s -> s.startsWith("A")) // Ara işlem
.map(String::toUpperCase) // Ara işlem
.sorted()
.forEach(System.out::println); // Nihai işlem
4.4 Lambda İfadeleri (Ok İşaretli Fonksiyonlar)
Lambda İfadeleri (Java 8), özellikle tek bir metodu olan arayüzlerin (Functional Interface) kolayca ve kısa yoldan uygulanmasını sağlayan isimsiz fonksiyonlardır.
Karmaşık Anonymous Inner Class yapısını, okunması çok kolay ve kısa bir söz dizimine dönüştürür.
Söz Dizimi:
Temel yapı: `(parametreler) -> { gövde }`
- Parametresiz: `() -> System.out.println("Merhaba")`
- Tek Parametreli: `s -> s.length()` (Parantezler isteğe bağlıdır)
- İki Parametreli: `(a, b) -> a + b`
Örnek (Geleneksel vs Lambda):
// Geleneksel Yöntem (Anonymous Inner Class)
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Koşuyor");
}
}).start();
// Lambda İfadesi (Aynı iş, çok daha kısa)
new Thread(() -> System.out.println("Koşuyor")).start();
4.5 Functional Interface’ler
Functional Interface (Fonksiyonel Arayüz), sadece bir tane soyut metot içeren arayüzlerdir (Interface). Java 8'de tanıtıldı ve Lambda ifadelerinin temelini oluşturur.
@FunctionalInterface
Bu anotasyon, bir arayüzün zorunlu olarak sadece tek bir soyut metot içermesini sağlar. Birden fazla eklerseniz derleme hatası alırsınız.
Java'nın Hazır Fonksiyonel Arayüzleri:
Lambda ile en sık kullanılan ve Java'nın içinde gelen 4 temel arayüz vardır (Stream API'de çok kullanılır):
- `Predicate
`: Girdi alır, `boolean` (True/False) döndürür. (Filtreleme için kullanılır) - `Consumer
`: Girdi alır, bir şey döndürmez (`void`). (Ekrana yazdırma gibi işlemler için) - `Function
`: Girdi alır (`T`), çıktı döndürür (`R`). (Bir tipi başka bir tipe çevirmek için, `map` işleminde) - **`Supplier
`:** Girdi almaz, çıktı döndürür. (Yeni nesne üretmek gibi işlemler için)
Bu arayüzler, kodda tekrarlayan desenleri engeller ve kodu daha temiz hale getirir.
4.6 Date ve Time API (Java 8)
Java'nın eski tarih/saat sınıfları (`java.util.Date`, `Calendar`) karmaşıktı. Java 8 ile birlikte gelen **`java.time`** paketi, daha kolay, thread-safe (iş parçacığı güvenli) ve değişmez (Immutable) tarih/saat nesneleri sunar.
Temel Sınıflar:
- `LocalDate`: Sadece tarihi (yıl, ay, gün) tutar. Saati dikkate almaz.
- `LocalTime`: Sadece saati (saat, dakika, saniye) tutar. Tarihi dikkate almaz.
- `LocalDateTime`: Hem tarihi hem de saati bir arada tutar.
- `ZonedDateTime`: Bölge ve saat dilimi (Time Zone) bilgisi içeren tarih/saat.
Örnek Kullanım:
LocalDate bugun = LocalDate.now();
LocalTime simdi = LocalTime.now();
// Tarih Manipülasyonu (Immutable olduğu için yeni nesne döner)
LocalDate yarin = bugun.plusDays(1);
System.out.println(yarin);
4.7 Multi-threading (Çoklu İş Parçacığı)
Programın aynı anda (eşzamanlı olarak) birden fazla iş yapabilme yeteneğidir. Her iş parçacığına Thread denir. Multi-threading, uygulamaların daha hızlı ve yanıt veren hale gelmesini sağlar (Örn: Bir program hem indirme yaparken hem de kullanıcı arayüzünü güncelleyebilir).
Thread Oluşturma Yolları:
- `Thread` sınıfını miras almak (`extends`): Sınıfın `run()` metodunu ezeriz (`override`).
- `Runnable` arayüzünü uygulamak (`implements`): Bu tercih edilen yöntemdir, çünkü Java'da bir sınıf sadece tek bir sınıfı miras alabilir.
Temel Metotlar:
- `start()`: Thread'i başlatır ve `run()` metodunun çalışmasını sağlar (Asenkron).
- `run()`: Thread'in çalıştıracağı kodun bulunduğu metottur. (Direkt çağrılırsa senkron çalışır).
- `sleep(millis)`: Thread'i belirtilen süre kadar duraklatır.
- `join()`: Bir Thread'in bitmesini bekler.
4.8 Synchronization (Senkronizasyon)
Aynı anda birden fazla Thread'in (iş parçacığı) ortak bir kaynağa (Örn: bir değişkenin değeri) erişmeye çalıştığında ortaya çıkan veri bozulması sorununu çözmek için kullanılır.
Senkronizasyon (Kilitleme)
Senkronizasyon, bir kaynağın (metot veya kod bloğu) aynı anda sadece bir Thread tarafından kullanılabilmesini garanti eder. Diğer Thread'ler sıraya girer ve kilit açılana kadar bekler.
- `synchronized` Metotlar: Metodun tamamını kilitler. Bir Thread bu metodu çalıştırıyorsa, aynı nesne üzerindeki diğer `synchronized` metotlar için de kilit devreye girer.
- `synchronized` Bloklar: Kilitlenecek kısmı daha hassas belirlemek için kullanılır. Daha verimli bir yöntemdir. (Örn: `synchronized (this) { ... }` veya `synchronized (SinifAdi.class) { ... }`)
Risk: Aşırı senkronizasyon, programı yavaşlatabilir ve Deadlock (kilitlenme) sorununa yol açabilir.
4.9 Reflection API (Yansıtma)
Reflection (Yansıtma), bir programın kendi yapısını (sınıflarını, metotlarını, alanlarını) çalışma zamanında (Runtime) inceleyebilmesi, değiştirebilmesi ve kullanabilmesidir.
Ne İşe Yarar?
- Bir sınıfın adını bilmeden bile o sınıftaki tüm metotları ve değişkenleri listeleyebilir.
- `private` olarak tanımlanmış metotları ve alanları bile değiştirebilir.
- Kullanım Alanları: Test framework'leri (JUnit), Dependency Injection (Spring), ORM araçları (Hibernate) gibi karmaşık kütüphanelerin temelinde yer alır.
Dezavantajları: Kodun hızını yavaşlatır, tip güvenliğini (type safety) azaltır ve güvenlik riskleri yaratabilir. Bu yüzden sadece gerektiğinde kullanılmalıdır.
Class> sinif = Class.forName("com.ornek.Kullanici");
Method[] metotlar = sinif.getDeclaredMethods();
// Metotları döngüye alıp adlarını yazdırabiliriz.
4.10 Annotations (Dipnotlar / Açıklamalar)
Annotations, kodun içine eklenen, derleyiciye, JVM'e veya diğer araçlara özel talimatlar veren meta-veri etiketleridir. Kodun kendisi değil, kod hakkındaki bilgidir.
Temel Annotations:
- `@Override`: Bir metodun, üst sınıftaki bir metodu ezeceğini belirtir. (En sık kullanılan).
- `@Deprecated`: Bu metodun veya sınıfın artık eski olduğunu ve kullanılmaması gerektiğini bildirir.
- `@SuppressWarnings`: Derleyicinin belirli uyarıları görmezden gelmesini sağlar (Dikkatli kullanılmalıdır).
Özel (Custom) Annotation'lar:
Kendi Annotation'larınızı tanımlayabilirsiniz. (Örn: `@Tarih(yazar="Ali")`). Bu Annotation'lar Reflection API ile okunur ve Spring veya Hibernate gibi Framework'ler bu bilgilere göre çalışır.
Tanımlama Örneği:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TestMetodu {
int sure() default 10;
}
5.1 JDBC (Java Database Connectivity)
JDBC, Java programları ile veritabanları (MySQL, PostgreSQL, Oracle vb.) arasında standart bir bağlantı ve etkileşim sağlamak için kullanılan bir API (Arayüz) setidir. SQL komutlarını veritabanına göndermeyi sağlar.
Temel Adımlar:
- Driver Yükleme: Kullanılacak veritabanına ait JDBC sürücüsünü (Driver) classpath'e eklemek.
- Bağlantı Kurma: `DriverManager.getConnection()` metodu ile veritabanı ile bir oturum (`Connection`) açmak.
- Komut Oluşturma: SQL sorgularını tutacak `Statement` veya `PreparedStatement` nesnesi yaratmak.
- Komut Çalıştırma: Sorguyu çalıştırmak (`executeUpdate` veya `executeQuery`).
- Sonuç İşleme: Sorgu sonuçlarını (`ResultSet`) okumak.
- Bağlantıyı Kapatma: Kaynakları serbest bırakmak için `Connection`, `Statement` ve `ResultSet` nesnelerini kapatmak.
PreparedStatement: Güvenlik ve performans için, kullanıcı girdisi içeren SQL sorgularında `PreparedStatement` kullanılması şiddetle tavsiye edilir (SQL Injection saldırılarını önler).
5.2 JavaFX / Swing (Masaüstü Uygulamaları)
Java, grafiksel kullanıcı arayüzü (GUI) geliştirmek için iki ana kütüphane sunar:
Swing (Geleneksel ve Hafif)
- Özellik: Java'nın eski ve platformdan bağımsız (lightweight) GUI kütüphanesidir. Tüm bileşenleri Java koduyla çizilir.
- Kullanım: Hızlı ve basit masaüstü uygulamaları için hala kullanılabilir.
JavaFX (Modern ve Zengin)
- Özellik: Swing'in modern halefidir. Zengin arayüzler, 3D grafikler, medya oynatma gibi gelişmiş özellikler sunar. CSS ile stillenebilir ve FXML (XML tabanlı arayüz tanımı) kullanarak kod ile arayüz tasarımını ayırmayı sağlar.
- Kullanım: Güncel ve profesyonel görünümlü masaüstü uygulamaları için tercih edilir.
5.3 Servlet & JSP (Web Programlama Temelleri)
Java'nın web sunucuları üzerinde dinamik web içeriği oluşturmak için kullandığı temel teknolojilerdir. Genellikle Tomcat, Jetty gibi bir Servlet/JSP konteyneri (Web sunucusu) içinde çalışırlar.
Servlet (Java Kodu Ağırlıklı)
- Rolü: Sunucu tarafındaki iş mantığını yürütür. Gelen HTTP isteklerini (`doGet`, `doPost`) işler, veritabanına erişir ve cevabı oluşturur.
- Yapısı: Saf Java sınıfıdır. Çıktı olarak HTML'i Java kodu içinde yazar (`response.getWriter().println("...");`), bu da HTML üretimi açısından zordur.
JSP (JavaServer Pages - HTML Ağırlıklı)
- Rolü: HTML, CSS, JavaScript kodunun içine gömülü Java kodları (Scriptlet) ile dinamik içerik oluşturur. Görünüm (View) katmanı için daha uygundur.
- İşleyiş: Web sunucusuna ulaştığında önce Servlet'e dönüştürülür ve ardından çalıştırılır.
Bu yapılar temel olsa da, modern Java web geliştirmede yerlerini Spring Boot, Jakarta EE gibi framework'lere bırakmıştır.
5.4 Spring Framework (Spring Boot Dahil)
Spring, modern Java uygulamaları (kurumsal, web, mikroservis) geliştirmek için dünyanın en popüler framework'üdür. Java EE'nin karmaşıklığını ortadan kaldırır ve hafif, hızlı çözümler sunar.
Temel Prensipler:
- IoC (Inversion of Control - Kontrolün Tersine Çevrilmesi): Nesnelerin yaratılması ve yönetilmesi sorumluluğunu (lifecycle) geliştiriciden alıp Framework'e verir.
- DI (Dependency Injection - Bağımlılık Enjeksiyonu): Bir nesnenin, ihtiyaç duyduğu diğer nesneleri (bağımlılıkları) dışarıdan almasıdır (genellikle `@Autowired` veya Constructor Injection ile).
Spring Boot (Hızlandırıcı)
Spring Boot, Spring Framework'ü kullanmayı çok kolaylaştıran bir projedir. Amacı, kurumsal uygulamaların hızlı, standart ve çok az ayar (configuration) ile çalışmasını sağlamaktır.
- Özellik: Otomatik Konfigürasyon, Gömülü Sunucular (Tomcat/Jetty), Üretim için hazır özellikler (Actuator).
- Fayda: Geliştiricilerin saatler süren konfigürasyon işini dakikalara indirir.
5.5 Maven ve Gradle (Build/Derleme Araçları)
Build araçları, Java projelerinin yaşam döngüsünü (kod derleme, test çalıştırma, bağımlılık yönetimi, paketleme/JAR oluşturma) otomatik hale getiren araçlardır. Profesyonel Java projelerinin olmazsa olmazıdır.
Maven
- Yapı: XML tabanlı konfigürasyon (`pom.xml`).
- Özellik: Convention over Configuration (Konfigürasyondan çok kural). Standart bir klasör yapısı izler. Bağımlılık yönetiminde (`dependency`) en yaygın kullanılan araçtır.
Gradle
- Yapı: Groovy veya Kotlin tabanlı konfigürasyon (`build.gradle`).
- Özellik: Maven'a göre daha esnek ve daha hızlıdır. Groovy/Kotlin sayesinde daha karmaşık ve özel build senaryolarını kod ile yazmaya imkan tanır. Android geliştirmede standart araçtır.
Bu araçlar olmadan büyük projelerde yüzlerce harici kütüphaneyi yönetmek imkansızdır.
5.6 REST API Geliştirme
**REST (Representational State Transfer)**, web servisleri (API'ler) oluşturmak için kullanılan bir mimari stildir. Günümüzdeki web sitelerinin, mobil uygulamaların ve mikroservislerin birbiriyle konuşma şeklidir.
REST'in Temelleri:
- Resources (Kaynaklar): Her şeye bir kaynak olarak bakılır (Örn: `/kullanicilar`, `/urunler/123`).
- HTTP Metotları: Kaynaklar üzerinde işlem yapmak için standart HTTP metotları kullanılır:
- `GET`: Kaynakları Oku/Al.
- `POST`: Yeni bir kaynak Oluştur.
- `PUT` / `PATCH`: Bir kaynağı Güncelle.
- `DELETE`: Bir kaynağı Sil.
- Stateless (Durumsuz): Sunucu, iki istek arasında istemci hakkında herhangi bir bilgi tutmaz. Her istek kendi içinde bağımsızdır.
Java'da REST API geliştirmek için en yaygın olarak **Spring Boot**'un alt projeleri olan **Spring Web** veya **Jersey/JAX-RS** kullanılır.
// Spring Boot REST API Örneği
@RestController
@RequestMapping("/api/urunler")
public class UrunController {
@GetMapping("/{id}")
public Urun getUrun(@PathVariable Long id) {
// Veritabanından ürünü getir
}
}
6. Yardımcı Konulara Giriş
Paketleme ve Test (JUnit) gibi profesyonel geliştirme araçlarını inceleyeceğiz.