İçeriğe geç

İç İçe Sınıflar — static, Inner, Anonim ve Yerel

Orta 9 dk Çok sık karşılaşılır

Önce şunu oku: final ve static — Kilitli Olan ve Ortak Olan , Erişim Belirleyiciler — Kim Hangi Kapıdan Girer?

30 saniyede özet

Bir sınıfın içine sınıf yazmanın dört yolu var. static iç içe sınıf bağımsızdır; inner sınıf onu kuran nesneye gizli bir ip ile bağlıdır. O ip, uzun yaşayan bir yerde tutulursa dış nesneyi de bırakmaz.

Bir uçurtmayı ipiyle uçurursun. Uçurtma ne kadar uzağa giderse gitsin, ipin öbür ucu hâlâ senin elinde.

Java’da bir sınıfın içine yazılan bazı sınıflar da böyle bir iple doğar. Bu ders o ipi görünür kılıyor.

İp görünmez, ama kutu bırakılamaz.
Adım adım oku
  1. Büyük ve ağır bir kutu: bir hesap nesnesi.
  2. Kutudan küçük bir uçurtma çıkıyor: bir iç nesne.
  3. Uçurtmanın ipi hâlâ kutuya bağlı.
  4. Uçurtmayı tutan biri, farkında olmadan kutuyu da tutuyor.
  1. Bayt: Hesap sınıfının içine küçük bir Hatırlatıcı sınıfı yazdım. Çok düzenli duruyor!

  2. Sen: Ama ekranlar kapandıkça bellek artıyor.

  3. Bayt: Hatırlatıcı zamanlayıcıda duruyor... ve içinde gizli bir şey taşıyor olabilir.

  4. Bayt: Önce iç içe sınıfların dört çeşidine bakalım.

Bir sınıfın içinde sınıf

ÇeşitNerede yazılırDış nesneye bağlı mı?
static iç içe sınıfSınıfın içinde, static ileHayır
inner sınıfBaşka bir sınıfın içinde static olmadan yazılan sınıf. Her nesnesi onu kuran dış nesneye bağlı doğar ve onun alanlarını doğrudan kullanabilir.Dışarıdan dis.new Ic() ile kurulur. Dış nesneyi kullandığı sürece onu gizli bir alanda (derlenmiş kodda this$0) tutar.Sözlükte gör →Sınıfın içinde, static olmadanEvet
Yerel sınıfBir metodun içindeInstance metotta, evet
anonim sınıfAdı olmayan, tek bir ifadenin içinde hem tanımlanıp hem kurulan sınıf. Tek metotlu arayüzler için yerini büyük ölçüde lambda'ya bıraktı.Instance metotta yazılıp dış nesneyi kullanırsa inner sınıf gibi onu tutar. İçindeki this, anonim nesnenin kendisidir.Sözlükte gör →Bir ifadenin içinde, adı yokInstance metotta, evet
Hesap sınıfının içinde static olmayan bir Hatirlatici sınıfı var. Başka bir sınıfta `new Hesap.Hatirlatici()` yazarsan ne olur? Cevabı göster

Derlenmez. Inner sınıfın nesnesi bir hesaba bağlı doğmak zorunda; doğrusu hesap.new Hatirlatici(). static iç içe bir sınıf olsaydı new Hesap.Hatirlatici() çalışırdı.

Kafam karıştı, daha basit anlat

static olan iç sınıf kendi başına yaşar. static olmayan, onu kuran nesneye bağlı doğar.

Hızlı kontrolBaşlangıç

static iç içe sınıf ile inner sınıf arasındaki temel fark nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bu kod derlenir mi, derlenirse ne yazdırır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
Main.java
1class Hesap {
2 private final String iban = "TR01";
3 class Hatirlatici {
4 String metin() { return iban + " için ekstre"; }
5 }
6}
7
8Hesap hesap = new Hesap();
9Hesap.Hatirlatici h = hesap.new Hatirlatici();
10System.out.println(h.metin());
Java 21UTF-8LF

Gizli ip: dış nesneye referans

Inner sınıf, dış sınıfın alanlarını sanki kendi alanlarıymış gibi okuyabilir. Bunu, onu kuran nesneyi gizli bir alanda tutarak yapar: Hesap.this. Bu nesneye dış nesneBir inner sınıf nesnesini kuran ve ona bağlı olan dış sınıf nesnesi. İçeriden Dis.this ile erişilir.Inner nesne yaşadıkça, onu kullandığı sürece dış nesne de yaşar. Heap dump'ta bu bağ this$0 adlı alan olarak görünür.Sözlükte gör → denir.

Anonim ve yerel sınıflar da instance metotta yazılıp dış nesneyi kullanırsa aynı ipi taşır. Lambda ise yalnızca bir alan ya da metot üzerinden this’i kullanırsa onu yakalar.

Aynı iş, iki yazım
Runnable a = () -> System.out.println(this.iban); // captures this: the whole account
String iban = this.iban;
Runnable b = () -> System.out.println(iban); // captures only the String
Java 18 ile gelen küçük değişiklik· istersen atla

Java 18’den beri derleyici, dış nesneyi hiç kullanmayan inner sınıflardan bu gizli alanı çıkarıyor. Dış nesnenin bir alanını ya da metodunu kullanan her inner sınıf ise onu hâlâ tutar.

Hızlı kontrolOrta

Bir inner sınıf nesnesi uzun yaşayan bir zamanlayıcıya veriliyor ve dış sınıfın bir alanını okuyor. Dış nesne için bu ne demek?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Hesap sınıfının instance metodunda yazılan her şeyi, hesabı (this) tutup tutmadığına göre ayır.

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sınıflandırılmamış

Hesabı tutar

Dış nesneye yol var

    Hesabı tutmaz

    Yalnızca bir değer var

      Ne zaman hangisi?

      Varsayılan seçim static iç içe sınıftır: bir Builder, küçük bir yardımcı ya da sınıfa ait bir sonuç tipi. İhtiyacı olan değeri parametre olarak alır.

      Inner sınıf, dış nesneye gerçekten ihtiyaç varsa yazılır: örneğin bir koleksiyonun kendi elemanlarını gezen Iterator’ı. Ömrü dış nesneden uzun olmamalıdır.

      Tek metotlu bir arayüz için anonim sınıf yerine artık lambda yazılır. Yerel sınıf ise seyrek görülür.

      Kafam karıştı, daha basit anlat

      Önce static yaz. Dış nesneye gerçekten ihtiyacın olduğunu görürsen static’i kaldır.

      Hızlı kontrolOrta

      Hesap için bir Builder yazacaksın. Hangi çeşit iç içe sınıf olmalı?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Kendin gör

      Hesap ekranı bir hatırlatıcı üretip uygulama boyunca yaşayan bir zamanlayıcıya veriyor. Sonra ekran kapanıyor ve GC çalışıyor. Hatırlatıcının nasıl yazıldığını seç.

      Hatırlatıcı hesabı bırakıyor mu?

      Tohum 1

      Oynat ya da adımla.

      Hız
      Adım 0

      Şu an ne oldu?

      Bir hesap ekranı açılacak

      Ekran bir hatırlatıcı üretip zamanlayıcıya verecek. Hatırlatıcı şöyle yazıldı: İç sınıf (inner), hesabın alanını okuyor.

      Görevler0/3

      • Ekran kapandığı hâlde bellekte kalan hesabı göraçık

        İpucu

        Dışarıdan görünmeyen bir referans arıyorsun.

      • Hesabı tutan lambda'yı bulaçık

        İpucu

        Lambda bir alanı okursa neyi yakalar?

      • Hesabın toplanmasını sağla, IBAN'ı açıkça vererekaçık

        İpucu

        Dış nesnesi olmayan iç içe sınıf hangisi?

      Olay günlüğü (0)

      Henüz olay yok. Oynat veya adımla.

      1. Varsayılanı oynat. Inner sınıf: ekran kapandı ama hesap ve geçmişi bellekte kaldı.
      2. Anonim sınıfı dene. Aynı gizli ip.
      3. İki lambda’yı karşılaştır. Biri this.iban okuyor, öteki yalnızca IBAN’ın kopyasını.
      4. static iç içe sınıfa geç. Hesap toplanıyor.
      Hızlı kontrolOrta

      Simülatörde iki lambda'dan biri hesabı bellekte tuttu, öteki tutmadı. Fark neydi?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Tuzaklar

      Uzun yaşayan bir yere inner nesne vermek. Zamanlayıcıya, static bir listeye ya da bir cache’e konan inner nesne, dış nesneyi ve onun tuttuğu her şeyi canlı tutar.

      Anonim sınıfta this sanmak. Anonim sınıfın içinde this anonim nesnenin kendisidir; lambda’da ise dış nesnedir.

      Serileştirilen inner sınıf. Inner nesneyi serileştirmek dış nesneyi de serileştirmeye çalışır.

      Hızlı kontrolİleri

      Her hesap ekranı kapandıktan sonra bellekte kalıyor. Sorunlu satırları işaretle.

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Hatalı satıra dokun, sonra kontrol et.

      HesapEkrani.java
      Java 21UTF-8LF
      Derinleş · Hesap: static Builder, gerçekten içeride yaşayan iterator ve kendi başına bir hatırlatıcı 3 dosya · ~97 satır · ilk okumada atlayabilirsin
      Proje dosyaları

      src/main/java/com/bank/account/ Account.java Builder static iç içe sınıftır: hesap henüz yokken çalışır.

      src/main/java/com/bank/account/Account.java
      public final class Account implements Iterable<Movement> {
      private final String iban;
      private final List<Movement> movements;
      private Account(Builder builder) {
      this.iban = builder.iban;
      this.movements = List.copyOf(builder.movements);
      }
      public String iban() {
      return iban;
      }
      @Override
      public Iterator<Movement> iterator() {
      return new MovementIterator();
      }
      /** Static: builds an account before any account exists. */
      public static final class Builder {
      private String iban;
      private final List<Movement> movements = new ArrayList<>();
      public Builder iban(String iban) {
      this.iban = iban;
      return this;
      }
      public Builder movement(Movement movement) {
      movements.add(movement);
      return this;
      }
      public Account build() {
      return new Account(this);
      }
      }
      /** Inner: walks this account's own movements and never outlives it. */
      private final class MovementIterator implements Iterator<Movement> {
      private int next;
      @Override
      public boolean hasNext() {
      return next < movements.size();
      }
      @Override
      public Movement next() {
      return movements.get(next++);
      }
      }
      }

      src/main/java/com/bank/account/ StatementReminder.java Hareket iterator'ı inner sınıftır: kendi hesabının hareketlerini gezer ve hesaptan uzun yaşamaz.

      src/main/java/com/bank/account/StatementReminder.java
      public final class StatementReminder implements Runnable {
      /** Static nested on purpose: the scheduler keeps it for days, so it must not hold an Account. */
      public static final class Payload {
      private final String iban;
      public Payload(String iban) {
      this.iban = iban;
      }
      public String iban() {
      return iban;
      }
      }
      private final Payload payload;
      private final Notifier notifier;
      public StatementReminder(Payload payload, Notifier notifier) {
      this.payload = payload;
      this.notifier = notifier;
      }
      @Override
      public void run() {
      notifier.send(payload.iban(), "Aylık ekstreniz hazır.");
      }
      }

      src/main/java/com/bank/account/ AccountScreen.java Hatırlatıcı static iç içe sınıftır: yalnızca IBAN'ı taşır, zamanlayıcıda günlerce dursa da hesabı tutmaz.

      src/main/java/com/bank/account/AccountScreen.java
      public class AccountScreen {
      private final ScheduledExecutorService scheduler;
      private final Notifier notifier;
      public AccountScreen(ScheduledExecutorService scheduler, Notifier notifier) {
      this.scheduler = scheduler;
      this.notifier = notifier;
      }
      public void scheduleStatementReminder(Account account) {
      var payload = new StatementReminder.Payload(account.iban());
      scheduler.schedule(new StatementReminder(payload, notifier), 30, TimeUnit.DAYS);
      }
      }

      Kendini sına

      Önce hızlı bir ısınma: puan yok, kayıt yok. Sonra asıl sorular.

      Şimşek turu1/4

      static iç içe sınıfın nesnesini kurmak için önce dış sınıfın bir nesnesi gerekir.

      Soru 1/3Orta

      Bir anonim sınıfın run metodu içinde this yazınca neye ulaşırsın?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Aklında kalacak üç şey

      1. 1 static iç içe sınıf dış sınıfın bir nesnesine ihtiyaç duymaz; inner sınıfın her nesnesi onu kuran dış nesneye bağlıdır.
      2. 2 Dış nesnenin alanlarını kullanan inner sınıf, anonim sınıf ya da this'i yakalayan lambda o nesneyi canlı tutar; uzun yaşayan bir yerde saklanırsa sızıntı olur.
      3. 3 Varsayılan seçim static iç içe sınıftır. Inner sınıf yalnızca dış nesneye gerçekten ihtiyaç varsa yazılır.
      Sonraki kapı Kapsülleme, soyutlama, kalıtım, polimorfizm. Dördü de aynı soruya cevap: bir şey değişince kaç dosya değişir? Soyutlama ve Dört Sütun — Hangi Değişikliğe Hangisi Yardım Eder? · 9 dk

      4 kart sonraki derste seni bekliyor

      0/4 kart bu dersten toplandı