← Kursa Dön
📄 Text · 12 min

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:

  1. Field'ları private yap → dışarıdan erişimi engelle

  2. public getter/setter metotları yaz → kontrollü erişim sağla

Erişim Belirleyicileri (Access Modifiers)

Java'da 4 erişim seviyesi var:

ModifierAynı SınıfAynı PaketAlt SınıfHer 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 private

public

Her yerden erişilebilir. Hiçbir kısıtlama yok.

class Ogrenci {
    public String isim;
}

Ogrenci ali = new Ogrenci();
ali.isim = "Ali";  // sorun yok

default (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());   // 20

Getter/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 private

  • Her field için get + FieldAdı ve set + FieldAdı

  • boolean field'larda getter is ile 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'larprivateVeri güvenliği
Getter/SetterpublicKontrollü erişim
Yardımcı metotlarprivateİç detay, dışarıyı ilgilendirmez
API metotlarıpublicDışarıdan kullanılacak
Kalıtıma açıkprotectedAlt 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şmedi

String 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 → protectedpublic (dar → geniş)

  • Getter değer okur, setter değer yazar — setter'da doğrulama yapılabilir

  • JavaBean convention: getFieldAdi(), setFieldAdi(), boolean için isFieldAdi()

  • 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