(Microsoft PowerPoint - java1.ppt [tryb zgodno\234ci])
Transkrypt
(Microsoft PowerPoint - java1.ppt [tryb zgodno\234ci])
Programowanie w środowiskach graficznych Wykład 1 Radosław Wajman Krzysztof Grudzień Katedra Informatyki Stosowanej, PŁ Język java Rok 1990, Bill Joy: „utworzenie obiektowego środowiska w oparciu o C++” Język OAK (Object Application Kernel), 1991 Projekt Green, James Gosling, Patrick Naughton, Mike Sheridan Sun Developer Network http://java.sun.com/docs/books/tutorial/java/index.html Środowisko Java Obiektowy język java (podobieństwo do C++); Nazwa pliku źródłowego nazwa.java (nazwa – to nazwa klasy – publicznej -zdefiniowanej w pliku); Kompilator: *.java kod B: *.class; Maszyna wirtualna (JVM); Tworzenie kodu wykonywalnego „w locie” Just-In-Time Biblioteka Javy. Usytuowanie systemu Java Przetwarzanie programów użytkownika Cechy języka Java automatyczne odśmiecanie (garbage collection) nie dopuszcza arytmetyki wskaźnikowej ścisła kontrola typów (konwersje) obsługa wyjątków wbudowane elementy współbieżności Przykład Programem w języku Java jest aplikacja (application), aplet (applet) lub servlet (server applet). Aplikacja jest programem samodzielnym, zaś aplet jest programem wbudowanym (np. w przeglądarkę WWW) i wykonywanym po stronie klienta – servlet po stronie serwera. Każda aplikacja musi zawierać dokładnie jeden moduł źródłowy nazywany modułem głównym aplikacji, którego klasa zawiera publiczną funkcję klasy Przykład public class Hello { public static void main(String args[]) { System.out.print("Hello, World!\n"); } //end main } // end Hello Kompilacja i uruchamianie javac Hello.java B-kod: Hello.class Interpretator: java Hello Jak działa interpretator? wyszuka plik o nazwie Hello.class, czy klasa Hello zawiera publiczną metodę statyczną main wykona instrukcje zawarte w bloku main. W języku Java każda instrukcja kończy się średnikiem, który pełni rolę symbolu terminalnego. Jak działa interpretator? do metody main jako parametr jest przekazywana (z wiersza rozkazowego) tablica obiektów (łańcuchów) klasy String; metoda main nie zwraca wyniku (typem zwracanym jest void); Ciało main zawiera jedną instrukcję; System.out.print("Hello, World!\n"); Jak działa interpretator? Słowo System jest nazwą klasy w standardowym środowisku języka. Klasa System zawiera statyczny obiekt składowy typu PrintStream o nazwie out wywołanie System.out oznacza pisanie do standardowego strumienia wyjściowego. Klasa PrintStream zawiera szereg przeciążeń metody o nazwie print (tu:parametr typu String). Kompilator automatycznie konwertuje "Hello, World\n" obiekt klasy String; odnośnik (referencja) do tego obiektu przekazywana do metody System.out.print(). jest Metoda print() generuje jeden wiersz wyjściowy i powraca do metody main, która kończy wykonanie. !!! w języku Java wszystkie stałe, zmienne i funkcje są elementami składowymi klas; nie ma wielkości globalnych, definiowanych poza klasą. nie deklaruje się metod (funkcji) składowych jako rozwijalnych (inline) bądź nie – decyzja należy do kompilatora. package moje.aplikacje; import javax.swing.*; import java.awt.Container; Określenie nazwy pakietu, do którego należą klasy zdefiniowane w tym pliku. Można opuścić. Zewnętrzne pakiety (ew. pojedyncze klasy lub interfejsy), z których korzystamy w naszym programie. Można uważać ten element jako odpowiednik dyrektywy #include w C/C++. class PierwszyGUI extends JFrame { public PierwszyGUI () { ... } } Definicje klas (klasy mogą dziedziczyć, jak w tym przykładzie). Definicja konstruktora. Inna klasa. Posiada składniki, które mogą być inicjalizowane. public class KlasaStartowa { PierwszyGUI ob1; PierwszyGUI ob2 = new PierwszyGUI (); public static void main (String[] args) { ... } } Metoda main klasy startowej. To od niej rozpoczyna się wykonywanie aplikacji. Musi być to metoda public static ! Aplikacja graficzna import javax.swing.*; class GUI extends JFrame { public static void main (String[] args) { GUI gui = new GUI (); gui.setSize (300, 200); gui.show (); } } Klasa z metodą main dziedzicząca z klasy JFrame (opisującej okienko graficzne), która pochodzi z pakietu javax.swing. Korzysta z najprostszych odziedziczonych metod. Struktura programu package ... // deklaracja pakietu (niekonieczna) import ... // deklaracje importu; gdy potrzebne import ... /** Komentarz dokumentacyjny zaczyna się ukośnikiem z dwoma gwiazdkami, kończy gwiazdką i ukośnikiem */ // To jest klasa A <- to jest komentarz zwykły public class A { . . . } /* To jest komentarz wielowierszowy */ // To jest klasa B class B { ...... } Struktura programu - uwagi Program może być zapisany w jednym lub wielu plikach źródłowych (.java); wszystkie klasy składające się na program można umieścić w jednym pliku albo każdą klasę można umieścić w odrębnym pliku; w jednym pliku .java może się znajdować tylko jedna klasa publiczna; jeśli w pliku .java znajduje się klasa publiczna, to plik musi mieć dokładnie taka samą nazwę, jak ta klasa. Struktura programu - Aplikacja: jedna z klas musi zawierać metodę public static void main(String[] args), a.klasa Przykład: Work i klasa Inna zdefiniowane w pliku Work.java b.po kompilacji dwa pliki: Work.class, Inna.class. c.Wywołujemy: java Work [argumenty] Klasy (przypomnienie) class nazwy klasy { Ciało klasy... } Przed słowem kluczowym class wystąpić jeden ze specyfikatorów: abstract, public, final, lub dwa z nich: np. public abstract, public final. może Abstract: klasa abstrakcyjna; nie można utworzyć instancji (obiektu) tej klasy; final: klasa nie może mieć podklas, nie mozna po niej dziedziczyć; Brak specyfikatora: klasa dostępna tylko dla danego pakietu; Po nazwie klasy mogą wystąpić frazy: a) ‘extends nazwa_superklasy’ b) ‘implements nazwy_interfejsów’. a) klasa dziedziczy publicznie (zawsze) od jednej klasy superklasa. b) w danej klasie zostaną zdefiniowane metody, zadeklarowane w implementowanych interfejsach. W języku Java każda klasa dziedziczy od predefiniowanej klasy Object. Zatem, jeżeli w definicji klasy nie występuje fraza extends, to jest to równoważne niejawnemu wystąpieniu w tej definicji frazy ‘extends Object’. oprócz słowa kluczowego class i nazwy klasy wszystkie pozostałe elementy w deklaracji klasy są opcjonalne. kompilator przyjmie domyślnie, że klasa jest niepubliczną, nieabstrakcyjną i niefinalną podklasą predefiniowanej klasy Object. Przykład public class Point { int x, ,y; } równoważne public class Point { int x, ,y; public Point() { super(); } } przykład class Point {int x,y; Point() {x=1; y=2;}} class CPoint extends Point { public int color = 0xFF00FF;} public class Super1 { public static void main(String args[]){ CPoint cp=new CPoint(); System.out.println("cp.color="+cp.color); System.out.println("cp.x="+cp.x); }//end main }//end Super1 Metody i klasy abstrakcyjne Metoda abstrakcyjna nie ma implementacji (ciała) i winna być zadeklarowana ze specyfikatorem abstract. Klasa, w której zadeklarowano jakąkolwiek metodę abstrakcyjną jest klasą abstrakcyjną i musi być opatrzona kwalifikatorem abstract. abstract class SomeClass { int n; abstract int getSomething(); void say() { System.out.println("Coś tam"); } } Abstrakcyjność klasy oznacza, iż nie można bezpośrednio, np. w wyrażeniu new tworzyć jej egzemplarzy. Interfejs interface nazwa { /* Deklaracje metod i definicje stałych */ } Interfejsy Interfejs klasy – jest to wersja klasy pozbawiona pól składowych i definicji metod, oznacza zestaw metod i zastępuje wielodziedziczenie. Klasa może implementować wiele interfejsów Interfejs (deklarowany za pomocą słowa kluczowego interface) to: •zestaw publicznych abstrakcyjnych metod, (domyślnie – nie trzeba pisać abstract) •oraz ewentualnie statycznych stałych, (domyślnie – nie trzeba pisać final static) Implementacja interfejsu w klasie to konieczność zdefiniowania w tej klasie wszystkich! metod interfejsu. Interfejsy interface Speakable { int QUIET = 0; int LOUD = 1; String getVoice(int voice); } interface Moveable { void startMoving(); void stopMoving(); } class Pies extends Zwierz implements Speakable, Moveable { Pies () {} Pies(String s) { super(s); } String getTyp(){ return "Pies"; } public String getVoice(int voice) { if (voice == LOUD) return "HAU... HAU... HAU... "; else return "hau... hau..."; } public void startMoving() { System.out.println("Pies " + name +"biegnie"); } public void stopMoving() { System.out.println("Pies " + name +"stanął"); } } Pakiety Przykład 1: package moj_pakiet; Przykład 2: Java.awt.Button b = new Java.awt.Button("0k"); Przykład 3: import java.awt.Button; // importuje nazwę klasy Java.awt.Button class A { java.awt.Frame f = new java.awt.Frame("Tytuł"); Button b = new Button("Ok"); } Przykład 4: import java.awt.*; // importuje wszystkie nazwy klas pakietu java.awt class A { Frame f = new Frame("Tytuł");// teraz możemy użyć też prostej nazwy klasy Button b = new Button("0k");// teraz możemy użyć też prostej nazwy klasy } Pakiet java.lang nie wymaga importu (m.in. zawiera klasy String i System). Definiowanie klas •Dane są reprezentowane przez pola klasy •Operacje wprowadzamy poprzez definicje metod klasy. •Zestaw metod klasy nazywany jest jej interfejsem. •W Javie argumenty przekazywane są metodom wyłącznie przez wartość. void incr(int x) { ++x; } void przestawPary(Para pl, Para p2) { Para temp = pl; pl = p2; p2 = temp; } Konstruktory – można przeciążać, istnieje konstruktor domyślny dopóki nie zdefiniujemy własnego class Para { int a, b; Para(int x, int y) { a = x; b = y; } } class Para { int a, b; Para(int x) { a = x; b = x; } } Komunikacja z obiektami Przykład 1: TypObiektu identyfikator = new NazwaKlasy(argumenty); Przykład 2: TypObiektu identyfikator; identyfikator = new NazwaKlasy(argumenty); Para x = new Para(1,3); // Tworzy obiekt klasy Para // za pomocą konstruktora Para(int, int) Przykład 3: Para pl = new // utworzenie Para p2 = new // utworzenie Para(l,2); nowego obiektu typu Para, pl - referencja do niego Para(3,4); nowego obiektu typu Para, p2 - referencja do niego Przykład 4: void add(Para p) { a = a + p.a ; b = b + p.b; } Słowo kluczowe this SYTUACJA 1 class Para { int a; int b; void add(int a, int b){ this.a + = a ; this.b + = b; } } SYTUACJA 2 Para add(Para p) { a = a + p.a; b = b + p. b; return this; } p.add(p1).add(p2).add( p3); co jest inną formą zapisu: p.add(p1); p.add(p2); p.add(p3); SYTUACJA 3: patrz wywołanie konstruktora z innego konstruktora Składowe statyczne Uwaga: Ze statycznych metod nie wolno odwoływać się do niestatycznych składowych klasy (obiekt może nie istnieć). Możliwe są natomiast odwołania do innych statycznych składowych. Przykład: statyczne pole i metoda class Para{ static int count = 0; Para(int x, int y) { count++; a = x; b = y; } static void showCount() { System.out.println ("Liczba utworzonych par: "+count); } } Para p1 = null,p2 = null; . . . . . . if (Para.count == 0) Para p1 = new Para(1,1); else while (Para.count < 10) { p2 = new Para(2,2); p1.add(p2); } System.out.println(p1.count); Para.showCount(); p1.showCount(); p2.showCount(); Typy danych Typy proste Nazwa typu Liczba bajtów przedziały wartości Znaczenie byte l -128...127 short 2 -32768...32767 int 4 -2147483648...2147483647 long 8 -9223372036854775808 ...9223372036854775807 float 4 -3.xE+38...3.xE+38 double 8 -1.xE+308...1.xE308 char 2 -0...65556 znaki Unicodu boolean 1 true false wartości logiczne: prawda lub fałsz liczby całkowite liczby rzeczywiste Deklaracje Deklaracje typów prostych deklaracja jest zarazem definicją - wydziela w pamięci miejsce na przechowanie zmiennej danego typu. Deklaracja zmiennej typu obiektowego jest faktycznie deklaracją (i definicją) referencji do obiektu, nie wydziela natomiast w pamięci miejsca na przechowanie samego obiektu. String s ; s = "ala ma kota"; int a = l; String s = "ala ma kota"; Konwersje arytmetyczne byte, short, char float long int double double. int ii; short ss; ss = 1; ii = ss; ss = ii; błąd ss = (short)ii; OK.; Operatory i wyrażenia Priorytet, Wiązanie 1, prawe 2, lewe 3, lewe Operator Nazwa ! Negacja logiczna ~ Bitowe uzupełnienie do l + Jednoargumentowy + (liczba dodatnia) - Jednoargumentowy - (liczba ujemna) ++ Operator zwiększania -- Operator zmniejszania (typ) Konwersja (rzutowanie) * Mnożenie / Dzielenie % Reszta z dzielenia + Dodawanie - Odejmowanie Operatory i wyrażenia Priorytet, Wiązanie 4, lewe Operator Nazwa << Przesunięcie bitowe >> Przesunięcie bitowe w prawo >>> Przesunięcie bitowe w prawo bez znaku < 5, lewe 6, lewe <= >= > Operatory relacyjne instanceof Stwierdzenie typu == Operatory równości - != Nierówności Operatory i wyrażenia Priorytet, Wiązanie 7, 8. 9, 10, 11, 12, prawe 13, prawe Operator Nazwa & Bitowa koniunkcja ^ Bitowe wyłączające ALBO | Bitowa alternatywa && Logiczna koniunkcja || Logiczna alternatywa ?: Operator warunku %= += &= |= <<= Operatory przypisania Operatory x = a > b; x = a > b && c<d; //x boolean!!! String s1; String s2 = "Napis"; s1 = s2 + " tralala"; s1 = s1 + s2; int k = 1; s1 = k + k + s2; // 2Napis s1 = s2 + k + k; // Napis11 s1 = s2 + (k+k); // Napis2 s1 = k + k; // błąd kompilacji (jeden z argumentów musi być typu String) Operatory Do porównywania obiektów nie należy używać operatorów relacyjnych i równości – nierówności! String s2 = "Napis"; String s3 = new String("Napis"); boolean b; b = (s2 == s3); // porównanie referencji: wynik false b = s2.equals(s3); // wynik true. Instrukcje sterujące if (wyr) ins wyrażenie wyr musi być wyrażeniem logicznym. if (wyr) ins1 else ins2 switch (wyr) { case ws1 : ins1 .... case wsN:ins3 default : insDef } wyrażenie wyr musi być wyrażeniem całkowitym. while (wyr) ins; do ins while (wyr); for (wyr1; wyr2; wyr3) ins continue [etykieta]; break [etykieta]; return wyr; Instrukcja break int i = 0; int j = 0; outerloop: // etykieta while (i < 100) { i++; while(true) { j++; . . . . . . if (i + j > 10) break outerloop; // przerwanie pętli oznaczonej etykietą outerloop, a nie bieżącej } } Zasięg zmiennych Uwaga: W Javie instrukcje wyrażeniowe i sterujące można umieszczać wyłącznie w metodach klasy lub w bloku statycznym Zasięg identyfikatora - fragment programu, w którym może być używany. Zasięg składowych klasy – w każdym miejscu klasy (miejsce deklaracji nieistotne) Zasięg identyfikatorów definiowanych wewnątrz metody jest lokalny. Zasięg parametrów metody jest lokalny class A { int a; void metoda1(){ int b; } void metoda2() { int c; } } W metodzie metoda1() możemy odwoływać się do zmiennej a, zmiennej b oraz metody metoda2(), a w metodzie metoda2() możemy odwoływać się do zmiennej a, zmiennej c i metody metoda1. Przesłanianie nazw zmiennych TAK W konstruktorach i metodach możemy przesłaniać identyfikatory pól klasy: class A { int a; void metoda(){ int a = 0; // przesłonięcie a = a + 10; // zmienna lokalna; this.a++; // pole klasy } NIE W Javie nie wolno przesłaniać zmiennych lokalnych w blokach wewnętrznych: int a; { int a; // BŁĄD kompilacji } Czas życia zmiennych Zmienne lokalne i referencje - od momentu deklaracji do wyjścia sterowania z bloku Obiekty – od chwili wykonania operacji new – do chwili usunnięcia przez garbage collector class A { String s ; void metoda1() { String pies = new String("Pies"); s = pies; } void metoda2(){ System.out.println("Jest " + s); } } Obiekt „pies" został stworzony w metodzie1. Referencja pies po zakończeniu działania tej metody przestaje istnieć. Ponieważ jednak referencja s wskazuje na obiekt „pies”, to obiekt nadal istnieje. Możemy się do niego odwołać w metodzie2. Specyfikatory dostępu Dostęp do pól i metod klasy specyfikator dostępu Opis pola/metody prywatny private dostępna tylko w danej klasie zaprzyjaźniony / pakietowy - (ew package) dostępna ze wszystkich danego pakietu chroniony protected dostępna z danej klasy i wszystkich klas ją dziedziczących publiczny public dostępna zewsząd klas Inicjalizacja pól Przy tworzeniu obiektu pola klasy mają zagwarantowaną inicjalizację na wartości ZERO (0, false - dla typu boolean, null - dla typów obiektowych, nie dotyczy to lokalnych zmiennych. PRZYKŁAD JAWNEJ INICJALIZACJI class Para { static String text = "Pary"; static int x = -1; int a = x+1; int b = x+1; Para() { a++; b++; } static String getText() { return text; } void show(){ System.out.println("( " + a + "," + b + ")"); } } Kolejność inicjalizacji pól REGUŁY DOTYCZĄCE KOLEJNOŚCI: • każde odwołanie do klasy inicjalizuje najpierw pola statyczne • tworzenie obiektu (new) inicjalizuje pola niestatyczne, po czym wykonywany jest konstruktor • kolejność inicjalizacji pól - według ich kolejności w definicji klasy (w podziale na statyczne i niestatyczne, najpierw statyczne) TAK class Para{ static String text = "Pary"; int a = x+1; int b = x+1; Para() { ... } Para(int x){ ... } Para(int x, int y) { ... } static int x = -1; } NIE (błąd: forward reference) class Para { static int y = x + 1; int a = x+1; int b = x+1; static int x = - 1; } Typ tablicowy Deklaracja tablicy tworzy referencję. Taka deklaracja nie alokuje pamięci dla samej tablicy. int[] arr; // arr jest referencją Można pisać też int arr[]; // arr jest zmienną typu int[], który jest typem obiektowym Alokowanie pamięci. Rozmiar tablicy może być ustalany dynamicznie podczas działania programu arr = new int[l0]; int[] tab = new int[n]; Tablice - przykłady Przykład: Przetwarzanie wszystkich elementów tablicy public static void main(String[] args) { for (int i = 0; i < args.length; i++) System.out.println(args[i]); } Przykład: Przekroczenie zakresu tablicy – kontrolowanie ilości parametrów programu public static void main(String[] args){ try{ System.out.println(args[0] + " " + args[1]);} catch(IndexOutOfBoundsException e){ System.out.println("Syntax: ... "); System.exit(1);} } Tablice - przykłady Tablice elementów obiektowych – wymaga inicjalizacji wszystkich obiektów - elementów = Button[] tab new Button[n]; for (int i = 0; 1 < tab.length; i++) tab[i] = new Button("Przycisk " + i); Inicjalizatory klamrowe int a[] ={1, 2, 3, 4, 5, 3}; //ale tylko w wierszu deklaracji tablicy Button b[] ={ new Button("A"), new Button("B") , new Button("C"), /* tu przecinek dozwolony*/ }; Tablice - w przypisaniach byte[] b1 = {l, 2, 3 }; byte[] b2 = {l, 2, 3, 5, 5 }; b2 = b1; Tablice - przykłady Tablice jako argumenty metod void chgtab(byte[] t){ for( int i=0; i < t.length; i++) t[i]++; } byte[] b1 = {1, 2, 3 }; chgtab(b1); Tablice różnych obiektów (takie tablice także jako argument funkcji) Component[] tab=new Component[]{new Button("A"), new Button("B"), new Label("A"), new Label("B"),}; for(int i=0;i<tab.length;i++) tab[i].setBackground(Color.blue); Tablice wielowymiarowe Przykładowe sposoby deklaracji i inicjalizacji: 1. inicjalizacja w nawiasach klamrowych int[][] macierz1 = {{ l, 2, 3 }, { 4, 5, 6}}; 2. dynamicznie int[][] macierz2 = new int[n][m]; Tablice wielowymiarowe Przykładowe sposoby deklaracji i inicjalizacji: 3. tablica składa się z wektorów o różnych rozmiarach public static void main(String[] arg) { int w[] = { 2, 3, 4 }; int n = 3; int[][] m3 = new int[n][]; // rozmiary wierszy będą zm. Dynam. for(int i = 0; i < m3.length; i++){ m3[i] = new int[w[1]]; for (int j = 0; j < m3[i]. m3[i].length length; j++) = m3[i][j] i + j; } for (int i = 0; i < m3.length; i++) { System.out.println("Rozmiar i-go wiersza " + m3[i].length); String out = " "; for(int j = 0; j < m3[i].length; j++) out += " " + m3[i][j]; System.out.println(out); } } Przeciążanie metod void add(Para p) { a += p.a; b += p.b;} void add(int i) { a += i; b += i; } void add(int i, int k){a += i; b += k; } p.add(jakasPara); p.add(3); p.add(1,2); Przeciążenie konstruktorów Para(int x) { a = x; b = x; } // oba pola inicjalizowane tą samą liczbą Para(int x, int y) { // każde pole inicja1izowane inną liczbą a = x; b = y; } Para() { this(1); } Para(int x) {this(x,x);} Para(int x, int y) { a = x; b = y; } Dziedziczenie Sekwencja budowania obiektu klasy pochodnej 1. 2. 3. 4. wywoływany jest konstruktor klasy pochodnej jeśli pierwszą instrukcją jest super(args), wykonywany jest konstruktor klasy bazowej z argumentami args, jeśli nie ma super(...), wykonywany jest konstruktor bezparametrowy klasy bazowej, wykonywane są instrukcje wywołanego konstruktora klasy pochodnej. Uwaga: Java nie uwzględnia wielodziedziczenia Podstawowe różnice między kompozycją a dziedziczeniem: •dziedziczenie pozwala wykorzystywać istniejący interfejs na rzecz obiektu klasy pochodnej, kompozycja - nie, •dziedziczenie oznacza, że obiekt klasy pochodnej może być traktowany tak samo jak obiekt typu definiowanego przez nadklasę. Obiektowe konwersje rozszerzające Każdy obiekt możemy przekształcić w obiekt jego (dowolnej) nadklasy. Nazywa się to konwersją rozszerzającą (rzutowaniem w górę) - upcasting, (up - bo w górę hierarchii dziedziczenia). Obiektowe rzutowanie w górę dokonywane jest automatycznie (nie musimy stosować operatora konwersji) przy: • przypisywaniu zmiennej-referencji odniesienia do obiektu klasy pochodnej, • przekazywaniu argumentów, gdy parametr jest typu nadklasy argumentu, • zwrocie wyniku, gdy wynik podstawiamy na zmienną oznaczającą obiekt nadklasy zwracanego wyniku. Obiektowe konwersje rozszerzające static Para sum(Para pl, Para p2, Para p3) { Para p = new Para(0,0); p.add(pl); p.add(p2); p.add(p3); return p; } Para p1 = new Para(1,2); ParaInh pi1 = new ParaInh(3,4), pi2 = new ParaInh(5,6); Para wynik = sum(pp1, pi1, pi2); Zastosowanie polimorfizmu class Zwierz { String name = "nieznany"; Zwierz() {} Zwierz(String s) {name = s; } String getTyp() {return "Jakiś zwierz";} String getName() {return name;} String getVoice(){return "?";} void speak() { System.out.println( getTyp()+" "+getName()+" mówi " +getVoice()); } } class Pies extends Zwierz { Pies(){} Pies(String s) { super(s); } String getTyp() { return "Pies"; } String getVoice() { return "HAU, HAU!"; } } class Kot extends Zwierz { Kot() {} Kot(String s) { super(s); } String getTyp() { return "Kot"; } String getVoice() { return "Miauuuu..."; } } Zastosowanie polimorfizmu class Main { public static void main(String[] arg) { Zwierz z1 = new Zwierz(), z2 = new Zwierz(); Pies pies = new Pies(), kuba = new Pies("Kuba"); Pies reksio = new Pies("Reksio"); Kot kot = new Kot(); Dialog(z1, z2); Dialog(kuba, reksio); Dialog(kuba, kot); Dialog(reksio, pies); } static void Dialog(Zwierz z1, Zwierz z2){ z1.speak(); z2.speak(); System.out.println("---------------"); } } Wynikiem działania programu będzie następujący wydruk Jakiś zwierz nieznany mówi ? Jakiś zwierz nieznany mówi ? Pies Kuba mówi HAU, HAU! Pies Reksio mówi HAU, HAU! Pies Kuba mówi HAU, HAU! Kot nieznany mówi Miauuuu... Pies Reksio mówi HAU, HAU! Pies nieznany mówi HAU, HAU! Metody wirtualne Wszystkie metody w Javie są wirtualne za wyjątkiem: • metod statycznych, • metod deklarowanych ze specyfikatorem final, • metod prywatnych (dla których odwołania w innych metodach danej klasy nie są polimorficzne). Dynamic binding (Late binding) – mechanizm pozwalający wykryć na jaki obiekt wskazuje referencja i zastosować odpowiednią metodę.