Definicje klas i obiektów
Transkrypt
Definicje klas i obiektów
Definicje klas i obiektów Tomasz Borzyszkowski Podstawy Do tej pory używaliśmy klas jedynie po to, by zdefiniować metodę main(). Klasy mają znacznie szersze zastosowanie w Java. W OOP (także w Java) klasy służą do definiowania nowych typów danych o znacznie większej sile wyrazu niż typy tradycyjne. Mając definicję klasy możemy tworzyć obiekty typu danej klasy. Klasa pełni rolę wzorca (schematu budowy) obiektu. Obiekty danej klasy nazywamy instancjami klasy. Klasy w Java pozwalają na zastosowanie idei hermetyzacji. Osiąga się to przez wyspecyfikowanie danych i kodu klasy, operującego na danych. Proste klasy mogą zawierać tylko dane lub tylko kod lecz większość klas zawiera zarówno kod jak i dane. Zwykle użytkownik nie ma bezpośredniego dostępu do danych obiektu, natomiast ma do dyspozycji metody dostępu do danych, przewidziane w klasie. Klasa definiuje więc interfejs do danych swoich instancji (właściwie: zmiennych instancji). 2 Ogólny schemat klasy W Java definicja klasy rozpoczyna się słowem kluczowym class. Ogólna postać definicji klasy jest następująca: class nazwaKlasy { typ zmiennaInst_1; // ... typ zmiennaInst_n; Zobacz: BoxDemo.java BoxDemo2.java typ metoda_1(parametry) { // ciało metody_1 } // ... typ metoda_n(parametry) { // ciało metody_n } } // koniec definicji klasy 3 Deklaracja obiektów Wytworzenie obiektu z danej klasy to zadanie dwuetapowe: Po pierwsze: trzeba zadeklarować zmienną typu danej klasy. Zmienna taka nie jest obiektem. Jest tylko zmienną mogącą wskazywać na obiekt. Kod Box myBox; Efekt null myBox Po drugie: trzeba utworzyć obiekt danej klasy i przypisać go do zmiennej. Operator new dynamicznie rezerwuje (w czasie wykonania) obszar pamięci na obiekt i zwraca wskazanie do tego obszaru. width Efekt Kod myBox = new Box(); height myBox depth 4 Konstruowanie obiektu Postać ogólna: zmiennaKlasy = new nazwaKlasy(); nazwaKlasy jest nazwą klasy, której obiekt właśnie powołujemy. Nazwa ta, waraz z ewentualnymi parametrami wyznacza konstruktor klasy. Konstruktor to specjalna metoda, o nazwie takiej jak klasa. Definiuje ona co trzeba zrobić by utworzyć obiekt danej klasy. Jeżeli klasa nie posiada konstruktora, to jest wywoływany konstruktor domyślny. Po utworzeniu konstruktora dla klasy konstruktor domyślny nie jest dostępny. Konstruktory nie posiadają typu wynikowego, nawet void. Dzieje się tak dlatego, że niejawnym typem wynikowym każdego konstruktora jest klasa, której obiekt konstruktor tworzy. Prócz powołania obiektu, zadaniem konstruktora jest jego inicjalizacja, tak by po utworzeniu był w pełni sprawnym obiektem. Zobacz: BoxDemo3.java 5 Słowo kluczowe this Czasem metoda obiektu potrzebuje odwołać się do obiektu wywołującego tę metodę. Umożliwia to słowo kluczowe this. Wewnątrz metody this oznacza obiekt, na rzecz którego wywołano metodę. this jest często używany do tworzenia metod polimorficznych, ale o tym opowiemy później. W Java nie można deklarować dwóch takich samych zmiennych lokalnych w obrębie jednego zakresu, jednak istnieje możliwość przesłonięcia zmiennych instancyjnych klasy przez parametry metody. Wewnątrz metody zmienne instancyjne byłyby niedostępne, gdyby nie możliwość użycia zmiennej this: Box (double width, double height, double depth){ this.width = width; this.height = height; this.depth = depth; } 6 Zbieracz śmieci Operator new dynamicznie rezerwuje pamięć na tworzony obiekt. Można się zastanowić jak obiekty są niszczone a ich pamięć zwalniana. W niektórych językach (np. C++) obiekty zarezerwowane dynamicznie muszą być zwalniane ręcznie poleceniem delete (lub podobnym). W Java przyjęto inne rozwiązanie. JVM posiada mechanizm zwany zbieraczem śmieci (ang.: garbage collection), który automatycznie zwalnia pamięć przydzieloną obiektom, do których brak jakichkolwiek odwołań. Zbieracz śmieci włącza się sam, gdy jest co posprzątać. Nie ma jednej reguły jego włączania i wyłączania się. Różne JVM posiadają różne algorytmy zbierania śmieci, lecz mają jedną wspólną cechę: programista nie musi pamiętać o zwalnianiu pamięci, jak i o samym zbieraczu śmieci. 7 Metoda finalize Ze zbieraczem śmieci związana jest metoda finalize. Zbieracz śmieci zaraz przed zwolnieniem obiektu uruchamia metodę finalize zwalnianego obiektu. Metoda ta jest stosowana, gdy obiekt rezerwuje pewne nie-Javowe zasoby, które muszą być zwolnione w specjalny sposób, np: deskryptory plików, czy fonty. Na metodzie tej nie możemy opierać poprawności implementowanego algorytmu, ponieważ nie wiemy, kiedy zostanie wywołana przez zbieracz śmieci. Postać metody: protected void finalize() { // tu kod metody } 8 Przeciążanie metod W Java możemy definiować wiele metod o tej samej nazwie w obrębie jednej klasy. Metody takie nazywamy przeciążonymi. Muszą one różnić się typami i/lub liczbą argumentów. Mogą także różnić się typami wynikowymi ale nie wystarcza to by metody były różne. Zobacz: OverloadDemo.java Metody przeciążone wspierają polimorfizm. W językach nie pozwalających na przeciążanie metod/funkcji często dla wyrażenia podobnych operacji dla różnych typów wejściowych trzeba używać różnych nazw. Np. w C funkcja abs() zwraca wartość bezwzględną liczby całkowitej, labs() długiej liczby całkowitej, fabs() liczby rzeczywistej, ... . W Java dzięki przeciążaniu metod można zdefiniować jedną nazwę metody działającą na różnych typach. Zobacz: AbsDemo.java W podobny sposób jak zwykłe metody, możemy przeciążać także konstruktory (patrz ponownie na przykład BoxDemo3.java) 9 Przekazywanie parametrów Obiekty jako parametry metod. Ponieważ klasy w Java są traktowane jak typy w tradycyjnych językach programowania, to można przekazywać obiekty (parametry typu klasowego) do metod i zwracać obiekty jako wartości. Zobacz: ParameterDemo.java Przekazywanie parametrów metod przez wartość i zmienną. W Java sposób przekazywania parametrów do metody zależy od ich typu. Parametry typów prostych zawsze są przekazywane przez wartość, natomiast przekazywanie obiektów do metod zawsze odbywa się przez zmienną. Przyponienie pojęć: Przekazywanie przez wartość: przekazywane wartości nie ulegają trwałym zmianom - po wywołaniu metody wartość jak przed Przekazywanie przez zmienną: przekazywane wartości ulegają trwałym zmianom - po wywołaniu metody wartość się zmienia Zobacz: CallByDemo.java 10 Kontrola dostępu Klasy definiowane dotychczas nie posiadały ważnej cechy programowania obiektowego: hermetyzacji, tj. oddzielenia świata zewnętrznego od danych klasy (patrz Stack.java). Java pozwala na kontrolę dostępu na poziomie pakietu, klasy i komponentu klasy. Realizowana jest ona za pomocą słów kluczowych (specyfikacji kontroli dostępu): public elementy są dostępne dla dowolnego innego kodu programu także spoza klasy private dla elementu klasy sprawia, że jest on dostępny tylko dla innych elementów tej samej klasy protected ma zastosowanie w procesie dziedziczenia klas (omówimy później) Domyślną specyfikacją kontroli dostępu, tj. gdy nic nie wyspecyfikowano, jest public. Zobacz: AccessDemo.java i Stack2.java 11 Elementy static Zobacz: Static1.java i Static2.java Zwykle by móc używać elementów zdefiniowanych w klasie, musimy najpierw utworzyć obiekt będący instancją danej klasy. Istnieje jednak możliwość zdefiniowania elementów, do których nie musimy się odwoływać za pośrednictwem obiektów. Do wyróżnienia takich elementów służy słowo kluczowe static. Typowym przykładem jest metoda main(). static może być stosowane do: Zmiennych instancji: są one wówczas zmiennymi globalnymi wszystkich obiektów danej klasy. Utworzenie obiektu danej klasy nie powoduje powstania statycznej zmiennej instacji. Metod: podlegają one następującym restrykcjom: Mogą wywoływać tylko inne metody statyczne Mogą korzystać wyłącznie ze zmiennych statycznych Nie mogą odwoływać się do zmiennych this i super Jeżeli trzeba wykonać obliczenia by zainicjalizować zmienne statyczne, można zdefiniować blok static. Będzie on wykonany tylko raz, gdy klasa jest po raz pierwszy załadowana. 12 Elementy final Słowo kuczowe final służy do oznaczenia zmiennych, których wartość ma być niezmienna we wszystkich instancjach. Pełnią one rolę stałych i muszą być inicjalizowane przy deklaracji. Przykłady: final int FILE_NEW = 1; final int FILE_OPEN = 2; final int FILE_SAVE = 3; final int FILE_QUIT = 4; Dalsza część programu może używać powyższych zmiennych jak stałych. Dodatkowo nie zajmują one miejsca w instancjach klasy. Przyjętą konwencją jest pisanie zmiennych final dużymi literami. Słowo kuczowe final może również być zastosowane do metod, lecz znaczenie takiego zastosowania jest zupełnie inne niż w przypadku zmiennych. Zastosowanie final do metod wyjaśnimy podczas omawiania dziedziczenia klas. 13 Tablice Omawiając tablice nie wspomnieliśmy, że są one implementowane jako obiekty. Jako obiekty posiadają specjalny atrybut length reprezentujący liczbę elementów jakie tablica może przechowywać (a nie ich aktualną liczbę). class Length { public static void main(String args[]) { int a1[] = new int[10]; int a2[] = {1, 2, 3, 4, 5, 6, 7, 8}; int a3[] = {4, 3, 2, 1}; int c = a1.length + a2.length + a3.length; System.out.println(c); } } Wynik: 22 Zobacz: Stack3.java 14 Klasy wewnętrzne Zobacz: Inner1.java Inner2.java W Java istnieje możliwość definiowania klasy wewnątrz innej klasy. Klasy takie nazywa się klasami zagnieżdżonymi. Zakres klasy wewnętrznej jest ograniczony klasą zewnętrzną, tj.: Jeżeli klasa B jest zdefiniowana wewnątrz klasy A, wówczas B jest znana wewnątrz A, natomiast nie jest poza A. Klasa wewnętrzna posiada dostęp do wszystkich elementów klasy zewnętrznej (nawet prywatnych), natomiast klasa zewnętrzna nie posiada dostępu do elementów klasy wewnętrznej. Istnieją dwa rodzaje klas wewnętrznych: Statyczne: zdefiniowane ze słowem kluczowym static. Nie mogą odwoływać się one do elementów klasy zewnętrznej (niestatycznej) wprost. Muszą zdefiniować obiekt klasy zewnętrznej i odwoływać się za jego pośrednictwem; rzadko używane. Niestatyczne: najczęściej używane. Mają dostęp do wszystkich elementów klasy zewnętrznej tak jak jej metody niestatyczne 15 Klasa String Zobacz: StringDemo.java Klasa String jest prawdopodobnie najczęściej używaną klasą języka Java, ponieważ każdy napis używany w programie jest obiektem klasy String. Dotyczy to także napisów stałych, np.: System.out.println(“To jest napis”); Napis “To jest napis” jest obiektem klasy String. Obiekty klasy String są niezmienne, tj. raz utworzone nie mogą zmieniać swojej zawartości. Jeżeli chcemy zmienić zawartość napisu, trzeba utworzyć nowy zmodyfikowany. W Java istnieje także klasa StringBuffer pozwalająca na dokonywanie zmian w przechowywanych napisach. Obiekty klasy String posiadają metody (m.in.) boolean equals(String o) równość dwóch napisów int length() długość napisu char charAt(int i) znak na pozycji i 16 Parametry linii komend W Java, pododnie jak w innych językach programowania, istnieje możliwość przekazania parametrów wywołania programu do metody main(). Parametry te są przekazane do programu jako tablica elementów typu String. Następujący program wypisuje wszystkie argumenty, z którymi został wywołany: class CommandLine { public static void main(String args[]) { for(int i = 0; i < agrgs.length; i++) System.out.println(“arg[“ + “] =” + args[i]); } } Przykładowe wywołanie: $ java CommandLine to tylko test Zobacz jak działa: CommandLine.java 17