Rozdział 9 - Klasy - Podsumowanie i pytania – typy prymitywne i referencyjne, modyfikatory dostępu

Typy prymitywne i referencyjne

  • W Javie występują dwa rodzaje typów:
    • typy prymitywneboolean, 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];
    
Wskazanie na obiekty w pamieci przez zmienne typu zlozonego
  • 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 drugiSamochodd = pierwszySamochod;
    drugiSamochodd.ustawKolor("Bialy");
    
    System.out.println(pierwszySamochod);
    System.out.println(drugiSamochodd);
    
    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ą:
Dwie zmienne typu zlozonego wskazuja na jeden obiekt w pamieci
  • Innymi słowy – poniższa linijka nie powoduje stworzenia kopii obiektu typu Samochod:
    Samochod drugiSamochodd = 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

  1. Jakie typy prymitywne są zdefiniowane w Javie?
  2. Czym są typy referencyjne (złożone)? Jak się je definiuje?
  3. 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;
      }
    }
    
  4. 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};
    
  5. 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]);
      }
    }
    

Odpowiedzi do pytań

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ł.
    1. 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ą.
    2. 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:
    1. Wszystkie pola klas powinny być prywatne.
    2. Wszystkie metody, które są używane wewnętrznie przez klasę, powinny być prywatne.
    3. 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

  1. Do czego służą modyfikatory dostępu w Javie?
  2. Czy modyfikatory dostępu można używać tylko podczas definicji pól klasy?
  3. Czym różnią się modyfikatory public i private?
  4. Kto ma dostęp do prywatnych pól i metod klasy?
  5. Czy moglibyśmy wszystkie pola i metody zawsze definiować z dostępem public? Jeżeli tak, to czy jest to dobry pomysł?
  6. 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);
      }
    }
    
  7. 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?
      }
    }
    
  8. 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);
      }
    }
    
  9. 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);
      }
    }
    

Odpowiedzi do pytań

Dodaj komentarz

Twój adres email nie zostanie opublikowany.