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.
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.0Dostę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: 10Wzorzec 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 5Nasł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: aliceKlasa 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 itselfKiedy 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
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
- Statyczne klasy zagnieżdżone
- Klasy wewnętrzne i dostęp do klasy zewnętrznej
- Klasy lokalne i anonimowe
- Wybór właściwej strategii zagnieżdżania