PO Wstęp do Javy: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Janusz (dyskusja | edycje)
Nie podano opisu zmian
Janusz (dyskusja | edycje)
 
(Nie pokazano 78 wersji utworzonych przez 3 użytkowników)
Linia 1: Linia 1:
{{powrot|Programowanie obiektowe|}}
{{powrot|Programowanie obiektowe|}}


== Wstêp do Javy ==
== Wprowadzenie ==


=== Wprowadzenie ===
Od tego wykładu zaczynamy poznawanie programowania obiektowego na przykładzie konkretnego języka programowania. Będzie nim Java. Java jest jednym ze współczesnych, nowoczesnych języków obiektowych. Jest też bardzo popularna. Wszystkie te cechy uzasadniają wybór tego języka jako narzędzia do pokazania mechanizmów obiektowych. Należy jednak podkreślić, że nie jest to jedyna możliwość. Niniejszy wykład z bardzo małymi zmianami można by było oprzeć na C#. Można też z bardzo dobrym skutkiem skonstruować wykład z programowania obiektowego zestawiając ze sobą czysto obiektowy Smalltalk z hybrydowym C++ (choć wymaga to już pewnej dojrzałości programistycznej od słuchaczy). Możliwości jest wiele, twórcy każdego wykładu muszą dokonać jakiegoś wyboru. Ważne jest to, by słuchaczom wykładu nie umknął podstawowy fakt: wybrany język programowania jest tylko narzędziem służącym do pokazania na przykładach na czym polega programowanie obiektowe. Wybór języka, aczkolwiek niezbędny, jest rzeczą wtórną.


Od tego wyk³adu zaczynamy poznawanie programowania obiektowe na przyk³adzie konkretnego jêzyka programowania. Bêdzie to Java. Java jest jednym ze wspó³czesnych, nowoczesnych jêzyków obiektowych. Jest te¿ bardzo popularna. Wszystkie te cechy uzasadniaj¹ wybór tego jêzyka jako narzêdzia do pokazania mechanizmów obiektowych. Nale¿y jednak podkreœliæ, ¿e nie jest to jedyna mo¿liwoœæ. Niniejszy wyk³ad z bardzo ma³ymi zmianami, mo¿na by by³o oprzeæ na C#. Mo¿na te¿ z bardzo dobrym skutkiem skonstruowaæ wyk³ad z programowania obiektowego zestawiaj¹c ze sob¹ czysto obiektowy Smalltalk z hybrydowym C++ (choæ wymaga to ju¿ pewnej dojrza³oœci programistycznej od s³uchaczy). Mo¿liwoœci jest wiele, twórcy ka¿dego wyk³adu musz¹ dokonaæ jakiegoœ wyboru. Wa¿ne jest to, by s³uchaczom wyk³adu nie umkn¹³ podstawowy fakt: wybrany jêzyk programowania jest tylko narzêdziem s³u¿¹cym do pokazania na przyk³adach na czym polega programowanie obiektowe i jego wybór, aczkolwiek niezbêdny, jest rzecz¹ wtórn¹.
W trakcie wykładu omówimy wiele cech i konstrukcji Javy. Chcemy jednak mocno podkreślić, że nie jest naszym celem stworzenie podręcznika Javy. Na szczęście firmowa dokumentacja Javy jest bardzo obszerna i powszechnie, darmowo dostępna. Dotyczy to tak definicji języka jak i opisu standardowych bibliotek. Nie czujemy się więc zobowiązani do omawiania tych cech Javy, które z punktu widzenia tego wykładu mają małe znaczenie (na przykład zupełnie pomijamy kwestie związane ze współbieżnością). Java mimo młodego wieku przeszła już wiele zmian, my będziemy bazować na jej dystrybucji J2SE 1.6 i opisie języka podanym w trzecim wydaniu jego specyfikacji (z roku 2005).


W trakcie wyk³adu omówimy wiele cech i konstrukcji Javy. Chcemy jednak mocno podkreœliæ, ¿e nie jest naszym celem stworzenie podrêcznika Javy. Na szczêœcie firmowa dokumentacja Javy jest bardzo obszerna i powszechnie, darmowo dostêpna. Dotyczy to tak definicji jêzyka jak i opisu standardowych bibliotek. Nie czujemy siê wiêc zobowi¹zani do omawiania tych cech Javy, które z punktu widzenia tego wyk³adu maj¹ ma³e znaczenie (na przyk³ad zupe³nie pomijamy kwestie zwi¹zane ze wspó³bie¿noœci¹). Java mimo m³odego wieku przesz³a ju¿ wiele zmian, my bêdziemy bazowaæ na jej dystrybucji J2SE 1.6 i opisie jêzyka podanym w trzecim wydaniu jego specyfikacji (z roku 2005).
Standardowe biblioteki Javy bardzo rozbudowane - instalując podstawową dystrybucję Javy (J2SE) dostajemy do dyspozycji prawie cztery tysiące klas zgrupowanych w około dwustu pakietach. Oczywiście nie będziemy próbowali ich wszystkich omawiać w ramach naszego wykładu, natomiast będziemy czerpać z nich przykłady rozbudowanych i złożonych hierarchii klas. Zajmiemy się na przykład kolekcjami i strumieniami oraz klasami do tworzenia graficznych interfejsów użytkownika.


Standardowe biblioteki Javy s¹ bardzo rozbudowane - instaluj¹c podstawow¹ dystrybucjê Javy (J2SE) dostajemy do dyspozycji prawie cztery tysi¹ce klas zgrupowanych w oko³o dwustu pakietów. Oczywiœcie nie bêdziemy próbowali ich wszystkich omawiaæ w ramach naszego wyk³adu, natomiast bêdziemy czerpaæ z nich przyk³ady budowy rozbudowanych i z³o¿onych hierarchii klas (zajmiemy siê strumieniami, kolekcjami i klasami do tworzeniem graficznych interfejsów u¿ytkownika).
Po tych wyjaśnieniach pora zaczynać!


Po tych wyjaœnieniach pora zaczynaæ!
== Zaczynamy ==


=== Zaczynamy ===
Java jest wysokopoziomowym, kompilowanym, obiektowym językiem programowania z silną kontrolą typów. Składnia Javy jest wzorowana na C/C++. Jednym z założeń projektowych Javy było stworzenie języka wzorowanego na C++ ale bezpieczniejszego, dlatego w Javie nie ma na przykład wskaźników. Inne założenie projektowe dotyczyło przenośności programów. Ponieważ Java m.in. została zaprojektowana z myślą o uruchamianie programów pobieranych przez sieć, język musiał być tak zaprojektowany i wyspecyfikowany, by efekt działania programu nie zależał od tego, jakiej implementacji języka użyto (oczywiście przy założeniu jej zgodności ze specyfikacją języka). Ciekawą cechą Javy jest to, że kompilator nie generuje kodu maszynowego, lecz kod pośredni (tak zwany ''bajtkod''), który jest wykonywany przez ''wirtualną maszynę Javy''. Takich wirtualnych maszyn Javy stworzono wiele dla różnych systemów operacyjnych i komputerów. Dzięki temu rozwiązaniu i wspomnianej wcześniej ścisłej specyfikacji języka, można napisać program w Javie na przykład na komputerze domowym, skompilować go, przesłać skompilowaną jego postać na drugi koniec świata na duży komputer w centrum obliczeniowym i tam go wykonać. Efekt działania będzie taki sam.<ref>Zdarzają się wprawdzie także w Javie problemy z przenoszeniem oprogramowania, ale są one zwykle spowodowane nie niezgodnością samych maszyn wirtualnych, lecz specyficznymi cechami środowisk, w jakich te programy się uruchamia. Ale należy mocno podkreślić, o ile w przypadku większości języków programowania normą jest to, że uruchomienie programu na maszynie o innej architekturze wymaga co najmniej ponownego skompilowania programu, o tyle w przypadku Javy regułą jest to, że program działa na każdym komputerze tak samo, bez żadnych dodatkowych zabiegów.</ref>


Java jest wysokopoziomowym, kompilowanym, obiektowym jêzykiem programowania z siln¹ kontrol¹ typów. Sk³adnia Javy jest wzorowana na C/C++. Jednym z za³o¿eñ projektowych Javy by³o stworzenie jêzyka wzorowanego na C++ ale bezpieczniejszego, dlatego w Javie nie ma na przyk³ad wskaŸników. Inne za³o¿enie projektowe dotyczy³o przenoœnoœci programów w Javie. Poniewa¿ Java m.in. zosta³a zaprojektowana z myœl¹ o uruchamianie programów pobieranych przez sieæ, jêzyk musia³ byæ tak zaprojektowany i wyspecyfikowany, by efekt dzia³ania programu nie zale¿a³ od tego, jakiej implementacji jêzyka u¿yto (oczywiœcie przy za³o¿eniu jej zgodnoœci ze specyfikacj¹ jêzyka). Ciekaw¹ cech¹ Javy jest to, ¿e kompilator Javy nie generuje kodu maszynowego, lecz kod poœredni (tak zwany bajtkod), który jest wykonywany przez wirtualn¹ maszynê Javy. Takich wirtualnych maszyn Javy stworzono wiele dla ró¿nych systemów operacyjnych i komputerów. Dziêki temu rozwi¹zaniu i wspomnianej wczeœniej œcis³ej specyfikacji jêzyka, mo¿na napisaæ program w Javie na przyk³ad na komputerze domowym, skompilowaæ go, przes³aæ skompilowan¹ jego postaæ na drugi koniec œwiata na du¿y komputer w centrum obliczeniowym i tam go wykonaæ. Efekt dzia³ania bêdzie taki sam.<ref>Zdarzaj¹ siê wprawdzie tak¿e w Javie problemy z przenoszeniem oprogramowania, ale s¹ one zwykle spowodowane nie niezgodnoœci¹ samych maszyn wirtualnych, lecz specyficznymi cechami œrodowisk, w jakich te programy siê uruchamia. Ale nale¿y mocno podkreœliæ, o ile w przypadku wiêkszoœci jêzyków programowania norm¹ jest to, ¿e uruchomienie programu na maszynie o innej architekturze wymaga co najmniej ponownego skompilowania programu, o tyle w przypadku Javy regu³¹ jest to, ¿e program dzia³a na ka¿dym komputerze tak samo, bez ¿adnych dodatkowych zabiegów.</ref>
Od napisania przez Kernighana i Ritchiego znakomitej książki o C stało się zwyczajem, by pierwszy prezentowany w danym języku program wypisywał pozdrowienie dla świata, nie wypada nam więc postąpić inaczej:
 
<source>
Od wydania znakomitej ksi¹¿ki o C przez Kernighana i Ritchiego sta³o siê zwyczajem, by pierwszy prezentowany w danym jêzyku program wypisywa³ pozdrowienie dla œwiata, nie wypada nam wiêc post¹piæ inaczej:
public class HelloWorld {  
 
  public static void main(String[] args) {
  public class HelloWorld {  
    System.out.println("Hello world!");
    public static void main(String[] args) {
      System.out.println("Hello world!");
    }
   }
   }
}
</source>
Programy w Javie składają się z ''klas''. Na razie przyjmijmy - w wielkim uproszczeniu - że klasa jest takim lepszym typem rekordowym z Pascala (czy typem struktur z C), zawierającym oprócz zwykłych pól także funkcje. W językach obiektowych funkcje zdefiniowane w klasach nazywamy ''metodami''.


Programy w Javie sk³adaj¹ siê z ''klas''. Na razie przyjmijmy - w wielkim uproszczeniu - ¿e klasa jest takim lepszym typem rekordowym z Pascala (czy typem struktur z C), zawieraj¹cym oprócz zwyk³ych pól tak¿e funkcje. W jêzykach obiektowych funkcje zdefiniowane w klasach nazywamy ''metodami''.  
Przedstawiony program składa się z jednej klasy o nazwie ''HelloWorld''. Definicja klasy rozpoczyna się od słowa '''class''' poprzedzonego być może dodatkowymi modyfikatorami dostępu. Tu mamy jeden: '''public''', informujący kompilator, że nasza klasa jest dostępna także poza swoim pakietem.<ref>W tym przykładzie słowo ''public'' przed deklaracją klasy można pominąć.</ref> O pakietach powiemy dalej Przedstawiona klasa zawiera tylko jedną składową, jest nią metoda o nazwie ''main''. Ta nazwa metody nie jest przypadkowa. Wykonanie całego programu w Javie polega na wykonaniu metody main z głównej klasy programu (w naszym programie jest tylko jedna klasa, więc wybór klasy głównej nie nastręcza tu problemów).  


Przedstawiony program sk³ada siê z jednej klasy o nazwie ''HelloWorld''. Definicja klasy rozpoczyna siê od s³owa '''class''' poprzedzonego byæ mo¿e dodatkowymi specyfikacjami dostêpu (tu mamy jedn¹ '''public''', informuj¹c¹ kompilator, ¿e nasza klasa jest dostêpna tak¿e poza swoim pakietem, o pakietach powiemy dalej).<ref>W tym przyk³adzie s³owo ''public'' przed deklaracj¹ klasy mo¿na pomin¹æ.</ref> Przedstawiona klasa zawiera tylko jedn¹ sk³adow¹, jest ni¹ metoda o nazwie ''main''. Ta nazwa metody nie jest przypadkowa. Wykonanie ca³ego programu w Javie polega na wykonaniu metody main z g³ównej klasy programu (w naszym programie jest tylko jedna klasa, wiêc wybór klasy g³ównej nie nastrêcza tu problemów).  
Metoda main musi być przygotowana na przyjęcie jako argumentu tablicy napisów zawierającej argumenty podane w wierszu polecenia przy wywołaniu programu, nawet jeśli, tak jak przykładowa metoda, ignoruje te argumenty. Parametry metody deklaruje się bardzo podobnie jak w większości języków programowania. Podaje się sekwencję deklaracji poszczególnych parametrów (u nas ta sekwencja ma długość 1), a poszczególne deklaracje oddziela się jakimś separatorem (w Javie jest to akurat przecinek). Każda deklaracja określa nazwę i typ parametru. W niektórych językach programowania, np. Pascalu czy C#, deklaracja określa również sposób przekazywania parametrów, ale w Javie nie ma takich dodatkowych informacji. W naszym przykładzie parametr nazywa się ''args'', a typem jego wartości jest ''String[]'', czyli tablica napisów, przy czym długość tablicy może być dowolna. Zwróćmy uwagę, że w Javie najpierw podaje się typ, a potem nazwę parametru, choć to parametr, a nie typ, jest w tym miejscu definiowany.


Metoda main musi byæ przygotowana na przyjêcie jako argumentu tablicy napisów zawieraj¹cej argumenty podane w wierszu polecenia przy wywo³aniu programu (nawet jeœli, tak jak przyk³adowa metoda, ignoruje te argumenty). Deklaracja parametrów metody wygl¹da bardzo podobnie jak w wiêkszoœci jêzyków programowania. Podaje siê sekwencjê deklaracji poszczególnych parametrów (u nas ta sekwencja ma d³ugoœæ 1), a poszczególne deklaracje oddziela siê jakimœ separatorem (w Javie jest to akurat przecinek). Ka¿da deklaracja okreœla nazwê i typ parametru (czasem te¿ jakieœ dodatkowe cechy parametru, jak na przyk³ad sposób przekazywania - tak jest w Pascalu czy C# - ale w Javie nie ma takich dodatkowych informacji). W naszym przyk³adzie parametr nazywa siê ''args'' a typem jego wartoœci jest ''String[]'', czyli tablica napisów, przy czym d³ugoœæ tablicy mo¿e byæ dowolna. Zwróæmy uwagê, ¿e w Javie (tak jak w C i innych jêzykach wywodz¹cych siê z C) najpierw podaje siê typ, a potem nazwê parametru (choæ to parametr, a nie typ, jest w tym miejscu definiowany).
Jeżeli w Javie mamy metodę, która wylicza jakąś wartość, czyli metodę będącą funkcją, to w jej nagłówku musimy określić typ wyniku. Jeśli metoda jest procedurą, czyli nie daje żadnej wartości, to zamiast typu wyniku podajemy słowo kluczowe ''void''.<ref>Składniowo wygląda to tak samo jak deklaracja funkcji nie dającej wyniku w C (skąd wzięła się ta składnia), ale w C słowo ''void'' nie jest słowem kluczowym, lecz nazwą typu mającego zero wartości).</ref> Metoda ''main'' w Javie nie daje żadnego wyniku. Dlatego w jej nagłówku pojawiło się słowo ''void''. Typ wyniku metody podaje się w Javie (i wielu innych językach programowania) przed nazwą metody (niezgodnie niestety z tradycją matematyczną).  


Poniewa¿ w Javie wszystkie metody s¹ funkcjami (a nie procedurami), nale¿y jeszcze okreœliæ typ wyniku metody ''main''. Tak pechowo siê sk³ada, ¿e akurat ta metoda jest kiepskim kandydatem na funkcjê, bo nie daje ¿adnego wyniku (w przeciwieñstwie do funkcji main z C/C++). Tradycyjny trik stosowany w jêzykach wywodz¹cych siê od C polega na definiowaniu takich funkcji jako funkcji o pustym zbiorze wyników. Typ reprezentuj¹cy pusty zbiór wartoœci nazywa siê ''void''. Typ wyniku metody podaje siê w Javie (i wielu innych jêzykach programowania) przed nazw¹ metody (niezgodnie niestety z tradycj¹ matematyczn¹).  
Przed typem metody podane są dodatkowe specyfikatory. Słowo '''public''' oznacza, że metoda jest widoczna poza klasą. Słowo '''static''' oznacza, że jest to metoda klasowa, czyli taka, do wywołania której nie jest potrzebny egzemplarz obiektu tej klasy. Tak jak wspominaliśmy wcześniej klasa odpowiada typowi, zaś wartości typu klasowego - egzemplarze tego typu,  to ''obiekty''. Zwykle wywołujemy metody obiektów, ale można też wywoływać metody klas. Jest to szczególnie istotne w naszym programie, gdzie nie utworzyliśmy żadnego obiektu.


Przed typem metody podane s¹ dodatkowe specyfikatory. S³owo '''public''' oznacza, ¿e metoda jest widoczna poza klas¹. S³owo '''static''' oznacza, ¿e jest to metoda klasowa, czyli taka, do wywo³ania której nie jest potrzebny egzemplarz obiektu tej klasy. Tak jak wspominaliœmy wczeœniej klasa odpowiada typowi, zaœ wartoœci typu klasowego, egzemplarze tego typu, to ''obiekty''. Zwykle wywo³ujemy metody obiektów, ale mo¿na te¿ wywo³ywaæ metody klas. Jest to szczególnie istotne w naszym programie, gdzie nie utworzyliœmy ¿adnego obiektu.
Treść metody ''main'' składa się z jednego wywołania metody o nazwie ''println'', wypisującej na konsoli tekstowej (czyli na standardowym wyjściu programów działających w trybie tekstowym) tekst zadany jako parametr. Wywołując metodę w Javie musimy podać obiekt, na rzecz którego tę metodę wywołujemy. W tym przypadku jest to obiekt będący wartością zmiennej ''out''<ref>W dalszej części zamiast pisać "obiekt będący wartością zmiennej x" będziemy pisać po prostu "obiekt x", co wprawdzie jest nieco mniej precyzyjne, ale za to znacznie krótsze.</ref> reprezentujący standardowy strumień wyjściowy (jest to obiekt klasy PrintStream). Zmienna ''out'' jest atrybutem klasowym (polem w terminologii rekordów z Pascala) klasy ''System''. W tej klasie zebrano podstawowe operacje i obiekty związany ze standardowym wejściem i wyjściem oraz z systemem operacyjnym, pod kontrolą którego działa program. Zwróćmy uwagę, że obiekt ''out'' jest atrybutem klasowym, to znaczy dostępnym bezpośrednio w klasie ''System'', a nie jej egzemplarzach (których zresztą dla tej akurat klasy nie daje się tworzyć). W celu odwoływania się do metody lub atrybutu klasy w Javie stosuje się (typową dla większości języków obiektowych) notację kropkową:
<source>
  obiekt.składowa
</source>
gdzie składowa może być atrybutem lub metodą. W tym drugim przypadku podajemy jeszcze listę (być może pustą) jej parametrów otoczoną nawiasami okrągłymi. Oczywiście możemy się dalej odwoływać do składowych atrybutu lub wyniku metody, pisząc na przykład:
<source>
  osoba.adres.ulica
</source>
Tej samej składni używa się przy odwoływaniu się do atrybutów i metod klasowych
<source>
  klasa.składowa
</source>


Treœæ metody ''main'' sk³ada siê z jednego wywo³ania metody o nazwie ''println'', wypisuj¹cej na konsoli tekstowej (czyli na standardowym wyjœciu programów dzia³aj¹cych w trybie tekstowym), tekst zadany jako parametr. Wywo³uj¹c metodê w Javie musimy podaæ obiekt, na rzecz którego tê metodê wywo³ujemy. W tym przypadku jest to obiekt ''out'' reprezentuj¹cy standardowy strumieñ wyjœciowy (jest to obiekt klasy PrintStream). Obiekt ten jest atrybutem (polem w terminologii rekordów z Pascala) klasy System. W tej klasie zebrano podstawowe operacje i obiekty zwi¹zany ze standardowym wejœciem i wyjœciem oraz z systemem operacyjnym, pod kontrol¹ którego dzia³a program. Zwróæmy uwagê, ¿e obiekt ''out'' jest atrybutem klasowym, to znaczy dostêpnym bezpoœrednio w klasie System, a nie jej egzemplarzach (których zreszt¹ dla tej akurat klasy nie daje siê tworzyæ). W celu odwo³ywania siê do metody lub atrybutu klasy w Javie stosuje siê (typow¹ dla wiêkszoœci jêzyków obiektowych) notacjê kropkow¹:
== Komentarze ==
  obiekt.sk³adowa
 
gdzie sk³adowa mo¿e byæ metod¹ (i wtedy podajemy jeszcze jej parametry otoczone nawiasami okr¹g³ymi) lub atrybutem. Oczywiœcie mo¿emy siê dalej odwo³ywaæ do sk³adowych atrybutu lub wyniku metody.
 
=== Komentarze ===
 
Komentarze jednowierszowe zaczynaj¹ siê od dwu ukoœników i rozci¹gaj¹ a¿
do koñca wiersza.


Komentarze jednowierszowe zaczynają się od dwu ukośników i rozciągają aż do końca wiersza.
<source>
   // To jest komentarz jednowierszowy
   // To jest komentarz jednowierszowy
</source>


Komentarze wielowierszowe ograniczone przez /* na pocz¹tku i */ na koñcu (oczywiœcie komentarz wielowierszowy mo¿e mieœciæ siê ca³kowicie w jednym wierszu, mo¿e te¿ nawet byæ kilka takich komentarzy w jednym wierszu).
Komentarze wielowierszowe ograniczone przez /* na początku i */ na końcu. Oczywiście komentarz wielowierszowy może mieścić się całkowicie w jednym wierszu, a nawet może być kilka takich komentarzy w jednym wierszu.
 
<source>
   /* To jest  
   /* To jest  
     komentarz  
     komentarz  
     wielowierszowy */
     wielowierszowy */
   /* A */ /* to */ /* kilka */ /* takich */ /* komentarzy */
   /* A */ /* to */ /* kilka */ /* takich */ /* komentarzy */
</source>
    
    
Komentarzy wielowierszowych nie mo¿na zagnie¿d¿aæ jednych w drugich.
Komentarzy wielowierszowych nie można zagnieżdżać.
<source>
  /* To jest komentarz /* to nadal jest komentarz */ a_to_już_nie
</source>


  /* To jest komentarz /* to nadal jest komentarz */ a_to_ju¿_nie
== Elementy leksykalne Javy ==


=== Elementy leksykalne Javy ===
Nie będziemy tu zbytnio wchodzić w szczegóły, odsyłając bardziej zainteresowanych czytelników do raportu języka. Pozwolimy sobie jedynie podsumować najważniejsze elementy leksykalne.


Nie bêdziemy tu zbytnio wchodziæ w szczegó³y, odsy³aj¹c bardziej zainteresowanych czytelników do raportu jêzyka. Pozwolimy sobie jedynie podsumowaæ najwa¿niejsze elementy leksykalne.
=== Identyfikatory ===


==== Identyfikatory ====
* Identyfikatory składają się z liter i cyfr, znak "_" jest traktowany jako litera.<ref>Także znak "$", ale nie powinno się go używać we własnych identyfikatorach ze względu na specjalną funkcję, którą pełni.</ref>
* Duże i małe litery są rozróżniane, czyli identyfikator ''X'' jest różny od identyfikatora ''x''.
* Ponieważ Java używa kodowania znaków Unicode<ref>Dokładniej UTF-16, czyli 16-bitowej wersji Unicodu.</ref>, to identyfikatory mogą zawierać znaki narodowe (w tym polskie).
* Nie ma ograniczenia na długość identyfikatorów.
* Identyfikatory nie mogą być słowami kluczowymi Javy ani literałami ''true'', ''false'' i ''null''.


* Identyfikatory sk³adaj¹ siê z liter i cyfr, znak "_" jest traktowany jako litera<ref>Tak¿e znak "$", ale nie powinno siê go u¿ywaæ w zwyk³ych programach.</ref>,
Kwestia zasadności używania w programamch identyfikatorów pisanych z użyciem znaków narodowych, czyli nie po angielsku, budzi dużo kontrowersji. Autor tego tekstu, doceniając inne argumenty, dotyczące na przykład pracy w międzynarodowych zespołach, pozwala sobie wyrazić w tym miejscu radość z faktu, że rozwiązując w Javie np. zadanie o wilku, kozie i kapuście może nazwać zmienną ''łódź'' zamiast ''lodz'' czy ''boat''.
* Poniewa¿ Java u¿ywa u¿ywa kodowania znaków UniCode, to identyfikatory mog¹ zawieraæ znaki narodowe (w tym polskie),
* Nie ma ograniczenia na d³ugoœæ identyfikatorów,
* Identyfikatory nie mog¹ byæ s³owami kluczowymi Javy ani litera³ami ''true'', ''false'' i ''null''.
 
==== S³owa kluczowe ====


Oto lista s³ów kluczowych Javy
=== Słowa kluczowe ===


<!--{| border="0" cellpadding="5" cellspacing="0" align="center"
Oto lista słów kluczowych Javy
|+'''S³owa kluczowe'''
-->


{| border="1" cellpadding="2"
{|  
|-
| width="120" | abstract || width="120" | continue || width="120" | for || width="120" | new || width="120" | switch
|-
|assert ||default ||if ||package ||synchronized
|-
|-
abstract || continue || for || new || switch
|boolean ||do ||goto ||private ||this
|-
|-
assert default if package synchronized
|break ||double ||implements ||protected ||throw
|-
|-
boolean do goto private this
|byte ||else ||import ||public ||throws
|-
|-
break double implements protected throw
|case ||enum ||instanceof ||return ||transient
|-
|-
byte else import public throws
|catch ||extends ||int ||short|| try
|-
|-
case enum instanceof return transient
|char ||final ||interface ||static ||void
|-
|-
catch extends int short try
|class ||finally ||long ||strictfp ||volatile
|-
|-
char final interface static void
|const ||float ||native ||super ||while
|}
 
=== Literały ===
 
W Javie mamy 6 rodzajów literałów:
* Liczby całkowite (np. 13 czy -2627), mogą być dziesiętne, szesnastkowe (0xC) lub ósemkowe (np. 015<ref>Zauważmy, że 015 oznacza liczbę 13. Ta paskudnie wybrana notacja do zapisu liczb ósemkowych pochodzi z języka C (jak chyba wszystkie nieeleganckie konstrukcje w Javie oraz, co wypada przyznać, spora część pozostałych konstrukcji składniowych Javy).</ref>). Największa dozwolona wartość literału całkowitego to 2147483647 czyli 2^31-1<ref>Formalnie rzecz biorąc największą wartością jest 2147483648, ale można jej użyć tylko jako argumentu jednoargumentowego operatora -, czyli nieco nieformalnie można powiedzieć, że zakres literałów całkowitoliczbowych to -2147483648..2147483647. Analogiczna sytuacja ma miejsce dla literałów typu long, czyli możemy przyjąć, lekko upraszczając, że ich zakresem jest -9223372036854775808..9223372036854775807</ref>, zaś literału będącego długą liczbą całkowitą (''long'') to 9223372036854775807L czyli 2^63-1. Literały typu long oznaczamy pisząc po ich ostatniej cyfrze duże L. Dokładne wartości tych stałych nie mają dla nas oczywiście znaczenia, natomiast bardzo ważne jest zwrócenie uwagi na to, że specyfikacja Javy nie pozwala implementacjom tego języka na jakąkolwiek dowolność w tym względzie.
* Liczby rzeczywiste (np. 1.0 czy -4.9e12), mogą być zapisanie w systemie dziesiętnym lub szesnastkowym<ref>Największa skończona, dodatnia wartość literału rzeczywistego to 3.4028235e38f, zaś literału podwójnej precyzji to 1.7976931348623157e308. Najmniejsze dodatnie wartości to odpowiednio 1.40e-45f i 4.9e-324. Znajomość tych liczb nie będzie potrzebna w dalszej części tego wykładu. Warto tylko dodać, ze specyfikacja Javy określa nawet standard reprezentowania tych liczb w pamięci (jako IEEE 754).</ref>.
* Literały logiczne ''false'' i ''true''.
* Literały znakowe (np. 'a' czy '\n').
* Literały napisowe (np. "Ala ma kota"). Na uwagę zasługuje fakt, że napisy nie są w Javie wartościami typu pierwotnego, lecz wartościami klasy ''String''. Mamy tu zatem sytuację, w której składnia języka jest związana z jedną ze standardowych klas.
* Literał '''null'''.
 
=== Operatory ===
 
Zestaw operatorów w Javie jest dość bogaty<ref>Pojęcie operatora nie jest do końca ściśle zdefiniowane w specyfikacji języka Java. Na przykład lista operatorów z rozdz. 3.12 specyfikacji języka oddzielnie wymienia '''?''' i ''':''', które stanowią jeden operator, nie wymienia zaś '''instanceof''', które w dalszej części specyfikacji konsekwentnie jest nazywane operatorem. Na szczęście dla nas te subtelności nie mają znaczenia.</ref>:
 
{|
|-
|-
class finally long strictfp volatile
| width="30" | = ||width="30"| > ||width="30"| &lt; ||width="30"| &#33; ||width="30"| ~ ||width="30"| ? : ||width="30"| ||width="30"| ||width="30"| ||width="30"|
|-
|-
const float native super while
| == || &lt;= || >= || &#33;= || && || &#124;&#124; || ++ || --
|-
|-
| + || - || * ||/ || & || &#124; || ^ || %  || &lt;&lt; || >> || >>>
|-
| += || -= || *= || /= || &= || &#124;= || ^= || %= || &lt;&lt;= || >>= || >>>=
|}
<!-- może podać przykłady użycia, bądź pogrupować np. wg liczby parametrów lub rodzaju -->
== Elementy składniowe Javy ==
=== Typy ===
Jak już wspominaliśmy Java jest językiem programowania z silnym systemem typów. To oznacza, że każda zmienna, atrybut czy parametr ma zadeklarowany typ. Kompilator wylicza typy wszystkich wyrażeń w programie i sprawdza, czy wszystkie operatory i metody są używane zgodnie z ich deklaracjami, czyli z argumentami odpowiednich typów. Także elementy każdej instrukcji muszą mieć właściwe typy, np. warunek w pętli '''while''' musi być wyrażeniem o wartości typu logicznego.
Specyficzną cechą Javy jest to, że typy w tym języku są podzielone na dwie kategorie:
* typy pierwotne,
* typy referencyjne.
Typy pierwotne to grupa ośmiu typów zawierających wartości proste. Tymi typami są:
* typ wartości logicznych: ''boolean'',
* typy całkowitoliczbowe: ''byte'', ''short'', ''int'', ''long'', ''char''<ref>Kwestia zaliczenia typu char do typów liczbowych budzi (słusznie) sporo kontrowersji. Kto ma wątpliwości, niech napisze w programie w Javie na przykład tak: ''System.out.println('a'*5+3);''. Sposób traktowania wartości znakowych został przeniesiony do Javy z języka C.</ref>,
* typy zmiennopozycyjne: ''float'', ''double''.
Typy referencyjne dzielą się z kolei na następujące kategorie:
* typy klas,
* typy interfejsów,
* typy tablic.
Wartościami typów referencyjnych są referencje (w pewnym uproszczeniu można o nich myśleć jako o wskaźnikach) do obiektów lub wartość ''null''.
Ponadto istnieje typ o pustej nazwie, będący typem wyrażenia ''null''. Ponieważ nie ma on nazwy, nie da się nawet zadeklarować zmiennej tego typu. Wartość tego typu można rzutować na dowolny typ referencyjny, więc można myśleć o wartości ''null'' jako o wartości każdego typu referencyjnego i nie przejmować się istnieniem tego typu.
=== Obiekty ===
Przez obiekt rozumie się w Javie dynamicznie stworzony egzemplarz jakiejś klasy lub dynamicznie stworzoną tablicę<ref>Tablice są w Javie traktowane jak obiekty, mimo że typy tablicowe nie są typami klasowymi. To dość subtelna kwestia, standard języka dzieli typy referencyjne na klasowe i tablicowe (oraz interfejsy), ale egzemplarze tablic są traktowane np. przez operator instanceof jak obiekty klasy Object, zaś tablice jako podklasy klasy Object.</ref>. Żeby to zrównanie egzemplarzy klas i tablic uprawomocnić zadbano, by zarówno egzemplarze klas jak i tablice rozumiały wszystkie metody z klasy ''Object'' (jak zobaczymy w dalszych wykładach efekt ten dla egzemplarzy klas jest w naturalny sposób osiągany dzięki dziedziczeniu).
=== Zmienne ===
Zmienne są (zwykle) nazwanymi pojemnikami na pojedyncze wartości typu z jakim zostały zadeklarowane. Zmienne typów pierwotnych przechowują wartości dokładnie tych typów, zmienne typów referencyjnych przechowują wartość ''null'' albo referencję do obiektu typu będącego zadeklarowanym typem zmiennej bądź jego podklasą.
Wyróżniamy siedem rodzajów zmiennych:
* zmienne klasowe,
* zmienne egzemplarzowe,
* zmienne lokalne,
* elementy tablic (te zmienne są anonimowe),
* parametry metod,
* parametry konstruktorów,
* parametry obsługi wyjątków.
Każda zmienna musi być zadeklarowana. Z każdą zmienną związany jest jej typ podawany przy deklaracji zmiennej. Typ ten jest używany przez kompilator do sprawdzania poprawności operacji wykonywanych na zmiennych. Jeśli kompilator wykryje w programie użycie zmiennej niezgodne z jej typem, to taki program zostanie odrzucony jako niepoprawny. Sprawdzanie zgodności typów jest bardzo ważną i użyteczną własnością Javy. Dzięki niej wiele prostych błędów w programach (w rodzaju omyłkowej próby pomnożenia liczby przez napis) jest wykrywanych już w fazie kompilacji. Oczywiście błędów logicznych (to jest działań programu niezgodnych z jego specyfikacją) kompilator nie jest w stanie wykrywać. A szkoda.
W Javie każda zmienna musi być zainicjowana przed pierwszym pobraniem jej wartości. Wymusza to kompilator Javy. W przypadku parametrów zachowanie tego wymogu jest oczywiste. Dla pozostałych czterech rodzajów zmiennych jest to już trudniejsze. W Javie przyjęto, że takie zmienne albo będą jawnie inicjowane przez programistę albo będą miały nadaną automatycznie wartość początkową. Zasady są tu następujące: zmienne klasowe, egzemplarzowe i elementy tablic są inicjowane wartościami domyślnymi, zaś zmienne lokalne muszą być jawnie zainicjowane przez programistę.
Przy deklaracji czterech rodzajów zmiennych nie będących parametrami można jawnie podawać ich wartości początkowe. To bardzo dobra praktyka. Jeśli w deklaracji zmiennych klasowych, egzemplarzowych lub tablic nie podano ich wartość początkowej, to taka zmienna zostanie automatycznie zainicjowana wartością domyślną. Ta wartość zależy od typu zmiennej, ogólnie można by powiedzieć, że odpowiada wartości 0 (dla wartości logicznych będzie to ''false'', a dla typów referencyjnych wartość ''null'').
W przypadku zmiennych lokalnych programista musi sam zadbać o zainicjowanie zmiennej przed jej odczytaniem. Może to zrobić albo od razu przy deklaracji zmiennej lokalnej (najbezpieczniejsze rozwiązanie) albo później - byleby tylko przed pierwszym użyciem tej zmiennej. Przy czym w tym drugim przypadku musi to zrobić tak, by kompilator miał pewność, że zmienna rzeczywiście została zainicjowana. Wbrew pozorom problem stwierdzenia, czy w danym miejscu programu zmienna miała wcześniej przypisaną wartość wcale nie jest prosty. Wyobraźmy sobie na przykład, że między deklaracją zmiennej a owym miejscem programu były zagnieżdżone instrukcje warunkowe i instrukcje pętli. Okazuje się, że nie ma i nigdy nie będzie algorytmu, który mógłby zawsze rozstrzygnąć, czy zmiennej przypisano uprzednio wartość. Zatem kompilator Javy, jeśli nie ma pewności, czy zmiennej nadano wartość przed pierwszym odczytaniem jej wartości, odrzuca program. Na szczęście w większości sytuacji występujących w praktyce, zdolność kompilatora do wykrywania, czy zmienna została zainicjowana, jest wystarczająca.
=== Konwencja nazewnicze ===
Konwencje kodowania w Javie zalecają pisanie nazw klas i interfejsów z wielkiej litery, nazw zmiennych, parametrów i metod z małej. W nazwach wielosłowowych kolejne słowa piszemy z wielkiej litery (bez podkreślników, jest to tzw. notacja CamelCase). Nazwy pakietów piszemy zawsze wyłącznie małymi literami, nazwy stałych (static final) wyłącznie wielkimi.
=== Instrukcje ===
Zestaw instrukcji Javy jest dość standardowy dla C-podobnych języków. Dlatego ograniczymy się tu jedynie do wymienienia tych instrukcji:
* instrukcja pusta
<source>
;
</source>
* instrukcja deklaracji zmiennej lokalnej
<source>
int j = 13;
int[] tab = new int[10];
char zn;
</source>
* instrukacja etykietowana
<source>
koniec: return 0;
</source>
* instrukcja wyrażeniowa
<source>
i = 0;
o.wypisz(i);
</source>
* instrukcja warunkowa
<source>
if (i > 0) i--;
if (i > j)
  max = i;
else
  max = j;
</source>
* instrukcja asercji
<source>
assert i>0;
assert i>=0: "i (" + i + ") mniejsze od zera";
</source>
* instrukcja wyboru
<source>
switch (i){
  default: System.out.println("Wartość spoza zakresu"); break;  // Tak, nie musi być ostatnia!
  case 1: case 2: System.out.println("Dodatnie"); break;
  case -1: case -2: System.out.println("Ujemne"); break;
  case 0: System.out.println("Zero"); break;
}
</source>
* pętla dopóki
<source>
while (i>0)
  i--;
</source>
* pętla wykonuj
<source>
do
  i++
while ( i < 0 );
</source>
* pętla dla (wersja podstawowa)
<source>
for(int j = 0; j<tab.length; j++)
  tab[j] = 0;
</source>
* pętla dla (wersja rozszerzona)
<source>
for(int elt: tab)
  System.out.print(elt+", ");
</source>
* instrukcja break
<source>
break;
break dalej;
</source>
* instrukcja kontynuuj
<source>
continue;
continue dalej;
</source>
* instrukcja powrotu
<source>
return;
return 0;
</source>
* instrukcja zgłoszenia wyjątku
<source>
throw new Wyjątek();
</source>
* instrukcja synchronizująca (zagadnienia współbieżności pomijamy)
* instrukcja try
<source>
try{
  i = i/k;
}
catch (ArithmeticException e){
  System.out.println("Dzielenie przez zero");
}
finally{
  System.out.println("Kończymy");
}
</source>
* instrukcja bloku (umożliwia deklarowanie zmiennych i klas lokalnych)
<source>
{
  int i = 1;
  i++;
  int j = i + 1;
  return i+j;
}
</source>
Jak widać deklaracje zmiennych lokalnych można mieszać z  instrukcjami bloku. Zmienna lokalna jest widoczna w bloku od miejsca deklaracji do końca bloku. Nie można deklarować w bloku zmiennej o nazwie już widocznej w bloku (np. zdefiniowanej na zewnątrz bloku).
<!-- dopisać o widoczności zmiennych i przesłanianiu -->


=== Przypisy ===
== Przypisy ==


<references/>
<references/>

Aktualna wersja na dzień 00:15, 8 mar 2012

<<< Powrót

Wprowadzenie

Od tego wykładu zaczynamy poznawanie programowania obiektowego na przykładzie konkretnego języka programowania. Będzie nim Java. Java jest jednym ze współczesnych, nowoczesnych języków obiektowych. Jest też bardzo popularna. Wszystkie te cechy uzasadniają wybór tego języka jako narzędzia do pokazania mechanizmów obiektowych. Należy jednak podkreślić, że nie jest to jedyna możliwość. Niniejszy wykład z bardzo małymi zmianami można by było oprzeć na C#. Można też z bardzo dobrym skutkiem skonstruować wykład z programowania obiektowego zestawiając ze sobą czysto obiektowy Smalltalk z hybrydowym C++ (choć wymaga to już pewnej dojrzałości programistycznej od słuchaczy). Możliwości jest wiele, twórcy każdego wykładu muszą dokonać jakiegoś wyboru. Ważne jest to, by słuchaczom wykładu nie umknął podstawowy fakt: wybrany język programowania jest tylko narzędziem służącym do pokazania na przykładach na czym polega programowanie obiektowe. Wybór języka, aczkolwiek niezbędny, jest rzeczą wtórną.

W trakcie wykładu omówimy wiele cech i konstrukcji Javy. Chcemy jednak mocno podkreślić, że nie jest naszym celem stworzenie podręcznika Javy. Na szczęście firmowa dokumentacja Javy jest bardzo obszerna i powszechnie, darmowo dostępna. Dotyczy to tak definicji języka jak i opisu standardowych bibliotek. Nie czujemy się więc zobowiązani do omawiania tych cech Javy, które z punktu widzenia tego wykładu mają małe znaczenie (na przykład zupełnie pomijamy kwestie związane ze współbieżnością). Java mimo młodego wieku przeszła już wiele zmian, my będziemy bazować na jej dystrybucji J2SE 1.6 i opisie języka podanym w trzecim wydaniu jego specyfikacji (z roku 2005).

Standardowe biblioteki Javy są bardzo rozbudowane - instalując podstawową dystrybucję Javy (J2SE) dostajemy do dyspozycji prawie cztery tysiące klas zgrupowanych w około dwustu pakietach. Oczywiście nie będziemy próbowali ich wszystkich omawiać w ramach naszego wykładu, natomiast będziemy czerpać z nich przykłady rozbudowanych i złożonych hierarchii klas. Zajmiemy się na przykład kolekcjami i strumieniami oraz klasami do tworzenia graficznych interfejsów użytkownika.

Po tych wyjaśnieniach pora zaczynać!

Zaczynamy

Java jest wysokopoziomowym, kompilowanym, obiektowym językiem programowania z silną kontrolą typów. Składnia Javy jest wzorowana na C/C++. Jednym z założeń projektowych Javy było stworzenie języka wzorowanego na C++ ale bezpieczniejszego, dlatego w Javie nie ma na przykład wskaźników. Inne założenie projektowe dotyczyło przenośności programów. Ponieważ Java m.in. została zaprojektowana z myślą o uruchamianie programów pobieranych przez sieć, język musiał być tak zaprojektowany i wyspecyfikowany, by efekt działania programu nie zależał od tego, jakiej implementacji języka użyto (oczywiście przy założeniu jej zgodności ze specyfikacją języka). Ciekawą cechą Javy jest to, że kompilator nie generuje kodu maszynowego, lecz kod pośredni (tak zwany bajtkod), który jest wykonywany przez wirtualną maszynę Javy. Takich wirtualnych maszyn Javy stworzono wiele dla różnych systemów operacyjnych i komputerów. Dzięki temu rozwiązaniu i wspomnianej wcześniej ścisłej specyfikacji języka, można napisać program w Javie na przykład na komputerze domowym, skompilować go, przesłać skompilowaną jego postać na drugi koniec świata na duży komputer w centrum obliczeniowym i tam go wykonać. Efekt działania będzie taki sam.<ref>Zdarzają się wprawdzie także w Javie problemy z przenoszeniem oprogramowania, ale są one zwykle spowodowane nie niezgodnością samych maszyn wirtualnych, lecz specyficznymi cechami środowisk, w jakich te programy się uruchamia. Ale należy mocno podkreślić, o ile w przypadku większości języków programowania normą jest to, że uruchomienie programu na maszynie o innej architekturze wymaga co najmniej ponownego skompilowania programu, o tyle w przypadku Javy regułą jest to, że program działa na każdym komputerze tak samo, bez żadnych dodatkowych zabiegów.</ref>

Od napisania przez Kernighana i Ritchiego znakomitej książki o C stało się zwyczajem, by pierwszy prezentowany w danym języku program wypisywał pozdrowienie dla świata, nie wypada nam więc postąpić inaczej:

public class HelloWorld { 
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

Programy w Javie składają się z klas. Na razie przyjmijmy - w wielkim uproszczeniu - że klasa jest takim lepszym typem rekordowym z Pascala (czy typem struktur z C), zawierającym oprócz zwykłych pól także funkcje. W językach obiektowych funkcje zdefiniowane w klasach nazywamy metodami.

Przedstawiony program składa się z jednej klasy o nazwie HelloWorld. Definicja klasy rozpoczyna się od słowa class poprzedzonego być może dodatkowymi modyfikatorami dostępu. Tu mamy jeden: public, informujący kompilator, że nasza klasa jest dostępna także poza swoim pakietem.<ref>W tym przykładzie słowo public przed deklaracją klasy można pominąć.</ref> O pakietach powiemy dalej Przedstawiona klasa zawiera tylko jedną składową, jest nią metoda o nazwie main. Ta nazwa metody nie jest przypadkowa. Wykonanie całego programu w Javie polega na wykonaniu metody main z głównej klasy programu (w naszym programie jest tylko jedna klasa, więc wybór klasy głównej nie nastręcza tu problemów).

Metoda main musi być przygotowana na przyjęcie jako argumentu tablicy napisów zawierającej argumenty podane w wierszu polecenia przy wywołaniu programu, nawet jeśli, tak jak przykładowa metoda, ignoruje te argumenty. Parametry metody deklaruje się bardzo podobnie jak w większości języków programowania. Podaje się sekwencję deklaracji poszczególnych parametrów (u nas ta sekwencja ma długość 1), a poszczególne deklaracje oddziela się jakimś separatorem (w Javie jest to akurat przecinek). Każda deklaracja określa nazwę i typ parametru. W niektórych językach programowania, np. Pascalu czy C#, deklaracja określa również sposób przekazywania parametrów, ale w Javie nie ma takich dodatkowych informacji. W naszym przykładzie parametr nazywa się args, a typem jego wartości jest String[], czyli tablica napisów, przy czym długość tablicy może być dowolna. Zwróćmy uwagę, że w Javie najpierw podaje się typ, a potem nazwę parametru, choć to parametr, a nie typ, jest w tym miejscu definiowany.

Jeżeli w Javie mamy metodę, która wylicza jakąś wartość, czyli metodę będącą funkcją, to w jej nagłówku musimy określić typ wyniku. Jeśli metoda jest procedurą, czyli nie daje żadnej wartości, to zamiast typu wyniku podajemy słowo kluczowe void.<ref>Składniowo wygląda to tak samo jak deklaracja funkcji nie dającej wyniku w C (skąd wzięła się ta składnia), ale w C słowo void nie jest słowem kluczowym, lecz nazwą typu mającego zero wartości).</ref> Metoda main w Javie nie daje żadnego wyniku. Dlatego w jej nagłówku pojawiło się słowo void. Typ wyniku metody podaje się w Javie (i wielu innych językach programowania) przed nazwą metody (niezgodnie niestety z tradycją matematyczną).

Przed typem metody podane są dodatkowe specyfikatory. Słowo public oznacza, że metoda jest widoczna poza klasą. Słowo static oznacza, że jest to metoda klasowa, czyli taka, do wywołania której nie jest potrzebny egzemplarz obiektu tej klasy. Tak jak wspominaliśmy wcześniej klasa odpowiada typowi, zaś wartości typu klasowego - egzemplarze tego typu, to obiekty. Zwykle wywołujemy metody obiektów, ale można też wywoływać metody klas. Jest to szczególnie istotne w naszym programie, gdzie nie utworzyliśmy żadnego obiektu.

Treść metody main składa się z jednego wywołania metody o nazwie println, wypisującej na konsoli tekstowej (czyli na standardowym wyjściu programów działających w trybie tekstowym) tekst zadany jako parametr. Wywołując metodę w Javie musimy podać obiekt, na rzecz którego tę metodę wywołujemy. W tym przypadku jest to obiekt będący wartością zmiennej out<ref>W dalszej części zamiast pisać "obiekt będący wartością zmiennej x" będziemy pisać po prostu "obiekt x", co wprawdzie jest nieco mniej precyzyjne, ale za to znacznie krótsze.</ref> reprezentujący standardowy strumień wyjściowy (jest to obiekt klasy PrintStream). Zmienna out jest atrybutem klasowym (polem w terminologii rekordów z Pascala) klasy System. W tej klasie zebrano podstawowe operacje i obiekty związany ze standardowym wejściem i wyjściem oraz z systemem operacyjnym, pod kontrolą którego działa program. Zwróćmy uwagę, że obiekt out jest atrybutem klasowym, to znaczy dostępnym bezpośrednio w klasie System, a nie jej egzemplarzach (których zresztą dla tej akurat klasy nie daje się tworzyć). W celu odwoływania się do metody lub atrybutu klasy w Javie stosuje się (typową dla większości języków obiektowych) notację kropkową:

  obiekt.składowa

gdzie składowa może być atrybutem lub metodą. W tym drugim przypadku podajemy jeszcze listę (być może pustą) jej parametrów otoczoną nawiasami okrągłymi. Oczywiście możemy się dalej odwoływać do składowych atrybutu lub wyniku metody, pisząc na przykład:

  osoba.adres.ulica

Tej samej składni używa się przy odwoływaniu się do atrybutów i metod klasowych

  klasa.składowa

Komentarze

Komentarze jednowierszowe zaczynają się od dwu ukośników i rozciągają aż do końca wiersza.

  // To jest komentarz jednowierszowy

Komentarze wielowierszowe są ograniczone przez /* na początku i */ na końcu. Oczywiście komentarz wielowierszowy może mieścić się całkowicie w jednym wierszu, a nawet może być kilka takich komentarzy w jednym wierszu.

  /* To jest 
     komentarz 
     wielowierszowy */
  /* A */ /* to */ /* kilka */ /* takich */ /* komentarzy */

Komentarzy wielowierszowych nie można zagnieżdżać.

  /* To jest komentarz /* to nadal jest komentarz */ a_to_już_nie

Elementy leksykalne Javy

Nie będziemy tu zbytnio wchodzić w szczegóły, odsyłając bardziej zainteresowanych czytelników do raportu języka. Pozwolimy sobie jedynie podsumować najważniejsze elementy leksykalne.

Identyfikatory

  • Identyfikatory składają się z liter i cyfr, znak "_" jest traktowany jako litera.<ref>Także znak "$", ale nie powinno się go używać we własnych identyfikatorach ze względu na specjalną funkcję, którą pełni.</ref>
  • Duże i małe litery są rozróżniane, czyli identyfikator X jest różny od identyfikatora x.
  • Ponieważ Java używa kodowania znaków Unicode<ref>Dokładniej UTF-16, czyli 16-bitowej wersji Unicodu.</ref>, to identyfikatory mogą zawierać znaki narodowe (w tym polskie).
  • Nie ma ograniczenia na długość identyfikatorów.
  • Identyfikatory nie mogą być słowami kluczowymi Javy ani literałami true, false i null.

Kwestia zasadności używania w programamch identyfikatorów pisanych z użyciem znaków narodowych, czyli nie po angielsku, budzi dużo kontrowersji. Autor tego tekstu, doceniając inne argumenty, dotyczące na przykład pracy w międzynarodowych zespołach, pozwala sobie wyrazić w tym miejscu radość z faktu, że rozwiązując w Javie np. zadanie o wilku, kozie i kapuście może nazwać zmienną łódź zamiast lodz czy boat.

Słowa kluczowe

Oto lista słów kluczowych Javy

abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while

Literały

W Javie mamy 6 rodzajów literałów:

  • Liczby całkowite (np. 13 czy -2627), mogą być dziesiętne, szesnastkowe (0xC) lub ósemkowe (np. 015<ref>Zauważmy, że 015 oznacza liczbę 13. Ta paskudnie wybrana notacja do zapisu liczb ósemkowych pochodzi z języka C (jak chyba wszystkie nieeleganckie konstrukcje w Javie oraz, co wypada przyznać, spora część pozostałych konstrukcji składniowych Javy).</ref>). Największa dozwolona wartość literału całkowitego to 2147483647 czyli 2^31-1<ref>Formalnie rzecz biorąc największą wartością jest 2147483648, ale można jej użyć tylko jako argumentu jednoargumentowego operatora -, czyli nieco nieformalnie można powiedzieć, że zakres literałów całkowitoliczbowych to -2147483648..2147483647. Analogiczna sytuacja ma miejsce dla literałów typu long, czyli możemy przyjąć, lekko upraszczając, że ich zakresem jest -9223372036854775808..9223372036854775807</ref>, zaś literału będącego długą liczbą całkowitą (long) to 9223372036854775807L czyli 2^63-1. Literały typu long oznaczamy pisząc po ich ostatniej cyfrze duże L. Dokładne wartości tych stałych nie mają dla nas oczywiście znaczenia, natomiast bardzo ważne jest zwrócenie uwagi na to, że specyfikacja Javy nie pozwala implementacjom tego języka na jakąkolwiek dowolność w tym względzie.
  • Liczby rzeczywiste (np. 1.0 czy -4.9e12), mogą być zapisanie w systemie dziesiętnym lub szesnastkowym<ref>Największa skończona, dodatnia wartość literału rzeczywistego to 3.4028235e38f, zaś literału podwójnej precyzji to 1.7976931348623157e308. Najmniejsze dodatnie wartości to odpowiednio 1.40e-45f i 4.9e-324. Znajomość tych liczb nie będzie potrzebna w dalszej części tego wykładu. Warto tylko dodać, ze specyfikacja Javy określa nawet standard reprezentowania tych liczb w pamięci (jako IEEE 754).</ref>.
  • Literały logiczne false i true.
  • Literały znakowe (np. 'a' czy '\n').
  • Literały napisowe (np. "Ala ma kota"). Na uwagę zasługuje fakt, że napisy nie są w Javie wartościami typu pierwotnego, lecz wartościami klasy String. Mamy tu zatem sytuację, w której składnia języka jest związana z jedną ze standardowych klas.
  • Literał null.

Operatory

Zestaw operatorów w Javie jest dość bogaty<ref>Pojęcie operatora nie jest do końca ściśle zdefiniowane w specyfikacji języka Java. Na przykład lista operatorów z rozdz. 3.12 specyfikacji języka oddzielnie wymienia ? i :, które stanowią jeden operator, nie wymienia zaś instanceof, które w dalszej części specyfikacji konsekwentnie jest nazywane operatorem. Na szczęście dla nas te subtelności nie mają znaczenia.</ref>:

= > < ! ~ ? :
== <= >= != && || ++ --
+ - * / & | ^ % << >> >>>
+= -= *= /= &= |= ^= %= <<= >>= >>>=


Elementy składniowe Javy

Typy

Jak już wspominaliśmy Java jest językiem programowania z silnym systemem typów. To oznacza, że każda zmienna, atrybut czy parametr ma zadeklarowany typ. Kompilator wylicza typy wszystkich wyrażeń w programie i sprawdza, czy wszystkie operatory i metody są używane zgodnie z ich deklaracjami, czyli z argumentami odpowiednich typów. Także elementy każdej instrukcji muszą mieć właściwe typy, np. warunek w pętli while musi być wyrażeniem o wartości typu logicznego.

Specyficzną cechą Javy jest to, że typy w tym języku są podzielone na dwie kategorie:

  • typy pierwotne,
  • typy referencyjne.

Typy pierwotne to grupa ośmiu typów zawierających wartości proste. Tymi typami są:

  • typ wartości logicznych: boolean,
  • typy całkowitoliczbowe: byte, short, int, long, char<ref>Kwestia zaliczenia typu char do typów liczbowych budzi (słusznie) sporo kontrowersji. Kto ma wątpliwości, niech napisze w programie w Javie na przykład tak: System.out.println('a'*5+3);. Sposób traktowania wartości znakowych został przeniesiony do Javy z języka C.</ref>,
  • typy zmiennopozycyjne: float, double.

Typy referencyjne dzielą się z kolei na następujące kategorie:

  • typy klas,
  • typy interfejsów,
  • typy tablic.

Wartościami typów referencyjnych są referencje (w pewnym uproszczeniu można o nich myśleć jako o wskaźnikach) do obiektów lub wartość null.

Ponadto istnieje typ o pustej nazwie, będący typem wyrażenia null. Ponieważ nie ma on nazwy, nie da się nawet zadeklarować zmiennej tego typu. Wartość tego typu można rzutować na dowolny typ referencyjny, więc można myśleć o wartości null jako o wartości każdego typu referencyjnego i nie przejmować się istnieniem tego typu.

Obiekty

Przez obiekt rozumie się w Javie dynamicznie stworzony egzemplarz jakiejś klasy lub dynamicznie stworzoną tablicę<ref>Tablice są w Javie traktowane jak obiekty, mimo że typy tablicowe nie są typami klasowymi. To dość subtelna kwestia, standard języka dzieli typy referencyjne na klasowe i tablicowe (oraz interfejsy), ale egzemplarze tablic są traktowane np. przez operator instanceof jak obiekty klasy Object, zaś tablice jako podklasy klasy Object.</ref>. Żeby to zrównanie egzemplarzy klas i tablic uprawomocnić zadbano, by zarówno egzemplarze klas jak i tablice rozumiały wszystkie metody z klasy Object (jak zobaczymy w dalszych wykładach efekt ten dla egzemplarzy klas jest w naturalny sposób osiągany dzięki dziedziczeniu).

Zmienne

Zmienne są (zwykle) nazwanymi pojemnikami na pojedyncze wartości typu z jakim zostały zadeklarowane. Zmienne typów pierwotnych przechowują wartości dokładnie tych typów, zmienne typów referencyjnych przechowują wartość null albo referencję do obiektu typu będącego zadeklarowanym typem zmiennej bądź jego podklasą.

Wyróżniamy siedem rodzajów zmiennych:

  • zmienne klasowe,
  • zmienne egzemplarzowe,
  • zmienne lokalne,
  • elementy tablic (te zmienne są anonimowe),
  • parametry metod,
  • parametry konstruktorów,
  • parametry obsługi wyjątków.

Każda zmienna musi być zadeklarowana. Z każdą zmienną związany jest jej typ podawany przy deklaracji zmiennej. Typ ten jest używany przez kompilator do sprawdzania poprawności operacji wykonywanych na zmiennych. Jeśli kompilator wykryje w programie użycie zmiennej niezgodne z jej typem, to taki program zostanie odrzucony jako niepoprawny. Sprawdzanie zgodności typów jest bardzo ważną i użyteczną własnością Javy. Dzięki niej wiele prostych błędów w programach (w rodzaju omyłkowej próby pomnożenia liczby przez napis) jest wykrywanych już w fazie kompilacji. Oczywiście błędów logicznych (to jest działań programu niezgodnych z jego specyfikacją) kompilator nie jest w stanie wykrywać. A szkoda.

W Javie każda zmienna musi być zainicjowana przed pierwszym pobraniem jej wartości. Wymusza to kompilator Javy. W przypadku parametrów zachowanie tego wymogu jest oczywiste. Dla pozostałych czterech rodzajów zmiennych jest to już trudniejsze. W Javie przyjęto, że takie zmienne albo będą jawnie inicjowane przez programistę albo będą miały nadaną automatycznie wartość początkową. Zasady są tu następujące: zmienne klasowe, egzemplarzowe i elementy tablic są inicjowane wartościami domyślnymi, zaś zmienne lokalne muszą być jawnie zainicjowane przez programistę.

Przy deklaracji czterech rodzajów zmiennych nie będących parametrami można jawnie podawać ich wartości początkowe. To bardzo dobra praktyka. Jeśli w deklaracji zmiennych klasowych, egzemplarzowych lub tablic nie podano ich wartość początkowej, to taka zmienna zostanie automatycznie zainicjowana wartością domyślną. Ta wartość zależy od typu zmiennej, ogólnie można by powiedzieć, że odpowiada wartości 0 (dla wartości logicznych będzie to false, a dla typów referencyjnych wartość null).

W przypadku zmiennych lokalnych programista musi sam zadbać o zainicjowanie zmiennej przed jej odczytaniem. Może to zrobić albo od razu przy deklaracji zmiennej lokalnej (najbezpieczniejsze rozwiązanie) albo później - byleby tylko przed pierwszym użyciem tej zmiennej. Przy czym w tym drugim przypadku musi to zrobić tak, by kompilator miał pewność, że zmienna rzeczywiście została zainicjowana. Wbrew pozorom problem stwierdzenia, czy w danym miejscu programu zmienna miała wcześniej przypisaną wartość wcale nie jest prosty. Wyobraźmy sobie na przykład, że między deklaracją zmiennej a owym miejscem programu były zagnieżdżone instrukcje warunkowe i instrukcje pętli. Okazuje się, że nie ma i nigdy nie będzie algorytmu, który mógłby zawsze rozstrzygnąć, czy zmiennej przypisano uprzednio wartość. Zatem kompilator Javy, jeśli nie ma pewności, czy zmiennej nadano wartość przed pierwszym odczytaniem jej wartości, odrzuca program. Na szczęście w większości sytuacji występujących w praktyce, zdolność kompilatora do wykrywania, czy zmienna została zainicjowana, jest wystarczająca.

Konwencja nazewnicze

Konwencje kodowania w Javie zalecają pisanie nazw klas i interfejsów z wielkiej litery, nazw zmiennych, parametrów i metod z małej. W nazwach wielosłowowych kolejne słowa piszemy z wielkiej litery (bez podkreślników, jest to tzw. notacja CamelCase). Nazwy pakietów piszemy zawsze wyłącznie małymi literami, nazwy stałych (static final) wyłącznie wielkimi.

Instrukcje

Zestaw instrukcji Javy jest dość standardowy dla C-podobnych języków. Dlatego ograniczymy się tu jedynie do wymienienia tych instrukcji:

  • instrukcja pusta
;
  • instrukcja deklaracji zmiennej lokalnej
int j = 13;
int[] tab = new int[10];
char zn;
  • instrukacja etykietowana
koniec: return 0;
  • instrukcja wyrażeniowa
i = 0;
o.wypisz(i);
  • instrukcja warunkowa
if (i > 0) i--;
if (i > j)
  max = i; 
else 
  max = j;
  • instrukcja asercji
assert i>0;
assert i>=0: "i (" + i + ") mniejsze od zera";
  • instrukcja wyboru
switch (i){
  default: System.out.println("Wartość spoza zakresu"); break;  // Tak, nie musi być ostatnia!
  case 1: case 2: System.out.println("Dodatnie"); break;
  case -1: case -2: System.out.println("Ujemne"); break;
  case 0: System.out.println("Zero"); break;
}
  • pętla dopóki
while (i>0)
   i--;
  • pętla wykonuj
do 
  i++ 
while ( i < 0 );
  • pętla dla (wersja podstawowa)
for(int j = 0; j<tab.length; j++)
  tab[j] = 0;
  • pętla dla (wersja rozszerzona)
for(int elt: tab)
  System.out.print(elt+", ");
  • instrukcja break
break;
break dalej;
  • instrukcja kontynuuj
continue;
continue dalej;
  • instrukcja powrotu
return;
return 0;
  • instrukcja zgłoszenia wyjątku
throw new Wyjątek();
  • instrukcja synchronizująca (zagadnienia współbieżności pomijamy)
  • instrukcja try
try{
  i = i/k;
}
catch (ArithmeticException e){
  System.out.println("Dzielenie przez zero");
}
finally{
  System.out.println("Kończymy");
}
  • instrukcja bloku (umożliwia deklarowanie zmiennych i klas lokalnych)
{
  int i = 1; 
  i++;
  int j = i + 1;
  return i+j;
}

Jak widać deklaracje zmiennych lokalnych można mieszać z instrukcjami bloku. Zmienna lokalna jest widoczna w bloku od miejsca deklaracji do końca bloku. Nie można deklarować w bloku zmiennej o nazwie już widocznej w bloku (np. zdefiniowanej na zewnątrz bloku).


Przypisy

<references/>