Przygotował: Jacek Sroka

Programowanie obiektowe

Wykłady 5 – Dziedziczenie, inicjalizacja, przeciążanie metod

1

Przygotował: Jacek Sroka

2

Przypomnienie ●

Typy podstawowe, klasy



Pakiety



Modyfikatory dostępu –

Działają dla klas, a nie obiektów



Inicjalizacja za pomocą konstruktorów



Przeciążanie metod/konstruktorów



Kapsułkowanie

Przygotował: Jacek Sroka

3

Kolejność inicjalizacji class Pom1 { Pom1() { System.out.println("Pom1()"); } } class TestInicjalizacji { int x = 777; Pom1 p = new Pom1(); int y = f(); int f() { System.out.println("f()"); return 1; } //deklaracje metod mogą się przeplatać z deklaracjami atrybutów int yy;

}

TestInicjalizacji() { System.out.println("TestInicjalizacji()"); } ...

Kolejność: Pom1(), f(), TestInicjalizacji()

Przygotował: Jacek Sroka

4

Kolejność inicjalizacji c.d. ... //kolejność deklaracji zmiennych ma znaczenie przy inicjalizacji int v = x; //int a = b; int b; //kolejność jest też istotna przy przekazywaniu parametrów //int a = b(u); int b(int u) { return u; } int u; //można jednak oszukiwać (wynik: h(); w=0) int z = h(); int h() { System.out.println("h(); w="+w); return w; } int w = 1313; ...

Przygotował: Jacek Sroka

Kolejność deklaracji metod ●

Nie jest istotna



Kod jest generowany w chwili kompilacji



W trakcie inicjalizacji obiektu jest tylko ciąg instrukcji pseudokodu

5

Przygotował: Jacek Sroka

6

Inicjalizacja statyczna Inicjalizacja składowych statycznych odbywa się jeden raz, w momencie pierwszego użycia klasy (odwołanie do składowej statycznej lub utworzenie obiektu) class InicjalizacjaStatyczna { static Pom1 p = new Pom1(); static void test() { System.out.println("test()"); } } InicjalizacjaStatyczna.test(); InicjalizacjaStatyczna is = new InicjalizacjaStatyczna(); Class c = InicjalizacjaStatyczna.class;

Przygotował: Jacek Sroka

7

Grupowanie inicjalizacji class BlokInicjalizacji { int x; //o wyjątkach jeszcze opowiemy { try { System.out.println("ustawiam x"); x = 7; } catch (Exception e) { System.out.println("Wpadłeś jak śliwka w studzienkę kanalizacyjną"); } } static int y; static { int pom; pom = 1; System.out.println("ustawiam y"); y = pom; } } ● ●

Kolejność znowu od góry na dół Przykłady użycia: odczytywanie konfiguracji z plików

Przygotował: Jacek Sroka

Przykład – składanie kawałków tortu class A { int iA = 1; void infoA() { System.out.println("A.infoA() na obiekcie klasy " + this.getClass().getSimpleName() + "\n iA="+iA); } } class B extends A { int iB = 2; void infoB() { infoA(); System.out.println("B.infoB() na obiekcie klasy " + this.getClass().getSimpleName() + "\n iA="+iA+", iB="+iB); } } class C extends B { int iC = 3; void infoC() { infoA(); infoB(); System.out.println("C.infoC() na obiekcie klasy "... } }

8

Przygotował: Jacek Sroka

9

Wynik ● ●

B składa się ze swoich składowych plus składowe A C składa się ze swoich składowych plus składowe B, czyli również składowe A

C c = new C(); c.infoC(); // A.infoA() na obiekcie klasy C // iA=1 // // A.infoA() na obiekcie klasy C // iA=1 // B.infoB() na obiekcie klasy C // iA=1, iB=2 // // C.infoC() na obiekcie klasy C // iA=1, iB=2, iC=3 A a = c; System.out.println("a.iA=" + a.iA + ", c.iA=" + c.iA); //a.iA=1, c.iA=1 ●



A co jak zmienimy nazwy atrybutów w hierarchii na takie same? W praktyce tego unikamy lub chronimy dostęp do atrybutów przy pomocy modyfikatorów dostępu

Przygotował: Jacek Sroka

Kompilacja vs uruchomienie Dobra intuicja dla kolejnych slajdów: –

Typy zmiennych są widoczne w chwili uruchomienia



W chwili działania są tylko obiekty określonych typów i referencje



Przypisanie na nadtyp jest zawsze bezpieczne



Przypisanie na podtyp wymaga dodatkowego kodu sprawdzającego (rzutowanie)

10

Przygotował: Jacek Sroka

11

Dostęp do atrybutów class A { int i = 1;

}

void infoA() { System.out.println("A.infoA() na obi...\n iA=" + i); }

class B extends A { int i = 2; void infoB() { infoA(); System.out.println("B.infoB()...\n iA=" + ((A) this).i + ", iB="+i); //albo super.i }} class C extends B { int i = 3;

}

void infoC() { infoA(); infoB(); System.out.println("C.infoC()...");//nie można super.super.i }

Przygotował: Jacek Sroka

12

Wynik C c = new C(); c.infoC(); // A.infoA() na // iA=1 // // A.infoA() na // iA=1 // B.infoB() na // iA=1, iB=2 //... // C.infoC() na // iA=1, iB=2,

obiekcie C obiekcie C obiekcie C obiekcie C iC=3

A a = c; System.out.println("a.i=" + a.i + ", c.i=" + c.i); // a.i=1, c.i=3 ● ●





Nadal podklasa ma wszystkie kawałki Rzutowanie może być niebezpieczne (wyjątek vs błąd kompilacji) chociaż akurat nie tutaj super to tak jak this referencja do bieżącego obiektu tylko że przestają być widoczne deklaracje wprowadzone przez podklasę (tylko jeden poziom wyżej) Przy odwoływaniu się do atrybutów istotny jest typ statyczny!

Przygotował: Jacek Sroka

13

Dostęp do metod class A { int i = 1;

}

void info() { System.out.println("A.info() z iA=" + i); }

class B extends A { int i = 2;

}

void info() { super.info(); System.out.println("B.info() z iA=" + ((A) this).i + ", iB=" + i); }

class C extends B { int i = 3;

}

void info() { super.info();//jak wywołać info() z A? System.out.println("C.info() z iA=" + ((A) this).i +...); }

Przygotował: Jacek Sroka

14

Wynik (polimorfizm) C c = new C(); c.info(); //A.info() z iA=1 //B.info() z iA=1, //C.info() z iA=1, A a = c; a.info(); //A.info() z iA=1 //B.info() z iA=1, //C.info() z iA=1, ●

iB=2 iB=2, iC=3

iB=2 iB=2, iC=3

Przy odwoływaniu się do metod istotny jest typ dynamiczny!

Przygotował: Jacek Sroka

15

Ćwiczenie class A { void staraMetoda() { System.out.println("A.staraMetoda()"); }

}

void pisz() { System.out.println("A.pisz()"); }

class B extends A { void pisz() { System.out.println("B.pisz()"); }

}

void nowaMetoda() { System.out.println("B.nowaMetoda()"); pisz(); super.pisz(); }

Jaki będzie wynik lub czy się nieskompiluje? B b = new B(); b.staraMetoda(); //A.staraMetoda() b.pisz(); //B.pisz() b.nowaMetoda(); //B.nowaMetoda() //B.pisz() //A.pisz() A a = new B(); a.pisz(); //B.pisz() a.staraMetoda(); //A.staraMetoda()

Przygotował: Jacek Sroka

16

Ćwiczenie class A { void pisz() { System.out.println("A.pisz()"); }

}

void jakieWiązanie() { pisz(); }

class B extends A { void pisz() { System.out.println("B.pisz()"); } }

B b = new B(); b.jakieWiązanie(); //B.pisz() A a = new B(); a.jakieWiązanie(); //B.pisz()

Przygotował: Jacek Sroka

17

Ćwiczenie class A { int x = 1;

}

void testZmiennej() { System.out.println(x); }

class B extends A { int x = 2; int y = 2;

}

void testZmiennej() { System.out.println(x); System.out.println(super.x); super.testZmiennej(); }

B b = new B(); A a = new B(); b.testZmiennej(); //2 //1 //1 a.testZmiennej(); //2 //1 //1

Przygotował: Jacek Sroka

18

Przeciążanie metod ●

Metody odróżniamy na podstawie nazwy i listy parametrów



Nazwy jakie nadamy parametrom nie mają znaczenia, liczą się jedynie ich typy public void mojaMetoda(String jakaśNazwa) public void mojaMetoda(String innaNazwa)



Czasami metody wykonujemy jedynie dla ich efektów ubocznych, a zwracana wartość nas nie interesuje (więc nie służy do odróżniania metod przeciążonych) space.write(entry, transaction, lease); dodaj(4, 5); //4+5; //nie skompiluje się

Przygotował: Jacek Sroka

19

Przeciążanie i promocja wartości class PrzeciazanieMetod { void f(char x) {System.out.println("char");} void f(byte x) {System.out.println("byte");} void f(short x) {System.out.println("short");} void f(int x) {System.out.println("int");} void f(long x) {System.out.println("long");} void f(float x) {System.out.println("float");} void f(double x) {System.out.println("double");} //void f(Object o) {System.out.println("Object");} //void f(String s) {System.out.println("String");}

} ●

Jak będą promowane wartości?



char jest promowany do int (nie do short)



Dla obiektów wybierany jest typ bardziej szczegółowy

pm.f('a'); pm.f((byte) 1); pm.f((short) 1); pm.f(1); pm.f(1L); pm.f(1f); pm.f(1d); pm.f(new Object()); pm.f("Ala ma kota"); pm.f(null);

Przygotował: Jacek Sroka

20

Przeciążanie metod c.d. class PrzeciazanieMetod { void f(short b, int i) {System.out.println("short, int");} void f(int b, short i) {System.out.println("int, short");} } //pm.f(1, 1); żadna metoda nie pasuje //pm.f((short) 1, (short) 1); pasują obie pm.f(1, (short) 1); //int, short pm.f((char) 1, (byte) 1); //int, short

Przygotował: Jacek Sroka

Wielodziedziczenie a przeciążanie ●

Nie zawsze jest możliwe zdecydowanie, którą z przeciążony metod wybrać (nawet jak jest jeden parametr)

void przeciążona(IntA a) {} void przeciążona(IntB b) {} PodKl p = new PodKl(); //implementowała IntA i IntB przeciążona(p); //błąd



Natomiast przy uogólnianiu wybierana jest klasa najbardziej szczegółowa

class NadA {} class NadB extends NadA {} class NadC extends NadB {} static void przeciążona(NadA a) {System.out.println("a");} static void przeciążona(NadB b) {System.out.println("b");} NadC c = new NadC(); przeciążona(c); //wypisze się b

21

Przygotował: Jacek Sroka

22

Zagadka Wymyśl dwa interfejsy, które nie mogą być naraz zaimplementowane przez jedną klasę

Przygotował: Jacek Sroka

23

Odpowiedź interface WykluczającyA { public void metoda(); } interface WykluczającyB { public int metoda(); } //Tak się nie da! class Wykluczanie implements WykluczającyA, WykluczającyB { public void metoda() {} public int metoda() { return 0; } }

Przygotował: Jacek Sroka

24

Zagadka komentarz class A {} class B extends A {} interface NieWykluczającyA { A metoda(); } interface NieWykluczającyB { B metoda(); } //czemu to ma sens? class NieWykluczanie implements NieWykluczającyA, NieWykluczającyB { public B metoda() { return new B(); } }

Przygotował: Jacek Sroka

25

Kolejność inicjalizacji ●

Statyczne składowe nadklasy



Statyczne składowe podklasy (Pełna inicjalizacja kawałka tortu odpowiadającego nadklasie)



Zwykłe składowe nadklasy



Konstruktor nadklasy (określony w pierwszej instrukcji konstruktora podklasy)



Uwaga nad dynamiczne wiązanie (Pełna inicjalizacja kawałka tortu odpowiadającego podklasie)





Zwykłe składowe podklasy Konstruktor podklasy (trzeba zacząć od wywołania konstruktora nadklasy, domyślne konstruktory może wywołać kompilator)

Przygotował: Jacek Sroka

26

Kolejność inicjalizacji (wersja oficjalna) ●

Statyczne składowe nadklasy



Statyczne składowe podklasy



Zaczyna się inicjalizacja nadklasy



Zaczyna się konstruktor nadklasy



Wiadomo, który konstruktor podklasy użyć



Zaczyna się inicjalizacja podklasy –

Inicjalizacja składowych podklasy



Konstruktor podklasy



Kończy się inicjalizacja podklasy



Inicjalizacja składowy nadklasy



Kontynuowanie konstruktora nadklasy

Przygotował: Jacek Sroka

Przykład – klasa pomocnicza class Echo { static int getValue(String s) { System.out.println("getValue(" + s + ")"); return 1; } static int getValue() { System.out.println("getValue()"); return 1; } }

27

Przygotował: Jacek Sroka

28

Przykład c.d. class A { int x = Echo.getValue("A.x"); void pisz() { System.out.println("A.pisz()"); }

}

A() { System.out.println("A()"); pisz(); }

class B extends A { int x = Echo.getValue("B.x"); int y = Echo.getValue("B.y"); void pisz() { System.out.println("B.pisz()"); }

}

B() { super(); System.out.println("B()"); pisz(); }

B b = new B(); //getValue(A.x) //A() //B.pisz() //getValue(B.x) //getValue(B.y) //B() //B.pisz()

Przygotował: Jacek Sroka

29

Przykład interfejsy interface MójInterfejs { int X = Echo.getValue(); public static final int Y = 2; int Z = Echo.getValue();//nieużywana stała //też zostanie //zainicjowana void róbXXX(); abstract public void róbYYY(); } class MojaKlasa implements MójInterfejs { public void róbXXX() { System.out.println("róbXXX("+X+")"); } public void róbYYY() { System.out.println("róbYYY("+Y+")"); } }

MojaKlasa mk; MójInterfejs mi; mk = new MojaKlasa(); mi = mk; mi.róbYYY(); System.out.println("leniwośc"); mk.róbXXX(); //róbYYY(2) //leniwość //getValue() //getValue() //róbXXX(1) //prawdopodobnie 2 została //zinlinowana

Przygotował: Jacek Sroka

30

final ●





Atrybuty –

Stałe kompilacji będą wplatane w wyrażenia



Wartość typu podstawowego lub referencji nie może ulec zmianie



Można użyć z argumentami metod

Metody –

Nie mogą być przesłaniane



Nie ma dynamicznego wiązania, czasami kompilator może wstawiać kod metody w miejsce jej wywołania (ang. inline)

Klasy –

nie można rozszerzać

Przygotował: Jacek Sroka

31

Atrybuty final trzeba zainicjalizować class TestFinal { final int x;

}

TestFinal() { x = 1; //inicjalizacja musi nastąpić //najpóźniej w konstruktorze }

Przygotował: Jacek Sroka

32

final a private ●

Metody private i tak nie można przesłonić, ale można zdefiniować metodę o takiej samej sygnaturze

class Zagadka { // final tu nic nie zmieni private void pisz() { System.out.println("Zagadka.pisz()"); } }

Zagadka() { pisz(); }

class PseudoPrzesloniecie extends Zagadka { private void pisz() { System.out.println("PseudoPrzesloniecie.pisz()"); } }

PseudoPrzesloniecie() { pisz(); }

PseudoPrzesloniecie p = new PseudoPrzesloniecie(); //Zagadka.pisz() //PseudoPrzesloniecie.pisz()

Przygotował: Jacek Sroka

33

Zwalnianie zasobów ●



W Javie nie ma żadnych jawnych bądź niejawnych destruktorów Zasoby inne niż pamięć przyjęło się zwalniać w metodzie close() (trzeba ją samemu definiować i wywoływać) plik.close(); gniazdo.close(); PołączenieZBazą.close();





Nie musimy/możemy się martwić o zwalnianie pamięci, w której przechowywane są Javowe obiekty (sterta) i zmienne (stos), bo sami jej nie przydzielamy O obiektach, których już nie będziemy potrzebować można zwyczajnie zapomnieć: { String s = "ala"; System.out.println(s); }

Przygotował: Jacek Sroka

34

Odśmiecanie/Finalizacja ●

Zwalnianiem pamięci po obiektach zajmuje się odśmiecacz



Tylko obiekty niedostępne z żadnego aktywnego wątku programu



Odśmiecaczem steruje JVM



Programista może "podpowiedzieć”: System.gc()



Jeżeli pamięci jest dużo, odśmiecanie może nie być potrzebne







Przed odśmieceniem danego obiektu wywoływana jest odziedziczona po Object metoda finalize() O finalize() wiadomo, że będzie wywołana co najwyżej raz, dlatego nie ma sensu zwalniać w niej jakichkolwiek zasobów –

Można natomiast sprawić, że obiekt stanie się ponownie dostępny



Jako ćwiczenie proszę sprawdzić, czy przed odśmieceniem (po raz drugi) przywróconego obiektu finalize() będzie wywołana drugi raz

Przydatne metody Runtime.getRuntime().freeMemory() i Runtime.getRuntime().totalMemory()

Przygotował: Jacek Sroka

35

Finalizacja – przykład class Finalizacja { int n; String[] smieci; Finalizacja(int n) { this.n = n; smieci = new String[n]; smieci[0] = "Każda literka zaśmieca dwa bajty."; for (int i = 1; i < n; i++) smieci[i] = smieci[i - 1] + smieci[i - 1]; }

}

protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize() dla n=" + n); }

Przygotował: Jacek Sroka

Finalizacja – przykład c.d. ●

Wywołanie: for (int i = 1; i