Encapsulation ve Erişim Belirleyiciler
Giriş
Önceki derslerde field'lara direkt erişiyorduk: ali.yas = 20, ali.isim = "Ali". Çalışıyor, sorun yok gibi görünüyor. Ama ya birisi şunu yazarsa?
ali.yas = -5; // negatif yaş?
ali.notOrtalamasi = 99.9; // 4.0 üzerinden 99.9?
hesap.bakiye = 999999999; // bakiyeyi direkt değiştirmek?Hiçbir kontrol yok. Mantıksız değerler atanabiliyor. Veriler korumasız.
Encapsulation (kapsülleme), verileri gizleyip dışarıdan erişimi kontrollü hale getirme prensibidir. OOP'un 4 temel prensibinden biri ve belki de en önemlisi.
Encapsulation Nedir?
Bir ilaç kapsülü düşün. İlacın içindeki kimyasallara direkt dokunamazsın — kapsül onları koruyor. Doğru dozda, doğru şekilde alman sağlanıyor. Encapsulation da aynı şeyi yapıyor: verileri kapsül içine koyuyor ve dışarıdan ancak kontrollü metotlarla erişilmesini sağlıyor.
İki adımda çalışır:
Field'ları
privateyap → dışarıdan erişimi engellepublicgetter/setter metotları yaz → kontrollü erişim sağla
Erişim Belirleyicileri (Access Modifiers)
Java'da 4 erişim seviyesi var:
| Modifier | Aynı Sınıf | Aynı Paket | Alt Sınıf | Her Yerden |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
| (default) | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
private
Sadece aynı sınıf içinden erişilebilir. Dışarıdan tamamen gizli.
class Ogrenci {
private String isim;
private int yas;
}
Ogrenci ali = new Ogrenci();
ali.isim = "Ali"; // DERLEME HATASI! isim privatepublic
Her yerden erişilebilir. Hiçbir kısıtlama yok.
class Ogrenci {
public String isim;
}
Ogrenci ali = new Ogrenci();
ali.isim = "Ali"; // sorun yokdefault (package-private)
Hiçbir modifier yazmazsan default olur. Aynı paket içinden erişilebilir.
class Ogrenci {
String isim; // default — aynı paketten erişilebilir
}protected
Aynı paket + alt sınıflardan erişilebilir. Kalıtım konusunda detaylı göreceğiz.
class Hayvan {
protected String isim; // alt sınıflar erişebilir
}💡 İpucu: Genel kural: field'lar private, metotlar public. Spesifik durumlarda protected veya default kullanılır. Şüphede kalırsan private yap — sonra gerekirse açarsın. Kapamak açmaktan zordur.
Getter ve Setter
Field'lar private yapıldıktan sonra dışarıdan nasıl erişeceksin? Getter (okuma) ve setter (yazma) metotları ile.
class Ogrenci {
private String isim;
private int yas;
// Getter — değeri döndürür
public String getIsim() {
return isim;
}
// Setter — değer atar
public void setIsim(String isim) {
this.isim = isim;
}
// Getter
public int getYas() {
return yas;
}
// Setter — doğrulama ile
public void setYas(int yas) {
if (yas < 0 || yas > 150) {
System.out.println("Geçersiz yaş: " + yas);
return;
}
this.yas = yas;
}
}Ogrenci ali = new Ogrenci();
ali.setIsim("Ali");
ali.setYas(20);
ali.setYas(-5); // "Geçersiz yaş: -5" — atama yapılmaz
System.out.println(ali.getIsim()); // Ali
System.out.println(ali.getYas()); // 20Getter/setter sayesinde:
Doğrulama yapabilirsin (negatif yaş engelleme)
Salt okunur field yapabilirsin (getter var, setter yok)
Loglama ekleyebilirsin (her değişiklikte log tut)
Hesaplama yapabilirsin (getter'da farklı değer döndür)
Neden Gizleme?
"Neden bu kadar zahmete gireyim, direkt erişsem olmaz mı?" diye düşünebilirsin. İşte nedenler:
1. Veri Doğrulama
// Encapsulation olmadan — KÖTÜ
hesap.bakiye = -1000000; // negatif bakiye? Kontrol yok!
// Encapsulation ile — İYİ
public void setBakiye(double bakiye) {
if (bakiye < 0) {
throw new IllegalArgumentException("Bakiye negatif olamaz!");
}
this.bakiye = bakiye;
}2. Değişikliğe Dayanıklılık
Diyelim ki Ogrenci sınıfında isim field'ını ad ve soyad olarak ikiye ayırmak istiyorsun.
// Encapsulation olmadan: KÖTÜ
// ali.isim kullanılan HER YERİ bulup değiştirmen lazım
// Encapsulation ile: İYİ
// Sadece getter/setter'ı güncelle, dışarıdaki kod değişmez
public String getIsim() {
return ad + " " + soyad; // iç yapı değişti ama dışarı aynı
}3. Salt Okunur Field
class Ogrenci {
private final String ogrenciNo;
Ogrenci(String ogrenciNo) {
this.ogrenciNo = ogrenciNo;
}
public String getOgrenciNo() {
return ogrenciNo; // sadece okunabilir
}
// setter YOK — öğrenci numarası değiştirilemez
}4. Hesaplanmış Değerler
class Dikdortgen {
private double en;
private double boy;
// Alan field olarak saklanmıyor, hesaplanıyor
public double getAlan() {
return en * boy;
}
// Çevre de hesaplanıyor
public double getCevre() {
return 2 * (en + boy);
}
}Alan ve çevre field olarak saklanmıyor, getter'da hesaplanıyor. Dışarıdan bakan kişi bunun bir field mı yoksa hesaplama mı olduğunu bilmez — bilmesine de gerek yok.
JavaBean Convention
Java ekosisteminde bir naming convention (isimlendirme kuralı) var. Buna JavaBean standartı denir. Framework'ler (Spring, Hibernate, vs.) bu kurala göre çalışır.
Kurallar:
Field'lar
privateHer field için
get+FieldAdıveset+FieldAdıbooleanfield'larda getterisile başlar
class Kullanici {
private String ad;
private String email;
private int yas;
private boolean aktif;
// String → getAd / setAd
public String getAd() { return ad; }
public void setAd(String ad) { this.ad = ad; }
// String → getEmail / setEmail
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
// int → getYas / setYas
public int getYas() { return yas; }
public void setYas(int yas) { this.yas = yas; }
// boolean → isAktif / setAktif
public boolean isAktif() { return aktif; }
public void setAktif(boolean aktif) { this.aktif = aktif; }
}boolean için getAktif() değil, isAktif() kullanıldığına dikkat et. Bu küçük fark framework'ler için önemli.
⚠️ Dikkat: IDE'ler (IntelliJ, Eclipse, VS Code) getter/setter'ları otomatik üretebilir. Elle yazmana gerek yok. IntelliJ'de Alt+Insert → Generate Getters and Setters.
Encapsulation ile Tam Örnek
class BankaHesabi {
private String sahibi;
private String hesapNo;
private double bakiye;
// Constructor
public BankaHesabi(String sahibi, String hesapNo, double baslangicBakiye) {
this.sahibi = sahibi;
this.hesapNo = hesapNo;
if (baslangicBakiye >= 0) {
this.bakiye = baslangicBakiye;
}
}
// Getter'lar
public String getSahibi() { return sahibi; }
public String getHesapNo() { return hesapNo; }
public double getBakiye() { return bakiye; }
// Setter — sadece sahibi değiştirilebilir
public void setSahibi(String sahibi) {
if (sahibi != null && !sahibi.isEmpty()) {
this.sahibi = sahibi;
}
}
// hesapNo ve bakiye için setter YOK — direkt değiştirilemez
// İş metotları — bakiye sadece bunlarla değişir
public void paraYatir(double miktar) {
if (miktar > 0) {
bakiye += miktar;
System.out.println(miktar + " TL yatırıldı. Bakiye: " + bakiye);
} else {
System.out.println("Geçersiz miktar!");
}
}
public boolean paraCek(double miktar) {
if (miktar > 0 && miktar <= bakiye) {
bakiye -= miktar;
System.out.println(miktar + " TL çekildi. Bakiye: " + bakiye);
return true;
}
System.out.println("İşlem başarısız!");
return false;
}
public void bilgiGoster() {
System.out.println("Hesap: " + hesapNo + " | Sahip: " + sahibi + " | Bakiye: " + bakiye);
}
}BankaHesabi hesap = new BankaHesabi("Ali Yilmaz", "TR001", 1000);
hesap.paraYatir(500); // 500 TL yatırıldı. Bakiye: 1500.0
hesap.paraCek(200); // 200 TL çekildi. Bakiye: 1300.0
hesap.paraCek(5000); // İşlem başarısız!
hesap.paraYatir(-100); // Geçersiz miktar!
// Direkt erişim engellendi
// hesap.bakiye = 999999; // DERLEME HATASI!
// hesap.hesapNo = "XXX"; // DERLEME HATASI!Bakiye sadece paraYatir() ve paraCek() ile değişebiliyor. Hesap numarası hiç değişemiyor. Veri güvende.
Erişim Belirleyicileri — Neyi Nerede Kullan?
| Ne? | Hangi Modifier? | Neden? |
|---|---|---|
| Field'lar | private | Veri güvenliği |
| Getter/Setter | public | Kontrollü erişim |
| Yardımcı metotlar | private | İç detay, dışarıyı ilgilendirmez |
| API metotları | public | Dışarıdan kullanılacak |
| Kalıtıma açık | protected | Alt sınıflar erişsin |
class SiparisIsleyici {
private ArrayList<String> siparisler = new ArrayList<>();
// Public — dışarıdan çağrılır
public void siparisEkle(String urun) {
if (stokKontrol(urun)) {
siparisler.add(urun);
}
}
// Private — sadece iç kullanım
private boolean stokKontrol(String urun) {
// stok kontrol mantığı
return true;
}
}Immutable Sınıf (Değiştirilemez)
Encapsulation'ın en uç hali: nesne oluşturulduktan sonra hiçbir field'ı değiştirilemez.
class Nokta {
private final int x;
private final int y;
public Nokta(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
// Setter YOK — nesne değiştirilemez
// Yeni değer lazımsa yeni nesne oluştur
public Nokta tasi(int dx, int dy) {
return new Nokta(x + dx, y + dy); // yeni nesne döndür
}
}Nokta p = new Nokta(3, 5);
Nokta p2 = p.tasi(1, 2); // yeni nesne: (4, 7)
// p hâlâ (3, 5) — değişmediString sınıfı da immutable'dır. Bu yüzden str.toUpperCase() orijinal String'i değiştirmez, yeni String döndürür.
Özet
Encapsulation: field'ları gizle (
private), metotlarla kontrollü erişim sağla (public)4 erişim seviyesi:
private→ default →protected→public(dar → geniş)Getter değer okur, setter değer yazar — setter'da doğrulama yapılabilir
JavaBean convention:
getFieldAdi(),setFieldAdi(), boolean içinisFieldAdi()Encapsulation sayesinde veri doğrulama, salt okunur field, hesaplanan değer gibi özellikler eklenir
Genel kural: field'lar private, API metotları public, yardımcı metotlar private
AI Asistan
Sorularını yanıtlamaya hazır