1. Wprowadzenie
W Java, tak jak w życiu, nie wszystko powinno być dostępne dla wszystkich i zawsze. Wyobraź sobie mieszkanie: nie chcesz, aby sąsiedzi chodzili po twojej sypialni, prawda? Tak samo w programach czasem chcemy „zamknąć drzwi” do pewnych zmiennych lub metod, aby nie dało się odwołać do nich z zewnątrz.
Do tego w Java istnieją modyfikatory dostępu – specjalne słowa, które określają, gdzie można używać danej zmiennej lub metody: na przykład public lub private.
Główne modyfikatory dostępu
| Modyfikator | Gdzie widoczne? |
|---|---|
|
Wszędzie, gdzie klasa jest widoczna (zarówno w innych pakietach, jak i w innych plikach) |
|
Tylko wewnątrz tej samej klasy |
|
Tylko w obrębie tego samego pakietu (tzn. w klasach w tym samym katalogu) |
Istnieją też inne modyfikatory, ale poruszymy je później, gdy dojdziemy do dziedziczenia.
Przykład w praktyce
public class User
{
public String name; // widoczne dla wszystkich
private int age; // widoczne tylko wewnątrz klasy User
public void sayHello()
{
System.out.println("Cześć, nazywam się " + name);
}
private void secretMethod()
{
System.out.println("To tajna metoda!");
}
}
Wyjaśnienie:
- name – widoczne wszędzie, gdzie można użyć klasy User.
- age – widoczne tylko wewnątrz samej klasy User.
- sayHello() – metoda publiczna, można ją wywołać z dowolnej innej klasy.
- secretMethod() – metoda prywatna, nie można jej wywołać z zewnątrz.
Jak to działa w prawdziwych zadaniach?
Załóżmy, że mamy klasę opisującą konto bankowe. Zdecydowanie nie chcemy, aby każdy mógł bezpośrednio zmieniać saldo! Dlatego zmienna salda będzie private, a do pracy z nią będą służyć specjalne metody.
Dlaczego nie należy robić wszystkiego public?
Może pojawić się pokusa: „A zróbmy wszystko public, żeby się nie męczyć!” Ale to droga do chaosu. Wyobraź sobie, że każdy może zmieniać twoje saldo, nazwę użytkownika, a nawet wywoływać metodę przeznaczoną tylko do użytku wewnętrznego. W dużych programach prowadzi to do błędów i „magicznych” problemów.
Złota zasada programisty Java:
Najpierw ustawiaj wszystko jako private, a potem udostępniaj na zewnątrz tylko to, co naprawdę potrzebne.
2. Zasięg zmiennych
Zasięg to obszar kodu, w którym zmienna „istnieje” i może być użyta. Po wyjściu poza ten obszar zmienna „znika” – jakby jej nie było.
W Java istnieje kilka rodzajów zmiennych według zasięgu:
- Zmienne lokalne – zadeklarowane wewnątrz metody lub bloku kodu { ... }.
- Parametry metody – zadeklarowane w nawiasach okrągłych metody.
- Pola klasy – zadeklarowane wewnątrz klasy, ale poza metodami.
Jeśli zmienna została zadeklarowana wewnątrz { bloku },
jest widoczna tylko wewnątrz tego bloku
i niedostępna poza nim.
Zmienne lokalne
Zmienna lokalna „żyje” tylko wewnątrz metody lub bloku, w którym została zadeklarowana.
void printSum(int a, int b)
{
int sum = a + b; // zmienna lokalna
System.out.println(sum);
}
// sum nie istnieje już w tym miejscu!
Próba odwołania się do sum poza metodą spowoduje błąd kompilacji: „Nie można znaleźć zmiennej sum”.
Parametry metody
Parametry to też zmienne, ale żyją tylko wewnątrz metody.
void greet(String name)
{
System.out.println("Cześć, " + name);
}
// name nie istnieje już w tym miejscu!
Pola klasy (pola składowe)
Pola klasy deklaruje się wewnątrz klasy, ale poza metodami. Są widoczne we wszystkich metodach tej klasy.
public class Counter
{
private int count = 0; // pole klasy
public void increment()
{
count++; // możemy używać pola
}
public int getCount()
{
return count; // również możemy używać pola
}
}
3. Przesłanianie zmiennych (shadowing)
Przesłanianie (shadowing) to sytuacja, gdy w jednej przestrzeni nazw (zasięgu) deklaruje się zmienną (lub parametr) o tej samej nazwie, co w zewnętrznym zasięgu. Wewnątrz takiego bloku „nowa” nazwa przesłania starszą i do zewnętrznej wartości nie można odwołać się bezpośrednio.
Przykład przesłaniania:
class ShadowDemo
{
int value = 10; // pole klasy
void printValue()
{
System.out.println(value); // 10 – wypisuje pole klasy
int value = 5; // zmienna lokalna przesłania pole klasy
System.out.println(value); // wypisuje 5, a nie 10
}
}
W tym przykładzie, gdy piszemy int value = 5;, deklarujemy nową zmienną lokalną, która ma priorytet nad polem klasy o tej samej nazwie. Przy odwołaniu do value wewnątrz metody printValue() będzie używana zmienna lokalna, a nie pole klasy.
Jeśli jednak trzeba odwołać się do statycznego pola klasy, użyj nazwy klasy jako prefiksu:
class ShadowDemo
{
static int value = 10; // statyczne pole klasy
void printValue()
{
System.out.println(value); // 10 – pole klasy
int value = 5;
System.out.println(value); // 5 – zmienna lokalna
System.out.println(ShadowDemo.value); // 10 – statyczne pole klasy, dostęp przez 'ShadowDemo'
}
}
Jeśli chcesz odwołać się do niestatycznego pola klasy, używa się słowa kluczowego this. Wskazuje ono na bieżącą instancję obiektu.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 – pole klasy
int value = 5;
System.out.println(value); // 5 – zmienna lokalna
System.out.println(this.value); // 10 – pole klasy, dostęp przez 'this'
}
}
4. Praktyka: modyfikatory dostępu i zasięg
Kontynuujmy rozwijanie aplikacji edukacyjnej: prosta ewidencja studentów. Zastosujemy różne modyfikatory dostępu dla pól i metod.
Przykład: klasa Student
public class Student
{
public String name; // imię studenta (widoczne dla wszystkich)
private int age; // wiek (widoczny tylko wewnątrz klasy)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("Cześć, nazywam się " + name);
}
private void printSecret()
{
System.out.println("Mój wiek: " + age);
}
public void revealSecret()
{
printSecret(); // można wywołać metodę prywatną wewnątrz klasy
}
}
Użycie klasy Student
public class Main
{
public static void main(String[] args)
{
Student s = new Student("John", 20);
s.sayHello(); // OK: metoda publiczna
s.revealSecret(); // OK: metoda publiczna, która wewnątrz wywołuje prywatną
s.age = 30; // Błąd! Pole age jest prywatne
s.printSecret(); // Błąd! Metoda printSecret jest prywatna
}
}
5. Typowe błędy przy pracy z modyfikatorami dostępu i zasięgiem
Błąd nr 1: Wszystko public – i witaj, chaosie!
Jeśli wszystkie pola i metody będą publiczne, ryzykujesz utratę kontroli nad tym, kto i jak zmienia twoje dane. Ustawiaj pola jako private i udostępniaj na zewnątrz tylko to, co niezbędne.
Błąd nr 2: Próba użycia zmiennej poza jej zasięgiem.
Na przykład zadeklarowano zmienną wewnątrz metody, a potem próbuje się odwołać do niej z zewnątrz – pojawi się błąd kompilacji. Zmienne lokalne żyją tylko w swoim bloku { ... }.
Błąd nr 3: Konflikt nazw (przesłanianie zmiennych).
Jeśli w metodzie zadeklarujesz zmienną o tej samej nazwie, co pole klasy, łatwo się pomylić, której zmiennej używasz. Używaj this, aby jednoznacznie wskazać pole klasy: this.value.
Błąd nr 4: Próba odwołania się do metody private lub pola z innej klasy.
Jeśli metoda lub pole są oznaczone jako private, są dostępne tylko wewnątrz tej samej klasy. Próba odwołania z zewnątrz spowoduje błąd kompilacji.
Błąd nr 5: Zapomniany zasięg w pętli lub bloku.
Zmienna zadeklarowana wewnątrz pętli albo dowolnego bloku { } żyje tylko wewnątrz tego bloku. Poza nim nie istnieje.
GO TO FULL VERSION