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