PO Wstęp do Javy

Z Studia Informatyczne
Wersja z dnia 14:25, 9 sty 2009 autorstwa Ciebie (dyskusja | edycje) (Przywrócono przedostatnią wersję, jej autor to Dorota)
Przejdź do nawigacjiPrzejdź do wyszukiwania

<<< Powrót

Wprowadzenie

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).

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ć!

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 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 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) {
     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 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). 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 (w przeciwieństwie do funkcji main z C/C++). 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 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ą:

 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.

 // 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, może też nawet być kilka takich komentarzy w jednym wierszu).

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

Komentarzy wielowierszowych nie można zagnieżdżać jednych w drugich.

 /* 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ć w zwykłych programach.</ref>,
  • 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

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),
  • liczby rzeczywiste (np. 1.0 czy -4.9e12), mogą być dziesiętne, szesnastkowe lub binarne,
  • 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 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:

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

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, 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 parametrami 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 dającym wartość logiczną).

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,
  • typy zmiennopozycyjne: float, double.

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

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

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 pusty, będący typem wyrażenia null. Ten typ nie ma nazwy (więc 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 typu pustego.

Obiekty

Przez obiekty rozumie się w Javie dynamicznie stworzony egzemplarz jakiejś klasy lub dynamicznie stworzoną tablicę (mimo że w Javie tablice nie są typami klasowymi). Ż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 jest zupełnie naturalnie 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 takiego jak typ zmiennej bądź do jego podklasy.

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, przy deklaracji zmiennej podaje się jej typ. Przy deklaracji zmiennych klasowych, egzemplarzowych i elementów tablic można podać ich wartość początkową. Jeśli tego nie zrobimy 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 zainicjalizowanie zmiennej przed jej użyciem.

Instrukcje

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

  • instruckja pusta
 ;
  • instrukcja deklaracji zmiennej lokalnej
 int j = 13;
 int[] tab = new int[10];
  • 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 continue
 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 = j/k;
 }
 catch (Exception e){
   System.out.println("Dzielenie przez zero");}
 finally{
   System.out.println("Kończymy");}
  • instrukcja bloku (umożliwia deklarowanie zmiennych lokalnych, a także klas lokalnych), np.
 {
   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.


Przypisy

<references/>