|
|
 |
 |
 |
Artykuły |
 |
|
Java - Wstęp do programowania cz. 1
23:04:17 26.09.2005
@progs
Java - wstęp do programowania.
1.
Czym jest java i co nam oferuje?
* java jest interpretowanym językiem programowania przeznaczonym do tworzenia samodzielnych aplikacji jak i apletów osadzanych w przeglądarkach internetowych
* jest w pełni obiektowym językiem w tym sensie że nie posiada zmiennych ani funkcji globalnych jak np. w C/C++ co sprzyja pisaniu bardziej czytelnych i zawierających mniejszą ilość błędów programów (nie ma nic gorszego niż zmienne globalne które ,modyfikowane nie wiadomo kiedy i nie wiadomo przez jaki fragment kodu zabierają nam mnóstwo czasu w szukaniu problemu w przypadku załamania się takiej aplikacji) co nie oznacza że nie można symulować zmiennych globalnych za pomocą publicznych pól obiektów np w klasach statycznych, jednak taki sposób programowania łanie wiele zasad prawidłowego programowania więc uznajemy że takie coś nie istnieje:)
* w jazie praktycznie wszystkie zmienne są obiektami klas je definiujących, jedynym wyjątkiem są typy podstawowe
* typy podstawowe maja jednakowe rozmiary na każdej platformie w przeciwieństwie np do języka C/C++ gdzie rozmiary danych definiuje system operacyjny i sprzęt na jakim się wykonuje
* wszystkie klasy wywodzą się od jednej klasy podstawowej Object dzięki czemu każda klasa ma zapewniony identyczny interfejs na najniższym poziomie, tak więc przy definiowaniu klas jeżeli nie podano inaczej klasa domyślnie dziedziczy klasę Object (referencja na Object może wskazywać na dowolny obiekt w jazie, coś jak wskaźnik void w C/C++)
* w jazie zarządzanie pamięcią jest realizowane przez JVM i nie musimy się martwić zwalnianiem pamięci co nie oznacza ze możemy nie zwracać uwagi na sposób tworzenia dużej ilości obiektów ale o tym później
* daje nam możliwość pisania aplikacji przenośnych między systemami operacyjnymi i platformami sprzętowymi pod warunkiem że nie polegamy w kodzie na systemowym szeregowaniu wątków i nie używamy metod natywnych, jest to bardzo atrakcyjna cechach w połączeniu z bardzo bogatym zestawem standardowych bibliotek dostępnych w standardowej instalacji oraz bardzo dużej ilości bibliotek dodatkowych również standardowych
* mamy dostęp do bogatego zestawu komponentów GUI zawartych w pakiecie Swing, możliwość programowania sieciowego, wielowątkowego, operacji na plikach, przetwarzania grafiki i obrazów, dostępu do najróżniejszych baz danych i wszystko to niezależne od żadnej konkretnej platformy systemowej
* daje nam możliwość skorzystania ze specyficznych właściwości platformy systemowej dzięki mechanizmowi JNI (Java Natie Interface) który pozwala nam definiować klasy w których poszczególne metody są zaimplementowane w języku C++ i umieszczone są w bibliotekach ładowanych dynamicznie (DLL, SO) w zależności od systemu dzięki czemu możemy napisać identyczne pod względem interfejsu klasy dla każdego z systemów np. pakiet LWJGL implementuje połączenie javy i OpenGL dla systemów Windows/Linux i chyba Solaris dzięki czemu możemy pisać aplikacje korzystające z akcelerowanej sprzętowo grafiki 3D bez martwienia sie o różnice między platformami, istnieje kilka projektów tego typu uzupełniając możliwości javy o wykorzystanie specyficznych właściwości różnych systemów operacyjnych w oparciu o jeden interfejs programistyczny
* w przeciwieństwie do C++ java wymusza obsługę zgłaszanych wyjątków i nie pozwala do przekroczenia zakresów tablic, konwersje obiektów są bezpieczniejsze oraz wykrywane nie zainicjalizowane referencje są zgłaszane na etapie kompilacji jako błędy
2.
Zanim zaczniemy
* na początku polecam do samodzielnego zapoznania sie z następującymi stronami zwierającymi obszerne opisy standardowych elementów języka
+ http://republika.pl/arturt/java/
+ http://www.javasoft.pl/java/java_index.html
o oraz następujące adresy z ćwiczeniami i przykładami
+ http://www.man.koszalin.pl/~rataj/java/cw1/cw1.html
+ http://www.man.koszalin.pl/~rataj/java/index.html
jednak są to tylko propozycje stron zaczerpnięte z forum z linkowni:)
Instalacja kompilatora i maszyny wirtualnej
1. Udajemy pod adres http://java.sun.com/j2se/1.5.0/download.jsp i pobieramy wersje JDK odpowiednią dla swojego systemu, ważne aby pobrać JDK a nie JRE gdyż nas interesuje poza możliwością uruchamiania programów także możliwość kompilacji własnych więc potrzebujemy pełny kompilator który i tak zawiera JRE w paczce
2. Instalujemy jak nam odpowiada albo wersje OFFLINE albo ściągamy sobie instalkę na dysk, jak kto woli
3. W zależności czy chcemy korzystać z edytora własnego czy z tego który będzie służył do ćwiczeń w tym tutku pomijamy lub pobieramy plik http://www.progs.cal.pl/pliki/ProgsEdit.jar oraz niezbędną paczkę do działania edytora (obecnie moduł włączony w plik JAR edytorka) http://www.progs.cal.pl/pliki/bsh-2.0b4.jar polecam tez odwiedzić stronkę domowa BeanShell www.beanshell.org gdzie można znaleźć dokumentacje, manual i źródełka projektu
4. plik bsh-2.0b4.jar kopiujemy do katalogu JAVA_HOME/jre/lib/ext - gdzie JAVA_HOME to katalog gdzie zainstalowaliśmy jawę np. (c:java lub /opt/java)
>
5. sprawdzamy czy instalacja i biblioteka działa poprawnie wpisując w lini poleceń:
>
java bsh.Console
- jeżeli uruchomi sie interpreter to oznacza ze biblioteka jest widziana przez domyślana wersję javy i instalacja dobiegła konca
- jeżeli otrzymamy komunikat ze plecenie java nie jest rozpoznawane to musimy dodać do ścieżki poszukiwań poleceń katalog JAVA_HOME/bin dla każdego systemu wygląda to inaczej wiec daruje sobie opis jak to zrobić
- możemy umieścić sobie w ulubionym miejscu skrót do edytora gdzie polecenie ma postać: JAVA_HOME/bin/java -jar SCIEZKA_DO_PLIKU_JAR_EDYTORA np (f:javabinjavaw -jar c:peProgsEdit.jar lub /opt/java/bin/java -jar /opt/pe/ProgsEdit.jar
- gdy tworzymy skrót to wpiszmy katalog roboczy jako KATALOG_GDZIE_JEST_JAR_EDYTORA np. c:pe lub /opt/pe
- w tym katalogu będzie poszukiwany plik "java_compiler.conf" gdzie powinna być wpisana ścieżka do katalogu gdzie zainstalowaliśmy javę, jednak gdy uruchamiamy edytor z domyślnej wersji javy którą właśnie zainstalowaliśmy plik ten jest zbędny gdyż konfiguracja zostanie odczytana z JVM, plik powinien zawierać w pierwszej lini wpis typu c:Java lub /opt/java
- zabieramy sie do pracy:)
Piszemy pierwszą aplikacje w javie
1.
Jeżeli chcemy pracować z edytorkiem to poznajmy go troszkę bliżej, jak nie możemy pominąć ten punkt
* podstawy pracy z edytorem - edytor domyślnie koloruje pliki javy w następujący sposób (konfiguracja jeszcze nie dostępna)
o słowa kluczowe języka są wyświetlane na granatowo, czcionką pogrubioną
o nazwy standardowych klas javy są też granatowe ale czcionka jest normalnej grubości
o napisy są niebieskie
o wartości numeryczne ciemno czerwone
o komentarze zielone czcionka pochyłą
* użyteczne skróty klawiaturowe
o CTRL + N - nowy pusty plik
o CTRL + O - otwarcie pliku
o CTRL + S - zapis pliku
o CTRL + F4 - zamkniecie pliku
o CTRL + Z - cofanie ostatniej edycji
o CTRL + SHIFT + Z - ponowienie ostatniej edycji
o CTRL + C - kopiowanie do schowka
o CTRL + X - wycięcie do schowka
o CTRL + V - wstawienie ze schowka
o CTRL + INSERT - ustawia lub kasuje zakładkę
o CTRL + UP - poprzenia zakładka
o CTRL + DOWN - następna zakładka
o CTRL + DELETE - kasuje wszystkie zakładki
o CTRL + F - okienko szukaj/zamień
o F9 - uruchom
o CTRL + F9 - kompilacja
* polecenie kompilacja automatycznie wywołuje akcje zapisz
* edytor nie obsługuje na razie projektów więc praca polega na edycji nie powiązanych plików
* w przyszłości będzie obsługiwany system projektów ANT http://ant.apache.org/ który jest standardowym odpowiednikiem polecenie make z C/C++ dla javy
* nie można na razie podać opcji polecień ze która normalnie podaje sie z lini poleceń (mało użyteczna opcja w środowisku graficznym ale może kiedyś ja dodam:)) (interfejs kompilatorów obsługuje taka możliwość ale nie ma żadego dojścia do niego na razie z poziomu edytora)
* edytor nie radzi sobie z klasami w pakietach czyli obsługuje najprostsze pliki java bez definicji pakietu i chyba to już nie ulegnie zmianie gdyż wspomniana obsługa ANT w momencie dodania rozwiąże problemy skomplikowanych struktur pakietów oraz różnorodnych dodatkowych akcji i polecień rozpoznawany przez to narzędzie i nie widzę potrzeby tworzenia własnej prymitywnej wersji buildera projektów
NOWOŚCI:
* edytorek wzbogacilł się o możlwość konwersji kodu do XHTML'a za pomocą stylów więc eleganco, w przeciwieństwie do spotykanego modułu java2html który konweruje kod na stare znaczniki HTML'a i nic sie z tym juz nie zrobi
+ kod który widać w przykladach jest konwertowany właśnie tym modułem
+ konwertowany kod pokazuje się w nowej zakładce i zawiera pełną definicje strony
* dodalem funkcje dir(Object obj) która wołana z poziomu skryptu lub w konsoli pozwala na wyświetlenie pełnej informacji o klasie lub obiekcie analogicznie do funkcji znanej w PYTHONIE która na konsoli wypisuje zawartość obiektu, struktura wyswietlana jest w osobnym okienku w obiekcie JTree i ma dwie zakladki Refleksja i Introspekcja gdzie pokazywana jest informacja o klasie wyciągnięta za pomacą tych dwóch metod, działa to niezbyt szybko gdyż brak optymalizacji i wykreślacz jest oparty o JLabel i możlwiść podawania tekstu HTML ale dzięki temu koloruje słowa kluczowe javy żeby było bardziej czytelne to drzewko. Wielkość czcionki tak jak w edytorze jest dobierana automatycznie w zaleznosci od wielkosci ekranu
+ parametrami funkcji może być klasa (pełna nazwa klasy np. javax.swing.JFrame) lub obiekt dowolnej klasy
* obecnie jest dołaczony moduł BeanShell do pliku JAR więc edytor może działać samodzielnie bez ściągania i instalowania biblioteki
Następnymi planowanymi dodatkami będzie dodatkowa zakładka z przestrzenią nazw widzianych w BSH z możliwoscią wywołania okienka pokazyanego przez funkcje dir . Jakoś na razie nie mogę zabrać się za integrację z ANT więc na razie nie będzie obsługi projektów ale z czasem też ta funkcja się znajdzie w projekcie
2.
pierwszy program w javie czyli mało sensowne ale ważne HelloWord
uruchommy edytorek lub swój ulubiony edytor tekstu i wpiszmy:
public classHelloWord{
public static void main(String[] args){
System.out.println("Witaj JAVO!");
}
}
* możemy zapisać wciskając CTRL+S lub od razu CTRL+F9 edytor sam zapyta o zapisanie, gdy używamy własnego edytora musimy zapisać i skompilować program z lini komend lub przez jego własny mechanizm kompilacji
- kompilacja: javac HelloWorld.java, w edytorze domyślnie dodawana jest opcja -O która karze kompilatorowi usuwać asercje i dokonuje optymalizacji kodu wynikowego
- uruchomienie: java HelloWorld
* zapiszmy plik jako HelloWorld.java to ważne zaraz powiem czemu tak jest
* po szczegółowy opis poszczególnych klas i ich metod, opcji kompilatora i dostępnych narzędzi odsyłam do podanych tutoriali i oczywiście do dokumentacji javy która polecam ściągnaą z http://java.sun.com/j2se/1.5.0/download.jsp#docs
1.
zobaczmy jak to co wpisaliśmy brzmi po ludzku
* linia 1 oznacza:
* definiujemy publiczna klasę o nazwie HelloWorld która domyślnie dziedziczy po klasie java.lang.Object jak wszystkie klasy które nie mają jawnie określonej klasy bazowej
* klasa Object jest przodkiem wszystkich klas w javie i określa minimalny interfejs dla definiowanej klasy i dostarcza nam następujących metod podstawowych (wyminie niektóre te ważniejsze)
o metoda equals(Object object) - definiuje porównanie obiektu z innym obiektem i true jak obiekty są identyczne na poziomie logicznym (domyślnie porównywane są adresy w pamięci co dla obiektów reprezętujących wartości jest bez sensu dlatego w każdej własnej klasie reprezentująca jakaś wartość musimy ta metodę przesłonić własną aby porównanie obiektów działało prawidłowo
o metoda toString() drukująca wartosć obiektu w postaci String, używana mi. przez metode print w wywolaniu System.out.println gdzie System to klasa wbudowana doatarczjaca wieleu uzytecznych metod oraz mi. zawiera pole publiczne out które jest obiektem klasy PrintStream i umozliwia nam przez swoja metode println(...) drukowanie obiektów na konsoli, dla każdej własnej klasy powinniśmy przeslonic tą metodę aby zwracala odpowiedni dla naszej klasy ciąg znakow
o hashCode() zwraca wartosc uzywana we wszystkich kolekcjach mieszajacych do identyfikacji obiektu, przy okazji omawiania w delszej czesci tworzenia wlasnych klas wartosciowych dowiemy sie dokladnie o co chodzi z tą metodą
* linia 2 zawiera definicje stanadrdowej metody main ktora musi zawierac kazda klasa ktora ma byc klasa uruchamialna, co to oznacza?, mianowicie java podczas uruchamia programu szuka w podanej klasie startowej tej metody i jak jej nie znajdzie komunikuje bląd gdyż nie wie jak ma uruchomic program, jest to odpowiednik metody int main(...) z C/C++
* linia 3 krótki opis klasy System i pola out jest w opisie lini 1
UWAGI:
W jednym pliku xxxx.java może być zwartych wiele klas ale tylko jedna publiczna i jeżeli jest klasa publiczna plik musi nazywać sie dokładnie tak jak ta klasa (jak w naszym pierwszym przykładzie), nawet w systemach nie rozróżniających wielkości liter w nazwie, nazwa musi być dokładnie taka jak nazwa klasy inaczej dostaniemy błąd kompilacji z informacja ze klasa publiczna musi być zawarta w pliku o nazwie jak nazwa klasy i zostanie przerwany proces kompilowania
2.
Pierwsza prawdziwa klasa
public class First{
// *0*
public First(){
System.out.println("konstruktor w akcji");
}
// *1*
public void fun_1(){
System.out.println("funkcja 1 w akcji");
}
// *2*
private void fun_2(String text){
System.out.println("funkcja 2 w akcji: " + text);
}
// *3*
public String fun_3(String text){
System.out.println("funkcja 3 w akcji: " + text);
return text.toUpperCase();
}
// *4*
public int fun_4(int x){
System.out.println("funkcja 4 w akcji: " + x*x);
return x*x;
}
// *5*
public void fun_5(String text){
String tmp = fun_3(text);
System.out.println("funkcja 5 w akcji: " + tmp);
}
// *X*
public static void main(String[] args){
First f = new First();
f.fun_1();
f.fun_2("java jest fajna");
f.fun_3("programowanie jest ciekawe");
int w = f.fun_4(743);
f.fun_5("jakis tam tekst");
}
}
Przeanalizujmy budowę przedstawionej klasy
oznaczony element // *0* jest konstruktorem klasy, a co to oznacza?, możemy sobie o tej specyficznej metodzie pomyśleć jak o czymś w rodzaju dekoratora wnętrz tzn:
o konstruktor nazywa się tak jak klasa (u nas First), zauważmy że przed nazwą wszystkich innych metod jest umieszczona deklaracja wartości zwracanej przez metodę, a konstruktor jako jedyny nie ma w tym miejscu niczego, nawet nie ma tam VOID co oznacza że metoda nie dostarcza żadnego wyniku, dzięki tym cechom łatwo nam stwierdzić która metoda jest konstruktorem a dodam ze może ich być kilka tak jak zresztą inne metody konstruktor może mieć wiele postać przy czym żadne dwie metody nie mogą mieć identycznych list parametrów (pomówimy o tym później, chciałem tylko zasygnalizować temat)
o konstruktor jest metodą wywoływaną automatycznie przez JVM w momencie tworzenia obiektu, jest to bardzo dobre zachowanie gdyż ten właśnie twór umożliwia nam nadanie domyślnych wartości różnym zmiennym w sposób automatyczny, gdyby nie to musielibyśmy mieć własną metodę i ręcznie ją wywoływać w celu ustalenia stanu zmiennych o czym łatwo można zapomnieć więc konstruktor jest dla nas udogodnieniem
o gdy nie zdefiniujemy konstruktora to zostanie on dodany automatycznie przez jave, ale będzie pusty więc trochę mało użyteczny, sami się przekonamy że będzie mało klas które nie będą wymagały jawnie zdefiniowanego konstruktora, czasem paru z różnymi parametrami
o w javie nie ma kolegi do pary czyli destruktora, czyli kogoś w rodzaju sprzątaczki która dba żeby zasoby obiektu zostały usunięte, jest metoda finalize dziedziczona z klasy Object która miała pełnić funkcje swojego rodzaju destruktora ale java nie gwarantuje ani czasu kiedy metoda ta zostanie uruchomiona ani nie gwarantuje że metoda ta w ogóle zostanie wykonana wiec rola finalizatorów pełni pomocniczą funkcję jako tzw, siatki bezpieczeństwa przy zwalnianiu krytycznych zasobów o czym pomówimy trochę później gdyż temat ten należy do zaawansowanych technik programowania i na razie nie miejsce tu na niego
następne elementy //*1-5* są przykładami różnych metod, przyjmójącymi parametry, zwaracjącymi wartości, kożystajace ze zmiennych lokalnych i wywołujące sie nawzajem, omówimy je wszystkie naraz skupiając sie na ważniejszych szczegółach konstrukcji metod jako takich
* ogólnie możemy przyjąc że każda metoda z wyjątkiem konstruktora ma następująca postać:
[static][void | typ zwracany ][private | public | protected | nic][final | abstract] nazwa_metody ([parametry | nic]) {
// tu jest ciało metody
[return wartość_zwracana; | return;]
}
- gdzie lementy w nawiasch [] są opcjonale tzn ze moga wystapic lub nie
- elemnty w nawiacach oddzielone | oznaczaają elementy zwzajemnie się wykluczajace czyli może wystąpić któryś z nich ale nie oba na raz
o typ metody (statyczna, finalna)
+ gdzie modyfikator static oznacza ze metoda jest metodą klasy a nie metodą egzemplarza klasy, a co to oznacza?, w praktyce oznacza to tyle że wszystkie elementy oznaczone jako static występują w klasie tylko raz i są wspólne dla wszystkich egzemplarzy stworzonych z tej klasy, więcej są dostępne nawet wtedy gdy żaden obiekt danej klasy nie został utworzony, co czasem jest bardzo przydatne np. definicje stałych lub metod statycznych (w klasie java.lang.Math mamy np. statyczne pole PI reprezentujące wartość stałej jako zmienną typu double ), przykładem metody statycznej jest np. nasza znajoma main, istnieje jednak ciemna strona elementów zadeklarowanych w ten sposób a mianowicie słabo wyuczeni programiści lub zatwardziali miłośnicy języka C przy pomocy tego symulują programowanie funkcjonalno-proceduralne w języku obiektowym, a w skrajnych przypadkach nawet liniowe;), no ale cóż my się uczymy pisać poprawne programy z punktu widzenia obiektowości i elementów tych będziemy używać w sytuacjach gdy ich użycie jest uzasadnione
+ natomiast final oznacza że raz zdefiniowana zmienna już nie ulegnie zmianie, a dla metody oznacza że nie będzie można jej przedefiniować w klasie pochodnej, ale tu uwaga, niezmienność pola klasy oznacza że nie można pod taką zmienna podstawić innego obiektu (coś jak wskaźnik const w C++), jednak gdy sama zmienna będzie skazywać na obiekt klasy mutowalnej jej stan może ulec zmianie więc trzeba być ostrożnym w poleganiu na final w przypadku klas posiadających mutatory, po drugie każda tablica o rozmiarze większym od 0 zawsze jest modyfikowalna, na szczęście obiektów modyfikowalnych nie jest wiele i sami też powinniśmy starać się w miarę potrzeby tworzyć obiekty stałe (o powodach takiego podejścia porozmawiamy troszkę dalej gdyż to nam tu nie będzie potrzebne)
+ modyfikator dostępu (prywatny, publiczny, chroniony, prywatny w ramach pakietu)
# private oznacza że dostęp do takiego pola ma jedynie klasa gdzie pole zostało zdefiniowane, czyli jest czymś w rodzaju zmiennej lokalnej klasy, wynika z tego że pola i metody prywatne, a nawet konstruktory nie są widziane poza ciałem klasy do której należą, najczęściej stanowią szufladki do przechowywania wartości właściwości ziarenek javy (tzw. java beans) o których też później porozmawiamy, oraz stanowią pomocnicze zmienne w klasie które mają być ukryte w implementacji a nie być częścią implementacji, nawet klasy pochodne nie widzą takich elementów
# protected oznacza to samo co private z tą różnicą że klasy pochodne mają dostęp do tych zmiennych, pól tego typu należy używać ostrożnie gdyż stają się w tym momencie częścią API klasy i nie możemy już w klasie bazowej dowolnie ich usuwać bez przerabiania klas pochodnych, natomiast definiowanie metod z tym przydomkiem jest bardzo powszechne gdy chcemy aby klasa pochodna magla na swój użytek przedefiniować lub skorzystać z danej metody w klasie bazowej
UWAGA: protected w ramach pakietu zachowuje się jak public wiec mamy dostęp w pakiecie do zabezpieczonych elementów innych klas tego pakietu
# public takie elementy widzi każdy i każdy może się do nich odwołać, metody tego typu stanowią interfejs naszej klasy a więc są czymś w rodzaju pokręteł i potencjometrów na obudowie radia ukrywając przed użytkownikiem wszystkie cewki, tranzystory i oporniki (elementy private, protected) pozwalając na używanie tegoż obiektu bez znajomości jego budowy
# final oznacza że zdefiniowana metoda nie może być nadpisywana w klasach potomych czyli tak jak ja zdefiniujemy w klasie podstwowowej będzie występwała w klasach potomnych, modyfikator przydaje się gdy zakładamy że dana metoda nigdy nie będzie i nie dopuszczamy takiej możliwości aby można było w żdanej potomnej klasie przedefiniować metody, np. gdy metoda spełnia ważne zadanie i jej definicja musi pozostać nie zmieniona gyż wykonuje jakieś krytyczne zadanie dla danej abstrakcji a klasy potomne mogłyby uszkodzić to działanie
# abstract oznacza że klasa definujaca taka metodę przeznaczona jest do dziedziczenia, użycie tego modyfikatora oznacza że:
* nie można utowrzyć egzemplarza danej klasy gdyż nie istenieje definicja danej metody abstrakcyjnej
* klasa taka może jednak być typem referencyjnym
* klasa dziedzicząca klasę zawierajacą metode abstarkcyjną jest zobowiązana do zdefiniowania danej metody lub przy braku tej definicji pozostaje dalej klasą abstrakcyjną
* najczęsciej klasy abstrakcyjne są uproszczoną definicją interfejsu upraszczając proces implementacji danego interfejsu dostarczając definicji niezbędnych metod a resztę pozostawiając do zdefiniowania w klasie potomnej, w stardardowej bibliotece javy jest sporo takich szkieltowych implementacji i najczęsciej ich nazwa zaczyna się od słowa Abstract np. AbstractAction, AbstractBorder, AbstractButton
* interfejsy są odmianą klas abstakcyjnych gdzie wszystkie metody z definicji są publiczne oraz abstrakcyjne przez co ułatwiają definicje takich klas gyż nie trzeba pamiętać o przydomkach abstract przy każdej metodzie
* klasa zawierajaca metodę abstarkcyjną musi mieć przydomek abstract
+ następnie występuje lub nie typ zwracanej wartości np. int, String, Object itd lub gdy metoda ma pełnić role procedury powinno być słówko kluczowe void co oznacza ze metoda nie może nic zwrócić, łatwo o tym zapomnieć pisząc warunkowe wyjście z metody przy pomocy return które to słówko nakazuje natychmiastowy powrót z metody do kodu wywołującego i przy którym można umieścić wartość która ma być zwrócona tak jak w naszej bezużytecznej w sumie klasie w miejscu np.// *4* gdzie zwracamy x*x, gdyby z jakiegoś powodu trzeba by wyskoczyć z metody typu void wtedy przy return nie może być nic czyli instrukcja ma postać return; inne formy będą powodować błąd kompilacji, a i jeszcze jedno umieszczenie return bez instrukcji warunkowej gdy za nią są jakieś instrukcje jest raportowane jako błąd, brak instrukcji return w metodzie deklarującej że coś zwraca też jest wykrywane jako błąd na etapie kompilacji
+ kolejnym elementem jest nazwa która może być dowolną dozwoloną przez składnie języka etykietą o następujących cechach:
+ nie może zawierać spacji
+ nie może rozpoczynać się cyfrą
+ nie powinna zawierać znaków specjalnych poza małymi wyjątkami tak więc do dyspozycji mamy litery, cyfry, znak podkreślenia (używany w nazwach stałych do oddzielenia słów)
+ zasady nadawania nazw poszczególnym elementom programu najlepiej podpatrzyć w źródłach javy (dostępnych w paczce instalacyjnej)
# ogólnie klasy i interfejsy nazywamy zaczynając od dużych liter np. Object, String, Frame
# metody powinny zaczynać się z małej litery np toString, intValue, remove, add, getDocumentBase gdzie od drugiego słowa składowego zaczynają się od wielkich liter
# zmienne lokalne i pola maja takie same zasady jak metody
# stałe są pisane wielkimi literami i słowa oddzielamy znakiem "_" np EXIT_ON_CLOSE, PI, SOME_VALUE
# nazwy powinny być nadawane w sposób jednoznacznie kojażące się z tym do czego służą, dopuszczane są skróty w stylu awt, io gdy są zrozumiałe i powszechnie stosowane, jak również dopuszcza się używania króciutkich oznaczeń dla zmiennych pętli sterujących itd np i, k, x, mx, zresztą dla zmiennych lokalnych i prywatnych konwencje te są słabsze gdyż elementy te nie są częścią API klasy i nikt z normalnych użytkowników nie będzie musiał się zastanawiać do czego służą tajemniczo nazwane zmienne
# warto przestrzegać tych reguł w elementach publicznych klas gdyż ułatwi to analizę i zrozumienie działania tych klas przez innych programistów gdyż cały rdzeń javy stosuje te konwencje (przynajmniej powinien) i programiści przyzwyczajeni są do takiego sposobu nazywania
+ powyższe konwencje są tylko propozycją a nie rygorystycznym wymogiem i jak nasze zwyczaje stanowią inaczej możemy ich nie przestrzegać ale używanie konwencji i maksymalne użycie bibliotek standardowych odróżnia amatorów od profesjonalistów;
3.
Zapoznajmy się teraz z budową samych klas
Klasy posiadają ogólnie następującą budowę:
[public | private | nic][static][final] class nazwa_klasy [extends typ | nic] implements [interfejs1, interfejs2,...interfejsN]{
/// ciało klasy
}
- gdzie pierwszy opcjonalny człon w znaczeniu jest podobny do analogicznych przydomków dla metody,
* public oznacza że klasa jest widoczna dla wszystkich oraz że plik ją definujący taką klasę musi się nazywać nazwa_klasy.java z dokładnością do wielkości liter czyli np. public class Testowa {...} musi znajdować się w pliku Testowa.java , gdy nazwa pliku jest inna podczas kompilacji otrzymamy komunikat o błędzie nazwy pliku źródłowego, jednak dany plik może zawierać dowolną ilość klas prywatnych (private) lub gdy nie ma nic klasa jest dostępna w ramach pakietu ale o pakietach porozmawiamy później
* private oznacza że dana klasa nie jest częścią API pakietu a jedynie elementem implementacji i jest używana wewnętrznie w danym pakiecie jako klasa pomocnicza, ale to tylko uproszczenie, sytuacje mogą być różne np. może istnieć w jakimś pakiecie interfejs w oparciu o który zaimplementowanych jest kilka klas prywatnych oraz istnieje w jakiejś klasie metoda zwracająca wg jakiś parametrów określony egzemplarz klasy prywatnej jednak deklarując zwracany typ referencyjny jako interfejs, może mało to jasny przykład, jednak jest to bardzo uproszczony schemat wzorca Factory czyli Fabryka który kiedyś też omówimy jak również i inne wzorce gdyż są bardzo użyteczny w procesie programowania złożonych projektów
* final oznacza że dana klasa nie może być dziedziczona i jej postać jest ostateczna, można obejść takie częściowo takie ograniczenie wykorzystując zamiast dziedziczenia kompozycje ale wtedy tracimy typ referencyjny danej klasy, znaczenie jest analogiczne jak dla metod
* static oznacza że klasa nie jest przeznaczona do tworzenia egzemplarzy obiektów tylko najczęściej jest klasą narzędziową udostępniającą jakieś metody również statyczne, wspomniałem że takie klasy są czasem wykorzystywane do symulowania programowania funkcjonalnego w javie ale jest to bardzo zła praktyka i nigdy tego nie róbmy, ale do czego można więc wykorzystać takie klasy?, np. gdy tworzymy klasę która nie posiada stanu tzn składa sie z samych metod i nie posiada żadnych pól lub pola te są identyczne dla każdego egzemplarza wtedy warto zamieć taka klasę na statyczną gdyż tworzenie egzemplarzy bezstanowej klasy jest nieuzasadnione i marnuje pamięć i inne zasoby na niepotrzebne identyczne obiekty np klasa Math jest klasa statyczną udostępniająca nam funkcji matematycznych i stałych, klasa ta nie posiada stanu więc została zaimplementowana jako statyczna, jest sporo klas statycznych w standardowym pakiecie javy. Innym powszechnym zastosowaniem klas statycznych są klasy realizujące wzorzec Factory ale również taka klasa może być utworzona jako klasa Singleton o czym jeszcze porozmawiamy później
- nazwa klasy musi być zgodna tym co powiedzieliśmy przy okazji metod, a co do konwencji to jak łatwo zauważyć nazwa klasy zaczyna się z dużej litery gdzie każde słowo tworzące nazwę jest rozpoczęte dużą literą np. TimerTask, JButton, Frame itd., klasy rozpoczynjace się od literki J z pakietu javax.swing trochę łamią tą zasadę gdyż dwie pierwsze litery są duże, ale cały pakiet przyjął taką konwencje i łatwo to zapamiętać
* extends jest słówkiem oznaczającym specyfikacje klasy bazowej czyli tej po której dana klasa będzie dziedziczyć, pamiętajmy że java w przeciwieństwie do C++ nie posiada dziedziczenia wielobazowego jednak mamy możliwość implementacji dowolnej ilości interfejsów, gdy sekcja extends nie wystąpi oznacza to że dana klasa dziedziczy po klasie Object czego nie musimy określać i nie powinniśmy nigdy tego robić gdyż jest to działanie domyślne. Powiedzmy teraz może słów parę dlaczego nie ma dziedziczenia wielobazowaego?, otóż wyobraźmy sobie że dziedziczymy jednocześnie dwie klasy i w obu klasach jest pole o identycznej nazwie i teraz mamy problem gdyż nie wiadomo z której klasy nasza klasa otrzyma to nieszczęsne pole, a może z obu? i jak zinterpretować odwołanie w naszej klasie this.nazwa_tego_pola, java nie rozstrzyga takich konfliktów i nie dopuszcza do dziedziczenia po kilku klasach na raz, a interfejsy z definicji nie posiadają stanu więc można je implementować w dowolnej ilości, a dziedziczenie interfejsu podlega takim samym zasadom jak dziedziczenie klas więc konflikt nazw metod nie nastąpi
* implements oznacza implementacje jednego lub więcej interfejsów o czym powiedzieliśmy w poprzednim punkcie
- teoria teorią ale zobaczmy klasę w akcji, teraz będzie już to jakiś sensowniejszy przykład, spróbujemy stworzyć klasę reprezentującą punkt w przestrzeni 3D czyli posiadającym trzy współrzędne, zapoznamy się w tym przykładzie z wieloma elementami tworzenia klas, a zaczniemy od zdefiniowania klasy bazowej jako punktu zawierającego dwa wymiary, nie skorzystamy z klasy Point z pakiety java.awt gdyż klasa ta posiada publiczne pola a my uczymy się poprawnie programować i postaramy się jak tylko się da unikać publicznych pól w klasach poza stałymi, klasa Point została napisana w ten sposób ze względu na wydajność kodu we wczesnych wersjach JVM i nie powinna być nigdy naśladowana, jednym wyjątkiem są klasy prywatne a szczególnie klasy zagnieżdżone o czym porozmawiamy troszkę później, a oto nasza klasa bazowa:
public class Point{
protected int x;
protected int y;
// konstruktor
public Point(int x,int y){
this.x=x;
this.y=y;
}
// metoda pobierajaca wartość pola x
public int getX(){
return x;
}
// metoda pobierajaca wartość pola y
public int getY(){
return y;
}
// przedefiniowana metoda z klasy Object
// użyteczna przy wypisywaniu obiektu na konsole
// przez System.out.println(...);
public String toString(){
return "Point - x= "+x+", y= "+y;
}
Zdefiniowana klasa jest reprezentacją punktu w przestrzeni dwu wymiarowej i jak widać na listingu posiada dwa pola prywatne dla wszystkich z wyjątkiem potomków czyli zabezpieczone typu int które przechowują dane o położeniu w pionie i poziome punktu więc w tej postaci klasa mogłaby reprezentować pojedynczy piksel na ekranie, ale my zajmiemy się budową klasy a nie jej przeznaczeniem:
* konstruktor służy nam do nadania wartości współrzędnym punktu, gdzie widzimy ciekawą konstrukcje this.x i this.y słówko this jest oznacza referencje do klasy w której się znajdujemy, sztuczka ta pozwala nam użyć w parametrach konstruktora nazw parametrów identycznych do tych które zadeklarowaliśmy w klasie a dzięki odwołaniu się jawnym do pól przez this java wie co ma począć z identycznymi nazwami pól gdyż jej podpowiadamy że chcemy aby podstawione zostały podane wartości do pól w klasie, bez tego słówka kompilator by zaprotestował gdyż nie wiedziałby co począć z odwołaniem x=x, pamiętajmy że słówko this jest domyślne i nie trzeba go stosować w kodzie metod gdy nie ma konfliktów nazw jednak tu jesteśmy zmuszeni poinformować kompilator o naszych zamiarach
* konstruktor jest publiczny wiec każda inna klasa może utworzyć egzemplarz naszej klasy
* klasa posiada dwie metody pozwalające na pobranie wartości współrzędnych ale nie posiada metod pozwalających na modyfikacje tych zmiennych co oznacza że:
o klasa jest klasą niezmienną czyli po utworzeniu nie można zmienić jej stanu więc może być używana bez obawy w kodzie wielowątkowym bez synchronizacji, może być wstawiana do kolekcji (jednak aby faktycznie było to możliwe trzeba przedefiniować metody z klasy Object: hashCode() i equals() co uczynimy w następnym temacie), może być bezpiecznie współdzielona przez wiele klas gdyż nie trzeba się martwić o zmiany stanu
o posiada prze definiowaną metodę toString() która umożliwia nam proste i ładne drukowanie na konsoli reprezentacji tekstowej wartości obiektu
* klasa jest publiczna więc musi być zapisana do pliku o nazwie Point.java
A teraz zobaczmy jak działa dziedziczenie i zdefiniujmy sobie klasę Point3D dodając pole reprezentujące głębokość
public class Point3D extends Point{
protected int z;
public Point3D(int x,int y,int z){
super(x,y);
this.z=z;
}
public int getZ(){
return z;
}
public String toString(){
return "Point - x= "+x+", y= "+y+", z="+z;
}
}
Zobaczmy co tu właściwie mamy:
W pierwszej lini pojawia się nowe słówko kluczowe extends które oznacza że tworzona klasa dziedziczy po klasie podanej po tym słówku, jak już wiemy w javie dziedziczymy tylko po jednej klasie więc zawsze będzie tu występowała jedna nazwa klasy bazowej, w Pythonie czy C++ sprawa wygląda inaczej i mozemy dziedziczyć wiele klas jednocześnie ale już mówiłem czemu w javie jest tak a nie inaczej.
Co oznacza dziedziczenie?
Onacza mniej więcej tyle dla kompilatora, tworzę właśnie nową klase i każe kompilatorwi coś w tym stylu:
* widzisz definuje nową klasa a że jestem sprytny lub leniwy jak kto woli to chcę aby ta nowa klasa zawierała wszystko to co klasa bazowa
* dodam coś nowego lub zmodyfikuje co mi nie odpowiada i nowa klasa będzie gotowa
* wiem że wszystkie metody publiczne i zabezpieczone z klasy bazowej będą do mojej dyspozycji w tej nowej klasie
* nowa klasa będzie posiadała funkcjonlność klasy bazowej + nowe możlwości które dodamy
Jak widzimy w zródełku dodaliśmy nowe pole "z" do przechowywania zmiennej odpowiedzialnej za głębokość, zmienna jest zabezpieczona więc jak zechcemy stworzyć punkt czaso-przestrzenny to wystarczy odziedziczyć po tej klasie i dodać pole czas
Zefiniowaliśmy nowy konstruktor dodając do parametrów brakujące pole z oraz, do inicjalizacji pol z klasy bazowej wykożystałem konstruktor z klasy Point dzięki słówku kluczowemu super które oznacza wywołanie konstruktora klasy bazowej, uwaga super musi być pierwszą instrukcją konstruktora, następnie podstawiamy wartosć z.
Tworzymy metodę pobierajacą wartość nowej współrzędnej czyli getZ.
Przedefiniujemy metode toString aby wypisywanie wartości obiektu klasy uwzględniało nową współrzędną, pamiętajmy że metoda to jest dziedziczona pośrednio z klasy Object gdzie pojawia się pierwszy raz, w klasie Point jest przedefiniowana a w Point3D następuje ponowna redefinicja metody.
Tu mała dygresja metoda nie jest przesłaniana gdyż ma identyczną nazwę i listę parametrów więc następuje jej redefinicja, uwaga będzie bardzo wazna w momencie redefinicji metody equals z klasy Object, druga uwaga na przyszłość typ zwracany przez metodę ani jej modyfikatory nie są częścią sygnatury metody więc nie mogą służyć do przesłaniania.
W dalszym ciągu nasza nowa kasa jest niezmienna gdyż nie dadaliśmy żadnej metody umożliwiajacej zmiane wartosci współrzędnych i raczej nigdy tego nie zrobimy gdyż klasy niezmienna będzie potrzebna do omowiania kolekcji oraz redefinicji metod hashCode i equals oraz ma wiele zalet w stosunku do klas modyfikowalnych ale o tym porozmawiamy niedługo.
Głupio pisać ale dziś miał się pojawić dalszy ciąg kursu, niestety zapomniałem że programy lubią się zawieszać, a ten NVU którego używam z wygody i lenistwa:), właśnie miał dziś swój dzień na wk.....e mnie dokładnie, jakieś 3-4 strony poszły przez ten incydent więc dalsza część pojawi się za jakiś czas, a jak koś używa tego programu do pisania stronek to wiece lepiej nie wklejać tekstu skopiowanego z HELPA windowsowego bo NVU chce wklejać jakieś skrypty javy i zawiesza się w takiej okoliczności, to tyle.
tu niestety muszę przerwać tą lekcje, ale CDN..., jak ktoś wypatrzy jakieś fanaberie lub błędy to dajcie znać na progs@o2.pl
Komentarzy: 0 | Głosuj (1,
2,
3,
4,
5,
6) | Ocena: 3.16 | Czytane: 7851 |  |
 |
Komentarze |
 |
|
Musisz się zalogować aby mieć możliwość komentowania
|  |
|
 |
|