Cześć! W dzisiejszej lekcji zapoznamy się z koncepcją modyfikatorów dostępu i rozważymy przykłady, jak z nimi pracować. Oczywiście powiedzenie „zapoznaj się” nie jest do końca trafne: większość z nich znasz już z poprzednich lekcji. Na wszelki wypadek odświeżmy sobie pamięć o najważniejszym punkcie. Dostęp do modyfikatorów to najczęściej słowa kluczowe regulujące dostęp do różnych części kodu. Dlaczego „najczęściej”? Ponieważ jeden z nich jest domyślnie ustawiony bez użycia słowa kluczowego :) Java ma cztery modyfikatory dostępu. Wymieniamy je w kolejności od najbardziej restrykcyjnej do najbardziej „pobłażliwej”:
- prywatny;
- domyślny (pakiet widoczny);
- chroniony;
- publiczny.
Prywatny modyfikator
private jest najbardziej restrykcyjnym modyfikatorem dostępu. Ogranicza widoczność danych i metod do jednej klasy. Ten modyfikator znasz z lekcji o getterach i setterach. Pamiętasz ten przykład?public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Rozważaliśmy to na poprzedniej lekcji. Popełniliśmy tutaj poważny błąd: upubliczniliśmy nasze dane, co umożliwiło innym programistom bezpośredni dostęp do pól i zmianę ich wartości. Co więcej... wartości te zostały nadane bez żadnych kontroli. Oznacza to, że nasz program mógłby stworzyć kota o nazwie „” w wieku -1000 lat i wadze 0. Aby rozwiązać ten problem, użyliśmy getterów i setterów, a także użyliśmy modyfikatora private, aby ograniczyć dostęp do danych.
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
public String getName() {
return name;
}
public void setName(String name) {
// input parameter check
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
// input parameter check
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
// input parameter check
this.weight = weight;
}
}
Zasadniczo ograniczanie dostępu do pól i wdrażanie metod pobierających i ustawiających to najczęstsze przykłady tego, jak prywatneprzydałby się w prawdziwej pracy. Innymi słowy, głównym celem tego modyfikatora jest osiągnięcie enkapsulacji w programie. Nawiasem mówiąc, dotyczy to nie tylko pól. Wyobraź sobie, że w twoim programie jest metoda, która implementuje BARDZO złożoną funkcjonalność. Co możemy zasugerować jako przykład? Załóżmy, że twoja metoda readDataFromCollider() przyjmuje jako dane wejściowe adres danych, odczytuje dane z Wielkiego Zderzacza Hadronów w formacie bajtowym, konwertuje te dane na tekst, zapisuje je do pliku i drukuje. Już sam opis metody wygląda przerażająco, nie mówiąc już o kodzie :) Aby kod był bardziej czytelny, lepiej nie pisać całej złożonej logiki metody w jednym miejscu. Zamiast tego powinniśmy podzielić funkcjonalność na osobne metody. Na przykład readByteData()odpowiada za odczyt danych, metoda convertBytesToSymbols() konwertuje dane odczytane z collidera na tekst, metoda saveToFile() zapisuje otrzymany tekst do pliku, a metoda printColliderData() drukuje nasz plik danych. Ostatecznie nasza metoda readDataFromCollider() będzie znacznie prostsza:
public class ColliderUtil {
public void readDataFromCollider(Path pathToData) {
byte[] colliderData = readByteData(pathToData);
String[] textData = convertBytesToSymbols(colliderData);
File fileWithData = saveToFile(textData);
printColliderData(fileWithData);
}
public byte[] readByteData(Path pathToData) {
// Reads data in bytes
}
public String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// Converts bytes to characters
}
public File saveToFile(String[] colliderData) {
// Saves read data to a file
}
public void printColliderData(File fileWithColliderData) {
// Prints data from the file
}
}
Jednak, jak zapewne pamiętasz z lekcji o interfejsach, użytkownik uzyskuje dostęp tylko do zewnętrznego interfejsu. A nasze 4 metody nie są tego częścią. Są to metody pomocnicze: stworzyliśmy je, aby poprawić czytelność kodu i nie upychać czterech różnych zadań w jednej metodzie. Nie musisz udzielać użytkownikowi dostępu do tych metod. Jeśli użytkownicy mają dostęp do metody convertBytesToSymbols() podczas pracy ze zderzaczem, najprawdopodobniej będą po prostu zdezorientowani tą metodą i będą się zastanawiać, do czego ona służy. Jakie bajty są konwertowane? Skąd oni przyszli? Po co konwertować je na tekst? Logika wykonywana w tej metodzie nie jest częścią interfejsu udostępnianego użytkownikowi. Tylko readDataFromCollider()Metoda jest częścią interfejsu. Co więc robimy z tymi czterema „wewnętrznymi” metodami? Prawidłowy! Użyj modyfikatora private , aby ograniczyć do nich dostęp. Dzięki temu mogą spokojnie wykonywać swoją pracę w klasie bez dezorientowania użytkownika, który nie musi znać logiki każdej metody.
public class ColliderUtil {
public void readDataFromCollider(Path pathToData) {
byte[] colliderData = readByteData(pathToData);
String[] textData = convertBytesToSymbols(colliderData);
File fileWithData = saveToFile(textData);
printColliderData(fileWithData);
}
private byte[] readByteData(Path pathToData) {
// Reads data in bytes
}
private String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// Converts bytes to characters
}
private File saveToFile(String[] colliderData) {
// Saves read data to a file
}
private void printColliderData(File fileWithColliderData) {
// Prints data from the file
}
}
Chroniony modyfikator
Kolejnym najbardziej restrykcyjnym modyfikatorem jest chroniony . Pola i metody oznaczone chronionym modyfikatorem dostępu będą widoczne:- we wszystkich klasach objętych tym samym pakietem co nasz;
- we wszystkich klasach, które dziedziczą naszą klasę.
public abstract class AbstractSecretAgent {
public static int agentCount = 0;
}
Ale nasi agenci są tajni! Oznacza to, że oni i nikt inny nie powinien wiedzieć, ile ich istnieje. Możemy łatwo dodać chroniony modyfikator do pola agent_counter . Wtedy instancje innych tajnych klas agentów i inne klasy znajdujące się w naszym pakiecie top_secret mogą uzyskać jego wartość.
public abstract class AbstractSecretAgent {
protected static int agent_counter = 0;
}
I to jest rodzaj specjalistycznego zadania, które wymaga chronionego modyfikatora :)
Widoczny modyfikator pakietu
Następny na liście jest domyślny modyfikator, znany również jako widoczny modyfikator pakietu. Nie jest wskazywane przez słowo kluczowe, ponieważ Java stosuje je domyślnie do wszystkich pól i metod. Jeśli napiszesz w swoim kodzie:int x = 10
zmienna x będzie miała widoczny dostęp do tego pakietu. Łatwo zapamiętać, co robi. Zasadniczo domyślnie = chronione dziedziczenie :) Podobnie jak chroniony modyfikator, jego zastosowanie jest ograniczone. Najczęściej dostęp domyślny jest używany w pakiecie, który ma pewne klasy narzędzi, które nie implementują funkcjonalności wszystkich innych klas w pakiecie. Podajmy przykład. Wyobraź sobie, że mamy pakiet „usług”. Zawiera różne klasy, które współpracują z bazą danych. Na przykład istnieje klasa UserService , która odczytuje dane użytkownika z bazy danych, CarServiceklasa, która odczytuje dane samochodu z tej samej bazy danych, oraz inne klasy, z których każda współpracuje z określonymi typami obiektów i odczytuje odpowiednie dane z bazy danych.
package services;
public class UserService {
}
package services;
public class CarService {
}
Ale byłoby łatwo, gdyby dane w bazie danych były w jednym formacie, a my potrzebujemy ich w innym. Wyobraź sobie, że daty urodzenia użytkowników w bazie danych są przechowywane jako <TIMESTAMP WITH TIME ZONE>...
2014-04-04 20:32:59.390583+02
... a zamiast tego potrzebujemy najprostszego obiektu — java.util.Date . Aby rozwiązać ten problem, w pakiecie usług możemy stworzyć specjalną klasę Mapper . Będzie odpowiedzialny za konwersję danych z bazy danych do znanych nam obiektów Javy. Prosta klasa pomocnicza. Zwykle deklarujemy wszystkie klasy jako public class ClassName , ale nie jest to wymagane. Możemy zadeklarować naszą klasę pomocniczą po prostu jako class Mapper . W tym przypadku nadal spełnia swoje zadanie, ale nie jest widoczny dla nikogo poza pakietem usług !
package services;
class Mapper {
}
package services;
public class CarService {
Mapper mapper;
}
A oto podstawowe rozumowanie: dlaczego ktoś spoza pakietu miałby widzieć klasę pomocniczą, która działa tylko z klasami w tym pakiecie?
Modyfikator publiczny
I wreszcie modyfikator publiczny ! Spotkałeś ten modyfikator pierwszego dnia nauki w CodeGym, kiedy po raz pierwszy uruchomiłeś public static void main(String[] args) . Teraz, kiedy przestudiowałeś lekcję o interfejsach, jej cel jest dla ciebie oczywisty :) W końcu modyfikator public został stworzony, aby dać coś użytkownikom. Na przykład interfejs twojego programu. Załóżmy, że napisałeś program tłumaczący, który może tłumaczyć rosyjski tekst na angielski. Utworzyłeś metodę translate(String textInRussian) , która implementuje całą niezbędną logikę. Oznaczyłeś tę metodę słowem public , a teraz jest częścią interfejsu:public class Translator {
public String translate(String textInRussian) {
// Translates text from Russian to English
}
}
Możesz powiązać tę metodę z przyciskiem „Tłumacz” na ekranie i gotowe! Każdy może z niego korzystać. Części kodu oznaczone modyfikatorem public są przeznaczone dla użytkownika końcowego. Podając rzeczywisty przykład, prywatny dotyczy wszystkich procesów zachodzących w telewizorze, ale publiczny dotyczy przycisków na pilocie służącym do zarządzania telewizorem. Co więcej, użytkownik nie musi wiedzieć, jak zbudowany jest telewizor ani jak działa. Pilot zdalnego sterowania to zestaw publicznych metod: on() , off() , nextChannel() , previousChannel() , gainVolume() , reduceVolume() itd. Aby utrwalić to, czego się nauczyłeś, zalecamy obejrzenie lekcji wideo z naszego kursu języka Java