Programowanie obiektowe Wykład 02 Maciej Wołoszyn mailto:woloszyn@fatcat.ftj.agh.edu.pl 10 marca 2009 Spis treści 1 Klasa String 2 2 Przeładowywanie metod 5 2.1 Konstruktory.................................. 6 3 this 7 4 Składowe i klasy final 8 4.1 Pola....................................... 8 4.2 Metody..................................... 9 4.3 Klasy...................................... 10 5 Odśmiecanie pamięci 10 6 Dziedziczenie 11 6.1 Klasa Object.................................. 11 6.2 Składnia..................................... 11 6.3 Inicjalizacja obiektów.............................. 12 6.4 Zasłanianie nazw................................ 12 6.5 Polimorfizm................................... 13 Proszę o przesyłanie na ten adres informacji o znalezionych błędach, literówkach oraz propozycji zmian i uzupełnień. Dokument przygotowano za pomocą systemu LATEX. Wszelkie prawa zastrzeżone. 1
Programowanie obiektowe. Wykład 02 2 1 Klasa String klasa biblioteczna służąca do obsługi napisów inicjalizacja możliwa w sposób taki sam jak dla zmiennych typów podstawowych: String s = "napis"; po utworzeniu obiektu klasy String treść napisu nie może już zostać zmodyfikowana (jeśli zachodzi potrzeba modyfikowania napisu, to można użyć StringBuilder/StringBuffer patrz dalej) dostępnych jest wiele konstruktorów (Doc. API) String s1 = new String(); String s2 = new String("inny napis"); String s3 = new String(s2); System.out.println("s1=["+s1+"]"); System.out.println("s2=["+s2+"]"); System.out.println("s3=["+s3+"]"); s1=[] s2=[inny napis] s3=[inny napis] niektóre metody klasy String: int length() zwraca długość napisu (ilość znaków zapisanych w Unikodzie) System.out.println( "L[s1]=" + s1.length() + " L[s2]=" + s2.length() ); L[s1]=0 L[s2]=10 boolean equals(object anobject) 1 zwraca true tylko gdy argument nie jest null i reprezentuje identyczną sekwencję znaków System.out.println( "s1.eq.s2 : " + s1.equals(s2) + ", s2.eq.s3 : " + s2.equals(s3) ); 1 wiecej na ten temat przy omawianiu klasy Object
Programowanie obiektowe. Wykład 02 3 s1.eq.s2 : false, s2.eq.s3 : true Dla porównania: System.out.println( "s1==s2 : " + s1==s2 + ", s2==s3 : " + s2==s3 ); s1==s2 : false, s2==s3 : false char charat(int k) zwraca k-ty znak napisu (lub rzuca wyjątek gdy k wskazuje na miejsce poza napisem) String w = "abcdef"; System.out.println( "w_5 = [" + w.charat(5) + "]" ); w_5 = [f] int compareto(string S) zwraca: 0 jeśli argument S jest identyczny z aktualnym napisem, wartość mniejsza od zera gdy aktualny napis jest mniejszy (leksykograficznie) niż argument S, wartość większa od zera w przeciwnym przypadku.... i wiele, wiele innych! String a = "Nowak"; String b = "Nowacki"; String c = "nowy"; System.out.println( "a-b: " + a.compareto(b) + ", b-c: " + b.compareto(c) + ", c-a: " + c.compareto(a) ); a-b: 8, b-c: -32, c-a: 32 Jak z liczby w prosty sposób otrzymać napis? double x = 0.5; String sx = new Double(x).toString(); System.out.println(sx);
Programowanie obiektowe. Wykład 02 4 0.5 double x = 0.5; String sx = String.valueOf(x); System.out.println(sx); 0.5 double x = 0.5; String sx = ""+x; System.out.println(sx); 0.5 jeśli w miejscu, w którym spodziewany jest obiekt typu String pojawi się obiekt innego typu, to automatycznie zostanie dla niego uruchomiona funkcja public String tostring() (zdefiniowana w nadrzędnej klasie Object lub redefiniowana w klasie pochodnej) Jak z napisu otrzymać liczbę? String s = "-1.2345e2"; double x= Double.valueOf(s).doubleValue(); System.out.println("x = " + x); x = -123.45 lub krócej: double x = Double.valueOf(s); class N { int k; class M { int k; public String tostring() { String s = "M["+k+"]"; return s; N on = new N(); on.k = 2; M om = new M();
Programowanie obiektowe. Wykład 02 5 om.k = 8; System.out.println(on); System.out.println(om); N@7d772e M[8] do obsługi napisów, które mogą być modyfikowane można użyć obiektów klas String- Buffer lub StringBuilder różnią się one tym, że StringBuilder nie jest synchronizowana (nie zapewnia bezpieczeństwa przy wielowątkowości), ale w zamian ma lepszą wydajność jeśli obiekt przechowujący tekst będzie używany w wielu wątkach, to należy zastosować StringBuffer konkatenacja ( sklejanie ) napisów za pomocą operatora + i klasy String jest znacznie mniej wydajne od użycia metody append i klas StringBuffer lub StringBuilder String s = new String("AB"); String s2 = s; s += "CD"; System.out.println(s); System.out.println(s==s2); ABCD, false StringBuilder sb = new StringBuilder("ab"); StringBuilder sb2 = sb; sb.append("cd"); System.out.println(sb); System.out.println(sb==sb2); abcd, true 2 Przeładowywanie metod przeładowywanie wszystkich metod realizuje się na zasadach takich jak w C++, tzn. wymagana jest unikalna lista argumentów
Programowanie obiektowe. Wykład 02 6 2.1 Konstruktory domyślny zostanie utworzony automatycznie, jeśli nie zdefiniujemy żadnego konstruktora; nie zostanie utworzony automatycznie, jeśli zdefiniujemy jakikolwiek konstruktor class C { C() { System.out.println("[C]"); [C] C c1; //System.out.println(c1); /* ZLE! */ C c2 = new C(); w odróżnieniu od C++ nie ma konstruktorów kopiujących Uwaga: w klasie Object jest metoda protected Object clone() jaka jest jej rola? brak mechanizmu listy inicjalizacyjnej; inicjalizacja wewnątrz konstruktora następuje po wcześniejszej automatycznej inicjalizacji zerami lub innymi wartościami określonymi przy deklarowaniu pól klasy class L { double x = Math.sqrt(2); String s = new String("sqrt(2)="); L() { System.out.println(s+x); x = Math.sqrt(4); s = new String("sqrt(4)="); L l1 = new L(); System.out.println(l1.s+l1.x); sqrt(2)=1.4142135623730951 sqrt(4)=2.0
Programowanie obiektowe. Wykład 02 7 inicjalizacja każdego pola statycznego odbywa się tylko raz przy utworzeniu pierwszego obiektu klasy, do której należy pole, lub przy pierwszym bezpośrednim odniesieniu się do takiego pola class K { static int i; static int j = i+1; System.out.println("K.j="+K.j); K.i=10; K k1 = new K(); System.out.println("K.j="+K.j); K.j=1 K.j=1 3 this this jest referencja do aktualnego obiektu (dostępną tylko wewnątrz metod) pozwala np. zwrócić referencję do aktualnego obiektu z użyciem return this; class P { int k; P printplus() { k++; System.out.print("[k="+k+"] "); return this; P p = new P(); p.printplus().printplus(); [k=1] [k=2] metody static nie mogą używać referencji this słowo kluczowe this umożliwia również wywoływanie konstruktora z innego konstruktora (można uniknąć powielania kodu) wywołanie następuje za pomocą this(argumenty);
Programowanie obiektowe. Wykład 02 8 musi być ono pierwszą instrukcją w konstruktorze (nie można także wywołać po sobie kilku konstruktorów) nie może wystąpić w metodzie innej niż konstruktor P(int k){ this.k = k; P(){ // System.out.println(); /* ZLE */ this(-1); P p1 = new P(); System.out.println(p1.k); -1 Przykład: Dodanie do klasy P metody: void set(int n) { this(n); // ZLE!!! <---linia 5 spowoduje następujący błąd kompilacji: Program.java:5: call to this must be first statement in constructor this(n); ^ 1 error 4 Składowe i klasy final 4.1 Pola odpowiednik danych składowych const w C++ zmienne, których wartość nie może być modyfikowana często stosowana konwencja: zapisywanie nazw pól final dużymi literami, np. final double PI2 = 6.2831853; Uwaga! w przypadku referencji niezmienność dotyczy samej referencji, a nie obiektu, na który ona pokazuje! Zastrzeżenie to dotyczy także tablic (które też są zawsze obiektami)!
Programowanie obiektowe. Wykład 02 9 class N { int n; N(int t) { n=t; final N f = new N(1); N g = new N(9); System.out.println("f="+f.n+" g="+g.n); f.n = 8; System.out.println("f="+f.n+" g="+g.n); // f = g; /* ZLE */ g = f; g.n = 7; System.out.println("f="+f.n+" g="+g.n); f=1 g=9 f=8 g=9 f=7 g=7 można definiować puste (blank) pola final musza one zostać zainicjalizowane przed użyciem (w każdym konstruktorze! a więc nie można np. poprzestać na automatycznie generowanym konstruktorze domyślnym), a później ich wartość nie może ulegać zmianom; umożliwiają przechowywanie różnych wartości dla różnych obiektów tej samej klasy class M { final int NR; M(int i) { NR = i; M m = new M(777); // m.nr = 3; /* ZLE */ 4.2 Metody metody zadeklarowane jako final nie mogą być redefiniowane (przesłaniane) w klasach pochodnych Uwaga: zupełnie inne znaczenie niż funkcje składowe const w C++! odwołania do nich mogą być zrealizowane przez kompilator jako inline
Programowanie obiektowe. Wykład 02 10 4.3 Klasy także klasy mogą zostać zdefiniowane jako final oznacza to, że nie można po takiej klasie dziedziczyć wszystkie metody takiej klasy będą również typu final (nawet jeśli tego nie napiszemy w deklaracji) 5 Odśmiecanie pamięci zawarty w Javie odśmiecacz pamięci (garbage collector) zajmuje się automatycznym zwalnianiem pamięci przydzielonej operatorem new czyli usuwaniem niepotrzebnych, niewykorzystywanych już obiektów nie ma w związku z tym odpowiednika znanego z C++ delete znaczne ułatwienie, ale kosztem efektywności do zwolnienia innej, niezarezerwowanej poprzez new pamięci, Java używa metody finalize() może się przydać np. gdy korzystamy z tzw. native methods, czyli wywoływania przez Javę kodu napisanego w innych językach (C, C++) ponieważ finalize() jest uruchamiana dopiero gdy zadziała garbage collector, należy pamiętać, że: obiekty mogą nie zostać nigdy obsłużone przez odśmiecacz (aby zwiększyć efektywność może się on nie uruchamiać dopóki jest wystarczająca ilość wolnej pamięci!) odśmiecanie nie jest tym samym co destrukcja w C++, a metoda finalize() nie jest odpowiednikiem destruktora! nawet przy zakończeniu działania programu zwrócenie zajętej pamięci do systemu nie oznacza włączenia odśmiecania nie ma takiej potrzeby, po prostu pamięć jest zwalniana hurtem Wniosek: jeśli chcemy mieć pewność, że jakaś operacja (np. zamknięcie pliku) zostanie wykonana zanim stracimy dostęp do obiektu, to musimy zadbać o zdefiniowanie oraz wywołanie specjalnie do tego przeznaczonej metody! do celów testowych, zwłaszcza podczas debugowania programu, przydatne może być wymuszenie uruchomienia odśmiecacza służy do tego metoda System.gc();
Programowanie obiektowe. Wykład 02 11 6 Dziedziczenie 6.1 Klasa Object przy definiowaniu każdej klasy w Javie korzystamy z dziedziczenia wszystkie klasy są pochodnymi klasy Object zawierającej m.in. metody: 6.2 Składnia String tostring() protected void finalize() boolean equals(object obj) protected Object clone() do określania klasy podstawowej Java używa słowa kluczowego extends, np. class Kolo extends Figura { /*... */ odwołanie do składników klasy bazowej poprzez super class Figura { String name = "Jakas figura"; protected double x,y; public double getx() { return x; public double gety() { return y; public String tostring() { return name+" ("+x+","+y+")"; public void setname(string s){ name=s; class Kolo extends Figura { double r = 1.0; public void setname(string s){ super.setname("kolo "+s); public void setr(double r0) { r=r0; Kolo k = new Kolo(); System.out.println(k); k.setname("k"); System.out.println(k);
Programowanie obiektowe. Wykład 02 12 Jakas figura (0.0,0.0) Kolo k (0.0,0.0) 6.3 Inicjalizacja obiektów przed inicjalizacją obiektu klasy pochodnej automatycznie wywoływany jest konstruktor domyślny klasy podstawowej; jeśli taki nie istnieje (lub nam nie odpowiada), to możemy spowodować uruchomienie innego konstruktora za pomocą instrukcji super(argumenty); umieszczonej na samym poczatku konstruktora klasy pochodnej Przykład: po dopisaniu do klasy Figura definicji konstruktora: Figura(double x0, double y0){x=x0;y=y0; program nie skompiluje się błąd: cannot find constructor Figura() wyjściem może być np. dodanie do klasy Kolo definicji: Kolo(){ super(0,0); lub Kolo(double x0,double y0,double r0){ super(x0,y0); r=r0;... i wtedy: Kolo k2 = new Kolo(2,-4.5,7); k2.setname("k2"); System.out.println(k2); Kolo k2 (2.0,-4.5) 6.4 Zasłanianie nazw w odróżnieniu od C++ redefiniowanie w klasie pochodnej metody, która była kilkakrotnie przeładowana w klasie podstawowej nie zasłania żadnej z pozostałych wersji
Programowanie obiektowe. Wykład 02 13 class A { String f() { return "A.f() "; class B extends A { String f(int i) { return "B.f(int)"; System.out.println("b.f()-->"+b.f()); System.out.println("b.f(1)-->"+b.f(1)); b.f()-->a.f() b.f(1)-->b.f(int) 6.5 Polimorfizm obiekty klasy pochodnej mogą być uznawane za rodzaje obiektów klasy podstawowej referencja do typu pochodnego może być zawsze przekazana w miejsce, gdzie oczekiwana jest referencja typu podstawowego class C { static void p(c x){system.out.println(x); class D extends C { C c = new C(); D d = new D(); C.p(d); C.p(c); D@7d772e C@11b86e7