Spis treści
- Rozdział 9 - Klasy
- Czym są klasy i do czego służą?
- Różnice między typami prymitywnymi i referencyjnymi
- Modyfikatory dostępu
- Podsumowanie i pytania – typy prymitywne i referencyjne, modyfikatory dostępu
- Pola klas
- Konstruktory
- Equals i porównywanie obiektów
- Referencje do obiektów
- Metody i pola statyczne
- Pakiety i importowanie klas
Typy prymitywne i referencyjne¶
- W Javie występują dwa rodzaje typów:
- typy prymitywne – boolean, byte, short, int, long, float, double, oraz char. Są to elementy budujące, z których składają się typy złożone.
- typy referencyjne – są to typy złożone, tworzone przez programistów poprzez pisanie klas, jak na przykład Samochod lub String. Do typów złożonych zaliczają się także tablice.
- Zmienne typów prymitywnych przechowują w pamięci konkretne wartości, natomiast zmienne typu referencyjnego – przechowują adresy obiektów w pamięci:
int a = 10; boolean b = true; String powitanie = "Witajcie!"; double[] rzeczywiste = new double[5];
- Zmienne typów referencyjnych to nie obiekty – to adresy obiektów. Gdy przypisujemy do jednej zmiennej typu referencyjnego wartość innej zmiennej typu referencyjnego, to kopiujemy adres obiektu docelowego, a nie obiekt docelowy:
Samochod pierwszySamochod = new Samochod(); // 1 pierwszySamochod.ustawKolor("Czerwony"); pierwszySamochod.ustawPredkosc(80); Samochod drugiSamochod = pierwszySamochod; drugiSamochod.ustawKolor("Bialy"); System.out.println(pierwszySamochod); System.out.println(drugiSamochod);
Wynikiem działania programu jest:Jestem samochodem! Moj kolor to Bialy, jade z predkoscia 80 Jestem samochodem! Moj kolor to Bialy, jade z predkoscia 80 - Powyżej mamy jeden obiekt typu Samochod (utworzony w linii (1) za pomocą słowa kluczowego new) oraz dwie zmienne, które na niego wskazują:
- Innymi słowy – poniższa linijka nie powoduje stworzenia kopii obiektu typu Samochod:
Samochod drugiSamochod = pierwszySamochod;
lecz przepisanie (skopiowanie) adresu obiektu, na który zmienna po prawej stronie operatora przypisania = wskazuje. - Pamiętajmy: zmienne typów referencyjnych (klas) oraz zmienne tablicowe przechowują adresy obiektów tych klas i tablic.
- Zmienne typu złożonego, w przeciwieństwie do zmiennych typu prymitywnego, tworzymy za pomocą słowa kluczowego new:
Samochod pierwszySamochod = new Samochod();
- Tworząc nowe obiekty typu String nie musimy (a nawet nie powinniśmy) używać słowa kluczowego new – wystarczy przypisać do zmiennej łańcuch tekstowy zawarty w cudzysłowach:
String powitanie = "Witajcie!";
- Tablice także są szczególne, ponieważ zamiast używać słowa kluczowego new, możemy użyć skróconego zapisu z użyciem nawiasów klamrowych, w których umieszczamy elementy tablicy. Rozmiar tablicy będzie wydedukowany na podstawie liczby podanych elementów:
// oba sposoby na utworzenie tablicy sa poprawne double[] rzeczywiste = new double[5]; double[] rzeczywiste2 = { 3.14, 5, -20.5 };
Typy prymitywne i referencyjne – pytania¶
- Jakie typy prymitywne są zdefiniowane w Javie?
- Czym są typy referencyjne (złożone)? Jak się je definiuje?
- Ile obiektów jest tworzonych w metodzie main poniższej klasy?
public class PytanieZagadka { public int liczba; public int[] liczby; public static void main(String[] args) { PytanieZagadka zagadka = new PytanieZagadka(); zagadka.liczba = 5; zagadka.liczby = new int[2]; PytanieZagadka innaZagadka = zagadka; } }
- Który z poniższych zapisów jest niepoprawny i dlaczego?
PytanieZagadka zagadka = PytanieZagadka(); int[] tablica1 = new int[]; String tekst = new String("Witajcie!"); int[] tablica2 = []; String innyTekst = "Halo?!"; int[] tablica3 = new int[] {1, 2}; int[] tablica4 = {1, 2, 3}; int[] tablica5 = new int[2] {1, 2};
- Jakie wartości zostaną wypisane na ekran?
public class PytanieZagadka { public int liczba; public int[] liczby; public static void main(String[] args) { PytanieZagadka zagadka = new PytanieZagadka(); zagadka.liczba = 5; zagadka.liczby = new int[2]; PytanieZagadka innaZagadka = zagadka; int calkowita = zagadka.liczba; int[] tablica = innaZagadka.liczby; calkowita = 1; tablica[0] = 10; tablica[1] = 100; System.out.println(zagadka.liczba); System.out.println(zagadka.liczby[0]); System.out.println(innaZagadka.liczby[1]); } }
Modyfikatory dostępu¶
- Modyfikatory dostępu służą do ustawienia zakresu widoczności pól oraz metod klasy.
- Modyfikatory dostępu pozwalają określić, jak będzie wyglądało użytkowanie klasy oraz kto, i w jakich okolicznościach, będzie mógł z pól i metod tej klasy korzystać.
- W Javie istnieją cztery modyfikatory dostępu:
- public,
- protected,
- domyślny (nie mający własnego słowa kluczowego),
- private.
- Gdy definiujemy pole (bądź metodę) z modyfikatorem public, to oznajmiamy, że to pole (lub metoda) jest dostępne dla całego "zewnętrznego świata" – każdy może się do takiego pola odnieść i wywołać taką metodę.
- Pola i metody zdefiniowane z modyfikatorem public to pola i metody publiczne.
- Używając modyfikatora private, możemy zdefiniować, że pola i metody nie mają być dostępne poza klasą – mają one być prywatne. Dostęp powinien być możliwy jedynie z poziomu tejże klasy i tylko ona powinna móc tymi polami i metodami zarządzać.
- Modyfikatory dostępu regulują dostęp do pól i metod klas, gdy odnosimy się do nich z innych klas.
- Gdy korzystamy z obiektu danej klasy w inne klasie, to do pól i metod publicznych możemy odnieść się bezpośrednio (1) (2) (3):
public class Ksiazka { public String tytul; // 1 public String autor; // 2 private double cena; public void ustawCene(double nowaCena) { // 3 if (czyCenaJestPoprawna(nowaCena)) { cena = nowaCena; } else { System.out.println( "Cena " + nowaCena + " jest nieprawidlowa!" ); } } private boolean czyCenaJestPoprawna(double cenaDoSprawdzenia) { return cenaDoSprawdzenia > 0; } }
public class Ksiegarnia { public static void main(String[] args) { Ksiazka lokomotywa = new Ksiazka(); lokomotywa.tytul = "Lokomotywa"; // 1 lokomotywa.autor = "Julian Tuwim"; // 2 lokomotywa.ustawCene(29.99); // 3 System.out.println(lokomotywa); } }
- Jeżeli spróbujemy odnieść się do pól prywatnych, to kod w ogóle się nie skompiluje:
lokomotywa.cena = -10; // pole prywatne! lokomotywa.czyCenaJestPoprawna(0); // metoda prywatna!
Kompilacja powyższego kodu (fragmentu metody main klasy Ksiegarnia) zakończyłaby się następującym błędem:Ksiegarnia.java:13: error: cena has private access in Ksiazka lokomotywa.cena = -10; ^ Ksiegarnia.java:14: error: czyCenaJestPoprawna(double) has private access in Ksiazka lokomotywa.czyCenaJestPoprawna(0); ^ 2 errors - Korzystanie z prywatnych pól i metod spoza klasy jest zabronione, ale w ramach klasy, w której te pola i metody są zdefiniowane, możemy się do takich pól odnosić:
public class Ksiazka { public String tytul; public String autor; private double cena; public boolean czyTakaSamaCena(Ksiazka innaKsiazka) { // poprawne odniesienie do prywatnego pola return cena != innaKsiazka.cena; } // definicje metod ustawCene oraz czyCenaJestPoprawna // zostaly pominiete }
Powyższy kod działa poprawnie pomimo, że odnosimy się w metodzie czyTakaSamaCena do prywatnego pola cena obiektu innaKsiazka, ponieważ odnosimy się do tego pola z kontekstu klasy Ksiazka, która jest typem obiektu innaKsiazka. - Czy nie moglibyśmy ustawić wszystkich pól w naszych klasach jako publiczne? Moglibyśmy, ale nie jest to dobry pomysł.
- Jeżeli udostępnimy prywatne pola klasy, to nie będziemy w stanie zapewnić ich poprawnej wartości – każdy będzie mógł zmienić np. pole cena obiektu klasy Ksiazka na wartość ujemną.
- Jeżeli udostępnimy prywatne metody klasy, to każdy będzie mógł z nich korzystać – wszelkie zmiany wprowadzane do metody będą musiały brać pod uwagę, że być może jest ona używana w setkach innych klas – każda zmiana może potencjalnie spowodować, że inne fragmenty kodu, zależne od tej metody, przestaną działać.
- Użycie modyfikatora private chroni nas przed potencjalnymi, niechcianymi zmiana pól prywatnych, a także korzystania z prywatnych metod, które są własnością klasy i nie powinny być stosowane przez inne klasy.
- Zasady dotyczące używania modyfikatorów dostępu public i private:
- Wszystkie pola klas powinny być prywatne.
- Wszystkie metody, które są używane wewnętrznie przez klasę, powinny być prywatne.
- Tylko metody, z których mają korzystać użytkownicy klas, mogą być publiczne.
- Nasze klasy powinny udostępniać tak mało ze swoich pól (czyli żadnych, zgodnie z regułą numer 1), jak to możliwe, oraz tylko te metody, które są wymagane, by nasze klasy spełniały swoje zadania (były używalne przez inne części naszego programu).
- Podejście do tworzenia klas, w którym ukrywamy przed użytkownikami tych klas ich wewnętrzną implementację, nazywamy enkapsulacją.
Modyfikatory dostępu – pytania¶
- Do czego służą modyfikatory dostępu w Javie?
- Czy modyfikatory dostępu można używać tylko podczas definicji pól klasy?
- Czym różnią się modyfikatory public i private?
- Kto ma dostęp do prywatnych pól i metod klasy?
- Czy moglibyśmy wszystkie pola i metody zawsze definiować z dostępem public? Jeżeli tak, to czy jest to dobry pomysł?
- Czy poniższy kod się skompiluje?
public class KlasaZagadka { private int liczba; public static void main(String[] args) { KlasaZagadka obiekt = new KlasaZagadka(); obiekt.liczba = 5; System.out.println(obiekt.liczba); } }
- Mając następujące klasy, do jakich pól i metody typu Osoba mamy dostęp w miejscach zaznaczonych jako (1?) i (2?)?
public class Osoba { public String nazwisko; private int wiek; public void ustawWiek(int wiekOsoby) { wiek = wiekOsoby; } public boolean czyWTymSamymWieku(Osoba innaOsoba) { // 1? } private void wypiszNazwisko() { System.out.println(nazwisko); } }
public class UzycieTypuOsoba { public static void main(String[] args) { Osoba osoba = new Osoba(); // 2? } }
- Czy poniższa klasa KlasaZagadka się skompiluje? Czy klasa UzycieZagadki się skompiluje?
public class KlasaZagadka { private int liczba; }
public class UzycieZagadki { public static void main(String[] args) { KlasaZagadka obiekt = new KlasaZagadka(); obiekt.liczba = 5; System.out.println(obiekt.liczba); } }
- Jakie modyfikatory dostępu możemy, a jakie powinniśmy (i dlaczego), wstawić na miejsca znaków zapytania, aby kod był poprawny?
public class InnaZagadka { ? int liczba; ? void ustawLiczbe(int wartosc) { liczba = tajnyAlgorytm(wartosc); } ? int tajnyAlgorytm(int x) { return (x + 2) * 11; } ? String toString() { return "Tajna liczba to " + liczba; } }
public class UzycieInnejZagadki { public static void main(String[] args) { InnaZagadka obiekt = new InnaZagadka (); obiekt.ustawLiczbe(10); System.out.println(obiekt); } }