« poprzedni punkt 

4. Instalacja Javy,  pierwszy program i kilka elementów składni

Instrukcje instalacyjne Javy dla różnych systemów można znaleźć tu:

dla Linuxa
dla Windows
dlaOS/2

Pisząc programy możemy korzystać ze zintegrowanych środowisk programowania (IDE) zob. listę miesięcznika JavaWorld , a także IDE polecane na I semestr dla Windows  lub po prostu używać wybranego edytora i w sesjach znakowych (terminalach, oknach DOS) uruchamiać kompilator i maszynę wirtualną Javy.

Jeśli nie korzystamy z IDE to:
  • powinniśmy zapewnić, by katalog, w którym program instalacyjny umieścił  kompilator Javy (javac.exe) oraz interpreter - maszynę wirtualną Javy (java.exe) znajdował się na ścieżce dostępu (PATH),
  • program źródłowy Javy zapisujemy w pliku źródłowym za pomocą dowolnego edytora (np. w pliku Test.java),
  • plik źródłowy np. Test.java kompilujemy za pomocą polecenia javac (np. javac Test.java)
  • w rezultacie kompilacji otrzymamy plik(i) klasowe (z rozszerzeniem .class) (np. Test.class)
  • wykonanie programu uruchamiamy za pomocą polecenia java (np. java Test)

Uwagi:

  1. Program źródłowy może być zapisany w wielu plikach z rozszerzeniem .java; w każdym pliku musi występować pełna definicja jednej lub kilku klas.
  2. Nazwa pliku powinna być dokładnie (a więc uwzględniając wielkie i małe litery) taka sama jak nazwa publicznej  klasy zdefiniowanej w tym pliku (czyli klasy z kwalifikatorem public; co to jest klasa publiczna - dowiemy się później). W  pliku źródłowym może wystąpić tylko jedna klasa publiczna.
  3. Uruchamiając maszynę wirtualną (polecenie java) podajemy jako argument nazwę klasy, w której jest zdefiniowana metoda main. Nie podajemy rozszerzenia pliku (".class") czyli: java Test a nie java Test.class.

Nasz pierwszy program może wyglądać tak:

public class Test {

   public static void main( String[] args ) {
      System.out.println( "Dzień dobry!");
   } 

}

Komentarze:

  1. Słowa kluczowe języka zaznaczono na niebiesko.
  2. Na razie nie przejmujemy się dziwnymi słowami (public, void itp.). Dowiemy się o nich z następnych wykładów.
  3. Program w Javie zawsze składa się z definicji klas. Tu mamy zdefiniowaną jedną klasę o nazwie Test. Do definiowania klas służy słowo kluczowe class. Samą definicję klasy podajemy w następujących potem nawiasach klamrowych. Nawiasy te zaznaczono na czerwono.
  4. W klasie Test została zdefiniowana metoda o nazwie  main (czyli coś bardzo podobnego do funkcji)
  5. Ciało metody zapisujemy w nawiasach klamrowych (zaznaczone na zielono).
  6. Wykonanie programu zacznie się właśnie od metody main, ale musi ona mieć dokładnie taki nagłówek jak podano (public static void main(String[ ] args)), z tym wyjatkiem, że słowo args możemy zastąpić innym (jest to nazwa zmiennej).
  7. Jak widać, metoda main ma parametr o nazwie args. Oznacza on tablicę łańcuchów znakowych (obiektów typu String) - która zawiera argumenty przekazane przy uruchomieniu programu (np. podane w wierszu poleceń sesji znakowej). W naszym programie nie korzystamy z tych argumentów.
  8. W metodzie main wywołujemy metodę println (już dla nas przygotowaną i znajdującą się w "bibliotece" standardowych klas języka), która wyprowadza przekazany jej argument na konsolę. Wywołanie metody stanowi wyrażenie. Kończąc je średnikiem przekształciliśmy je w instrukcję programu "do wykonania".
  9. Na razie nie przejmujemy się tym po co i dlaczego trzeba pisać System.out.println. Dowiemy się tego później.
  10. Argument podany w wywołaniu metody println - to literał łańcuchowy (napis podany literalnie). Pojęcie literału łancuchowego znamy już z poprzedniego wykładu.
  11. Zapisany program musimy skompilować: javac Test.java (lub środkami IDE)
  12. W rezultacie otrzymamy plik Test.class
  13. Uruchamiamy program ( w IDE lub  pisząc: java Test). Program wyprowadzi na konsolę napis "Dzień dobry!"

Trzeba jednak pamiętać, że obowiązuje nas przejrzysty styl programowania. Elementy stylu będziemy poznawać sukcesywnie przy omawianiu języka. W omawianym przykładzie zastosowaliśmy wcięcia i odpowiednie rozmieszczenie nawiasów klamrowych

Przy kompilacji programu znaki spacji, tabulacji, końca wiersza (tzw. whitespaces) występujące pomiędzy konstrukcjami składniowymi języka (takimi jak nazwy zmiennych, metod, słowa kluczowe, literały itp.) są pomijane. Zatem możemy dowolnie "formatować" kod programu (dzielić na wiersze, umieszczać dodatkowe spacje np. po nawiasie otwierającym listę argumentów wywołania metody).

Przy kompilacji pomijane są również komentarze.
W Javie mamy trzy rodzaje komentarzy:

  • teksty umieszczone w jednym wierszu programu po znakach // (komentarze jednowierszowe; cały tekst po znakach // do końca wiersza traktowany jest jako komentarz),
  • teksty umieszczone pomiędzy znakami /* i */ (teksty te mogą obejmować kilka wierszy),
  • teksty umieszczone pomiędzy znakami /** i */ (komentarze dokumentacyjne, które sa przetwarzane przez oprogramowanie tworzące dokumentację, np. javadoc; mogą obejmować kilka wierszy).

Przykład:

/**
 Klasa TestComm pokazuje
 użycie komentarzy
 (to jest komentarz dokumentacyjny)
*/

public class TestComm {

  /*
     to jest komentarz
     wielowierszowy
  */

  /* to też jest
     komentarz */

  // a to komentarz jednowierszowy

   public static void main( String[] args ) {
      // i tu też komentarz
      System.out.println( "Zob. komentarze" );   // i tu też
   }

}

Oczywiście, komentarze nie mogą rozdzielać jednolitych konstrukcji składniowych (np. znajdować się "w środku" nazwy zmiennej czy metody).

W trakcie kompilacji programu sprawdzana jest jego składniowa poprawność. Kompilacja może więc zakończyć się niepowodzeniem (wtedy nie dostaniemy pliku .class), a kompilator powiadomi nas o tym gdzie i jakie błędy wystąpiły.
Np. gdybyśmy w naszym programie testowym zapomnieli zamknąć nawias okrągły w wywołaniu metody println:

System.out.println( "Dzień dobry!";

to kompilator wyprowadziłby następujący komunikat:

Test.java:4: ')' expected
      System.out.println( "Dzień dobry!";
                                        ^
1 error

Mamy tu wyraźnie powiedziane, że błąd wystąpił w 4 wierszu pliku Test.java, że oczekiwany był okrągły nawias zamykający (a zabrakło go), przy czym miejsce w którym wystąpił problem wskazane jest znakiem ^.

Nie zawsze komunikaty z kompilacji będą tak klarowne. Czasami błąd  nie będzie dokładnie zlokalizowany (informacja będzie wskazywać na inny wiersz niż ten, w którym wystąpił błąd). Musimy wtedy głębiej zastanowić się nad przyczyną błędu i przeanalizować poprawność poprzedzających wierszy programu.

Program, który przeszedł etap kompilacji jest poprawny składniowo, ale jego wykonanie niekoniecznie musi być poprawne. Mogą w nim bowiem wystąpić tzw. błędy fazy wykonania.
Na przykład, poniższy program kompiluje się bezbłędnie,

public class TestRTE {

   static String napis;

   public static void main( String[] args ) {
      System.out.println( napis.length()  );
   }

ale przy jego wykonaniu wystąpi błąd:

Exception in thread "main" java.lang.NullPointerException
        at TestRTE.main(TestRTE.java:7)

bowiem próbujemy pobrać długość łańcucha znakowego (napis), który nie istnieje. Zresztą przyczyna nie jest tu istotna, teraz ważne jest tylko byśmy wiedzieli, że  błąd wykonania może wystąpić mimo pomyślnej kompilacji).
Zwrócmy uwagę, że Java nazywa takie błędy wyjątkami (exception) i że JVM podaje jaki rodzaj wyjątku wystąpił, w jakiej klasie i w jakiej metodzie i który wiersz w programie go spowodował.

W końcu warto jeszcze raz podkreślić oczywistą prawdę, że nawet jeśli program nie ma błędów składniowych (kompilacja pomyślna) i nie występują błędy fazy wykonania - to jeszcze nie znaczy, że jest on poprawny.
Na przykład taki program przejdzie pomyślnie kompilację i wykona się bez błędów (zgłaszanych przez JVM):

public class TestBad {

   public static void main( String[] args ) {
      System.out.println("2 + 2 = " + (2 - 2) );
   }

}

2 + 2 = 0

ale wyprowadzi (raczej) niepoprawny wynik, bo wygląda na to, że programista pomylił się i zamiast operatora + użył operatora -.

Takie błędy są najtrudniejsze do wykrycia, a ich unikanie (czy walka z nimi) wymaga:

  • tworzenia poprawnych algorytmów,
  • starannego ich zapisu w języku programowania,
  • pieczołowitego testowania gotowego programu, przy różnych danych wejściowych i oceny czy wyniki podawane przez program są poprawne.

« poprzedni punkt