Java Academy · Lekcja

Klasy wewnętrzne i dostęp do klasy zewnętrznej

Używaj niestatycznych klas wewnętrznych, które przechowują odwołanie do instancji klasy zewnętrznej.

Lekcja 2 z 413 kroki

Klasy wewnętrzne i dostęp do klasy zewnętrznej to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Klasy wewnętrzne

Niestatyczna klasa wewnętrzna przechowuje niejawne odwołanie do instancji klasy zewnętrznej. Może uzyskiwać dostęp do wszystkich składowych klasy zewnętrznej, także prywatnych.

Deklarowanie i używanie klasy wewnętrznej

Utwórz instancję klasy wewnętrznej wyłącznie za pośrednictwem instancji klasy zewnętrznej, używając outer.new InnerClass().

class BankAccount {
    private double balance;
    private String owner;

    BankAccount(String owner, double initial) {
        this.owner = owner; this.balance = initial;
    }

    class Transaction {
        void deposit(double amount) {
            balance += amount; // accesses outer field directly
            System.out.println(owner + " deposited " + amount);
        }
    }
}

BankAccount acct = new BankAccount("Alice", 500);
BankAccount.Transaction tx = acct.new Transaction();
tx.deposit(100); // Alice deposited 100.0

Dostęp do składowych klasy zewnętrznej

Klasy wewnętrzne mogą uzyskiwać dostęp do wszystkich składowych klasy zewnętrznej — nawet do prywatnych pól i metod.

class Encryption {
    private final String secretKey = "s3cr3t"; // private!

    class Encryptor {
        String encrypt(String data) {
            // Inner class accesses private outer field
            return data + "|" + secretKey.hashCode();
        }
    }

    class Decryptor {
        String decrypt(String encrypted) {
            int idx = encrypted.lastIndexOf('|');
            return encrypted.substring(0, idx);
        }
    }
}

Przysłanianie nazw za pomocą this

Gdy klasa wewnętrzna ma pole o tej samej nazwie co klasa zewnętrzna, użyj OuterClass.this.field, aby odwołać się do pola klasy zewnętrznej.

class Outer {
    int value = 10;

    class Inner {
        int value = 20; // shadows outer.value

        void printBoth() {
            System.out.println("Inner value: " + value);         // 20
            System.out.println("Outer value: " + Outer.this.value); // 10
        }
    }
}

new Outer().new Inner().printBoth();
// Inner value: 20
// Outer value: 10

Wzorzec Iterator z użyciem klasy wewnętrznej

Klasyczny przypadek użycia: implementacja iteratora jako klasy wewnętrznej, która uzyskuje dostęp do prywatnych danych otaczającej kolekcji.

class NumberRange implements Iterable<Integer> {
    private final int start;
    private final int end;

    NumberRange(int start, int end) {
        this.start = start; this.end = end;
    }

    @Override
    public java.util.Iterator<Integer> iterator() {
        return new RangeIterator(); // inner class instance
    }

    private class RangeIterator implements java.util.Iterator<Integer> {
        private int current = start; // accesses outer start

        public boolean hasNext() { return current <= end; }
        public Integer next()    { return current++; }
    }
}

for (int n : new NumberRange(1, 5)) System.out.print(n + " ");
// 1 2 3 4 5

Nasłuchiwacze zdarzeń jako klasy wewnętrzne

Programowanie GUI i programowanie sterowane zdarzeniami często korzystają z klas wewnętrznych do implementowania nasłuchiwaczy zdarzeń mających dostęp do stanu otaczającego komponentu interfejsu użytkownika.

// Swing-style example (conceptual)
class LoginPanel {
    private String username = "";

    class LoginButtonListener {
        void onButtonClicked() {
            if (username.isEmpty()) {
                System.out.println("Please enter username"); // accesses outer field
            } else {
                System.out.println("Logging in as: " + username);
            }
        }
    }

    void setUsername(String name) { this.username = name; }
}
LoginPanel panel = new LoginPanel();
panel.setUsername("alice");
panel.new LoginButtonListener().onButtonClicked();
// Logging in as: alice

Klasa wewnętrzna w kolekcjach

Niektóre implementacje kolekcji używają klas wewnętrznych do przechowywania kursorów lub widoków opartych na otaczającej kolekcji.

class SimpleStack<T> {
    private Object[] elements;
    private int size = 0;

    SimpleStack(int capacity) {
        elements = new Object[capacity];
    }

    void push(T item) {
        if (size >= elements.length) throw new StackOverflowError();
        elements[size++] = item;
    }

    T pop() {
        if (size == 0) throw new java.util.EmptyStackException();
        T item = (T) elements[--size];
        elements[size] = null;
        return item;
    }

    int size() { return size; }
}

Ostrzeżenie o wycieku pamięci

Instancje klas wewnętrznych przechowują odwołanie do instancji klasy zewnętrznej. Jeśli instancja klasy wewnętrznej wydostanie się poza klasę zewnętrzną (np. zostanie zapisana w długo żyjącej kolekcji), instancja klasy zewnętrznej nie może zostać odzyskana przez garbage collector.

// Memory leak: anonymous inner class (implicit outer ref) stored statically
class LeakExample {
    byte[] data = new byte[1024 * 1024]; // 1MB

    // BAD: this Runnable holds a reference to LeakExample.this
    Runnable leak = new Runnable() {
        public void run() { System.out.println(data.length); }
    };
}
// If 'leak' is stored somewhere, LeakExample (and its 1MB) is never GC'd

// FIX: use a static nested class or a lambda that captures only needed values
static Runnable noLeak(byte[] data) {
    return () -> System.out.println(data.length); // captures array, not outer
}

Porównanie klasy wewnętrznej i lambdy

Współczesne lambdy w Javie często zastępują anonimowe klasy wewnętrzne dla interfejsów z jedną metodą. Lambdy są bardziej zwięzłe, ale NIE mają własnego this.

// Old way: anonymous inner class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running (inner class) — this is the Runnable");
    }
};

// Modern: lambda
Runnable r2 = () -> System.out.println("Running (lambda)");

r1.run();
r2.run();

// Lambda this = enclosing class (not the Runnable)
// Inner class this = the Runnable instance itself

Kiedy używać klas wewnętrznych

Używaj niestatycznych klas wewnętrznych, gdy:

  • Klasa musi uzyskiwać dostęp do prywatnego stanu instancji klasy zewnętrznej
  • Każda instancja klasy wewnętrznej jest logicznie powiązana z konkretną instancją klasy zewnętrznej
  • Używasz wzorca Iterator do enkapsulowania logiki przechodzenia

Praktyka: Paginator

Praktyczny paginator, w którym klasa wewnętrzna przechowuje stan strony i uzyskuje dostęp do zewnętrznej listy danych.

import java.util.*;

class Paginator<T> {
    private final List<T> items;
    private final int pageSize;

    Paginator(List<T> items, int pageSize) {
        this.items = items; this.pageSize = pageSize;
    }

    class Page {
        private int pageIndex = 0;

        List<T> current() {
            int start = pageIndex * pageSize;
            int end = Math.min(start + pageSize, items.size()); // outer
            return start < items.size() ? items.subList(start, end) : List.of();
        }
        boolean hasNext() { return (pageIndex + 1) * pageSize < items.size(); }
        void next() { if (hasNext()) pageIndex++; }
    }
}
Paginator<String> p = new Paginator<>(List.of("a","b","c","d","e"), 2);
Paginator.Page page = p.new Page();
System.out.println(page.current()); // [a, b]
page.next();
System.out.println(page.current()); // [c, d]

Szybkie sprawdzenie

Co przechowuje klasa wewnętrzna (niestatyczna), czego nie przechowuje statyczna klasa zagnieżdżona?

Podsumowanie: klasy wewnętrzne i dostęp do klasy zewnętrznej

Najważniejsze informacje:

  • Niestatyczne klasy wewnętrzne przechowują niejawną referencję do otaczającej instancji klasy zewnętrznej
  • Instancje klas wewnętrznych należy tworzyć wyłącznie za pośrednictwem instancji klasy zewnętrznej: outer.new Inner()
  • Klasy wewnętrzne mogą uzyskiwać dostęp do wszystkich składowych klasy zewnętrznej, w tym do pól prywatnych
  • Używaj Outer.this.field, aby rozróżnić przesłonięte pola
  • Najlepsze zastosowania: Iterator, nasłuchiwacze zdarzeń, wewnętrzne struktury danych
  • Ryzyko: instancje klas wewnętrznych uniemożliwiają zwolnienie instancji klasy zewnętrznej przez moduł garbage collectora
Bezpłatny start

Ucz się Java dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
104
Lekcje
374

Często zadawane pytania

Czy lekcja „Klasy wewnętrzne i dostęp do klasy zewnętrznej” jest bezpłatna?

Tak — pełny tekst „Klasy wewnętrzne i dostęp do klasy zewnętrznej” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Klasy wewnętrzne i dostęp do klasy zewnętrznej”?

Używaj niestatycznych klas wewnętrznych, które przechowują odwołanie do instancji klasy zewnętrznej. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Klasy wewnętrzne i dostęp do klasy zewnętrznej”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Statyczne klasy zagnieżdżone
  2. Klasy wewnętrzne i dostęp do klasy zewnętrznej
  3. Klasy lokalne i anonimowe
  4. Wybór właściwej strategii zagnieżdżania
← Powrót do Java Academy