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:
- 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.
- 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.
- 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:
- Słowa kluczowe języka zaznaczono na niebiesko.
- Na razie nie przejmujemy się dziwnymi słowami (public, void itp.). Dowiemy się o nich z następnych wykładów.
- 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.
- W klasie Test została zdefiniowana metoda o nazwie main (czyli coś bardzo podobnego do funkcji)
- Ciało metody zapisujemy w nawiasach klamrowych (zaznaczone na zielono).
- 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).
- 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.
- 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".
- Na razie nie przejmujemy się tym po co i dlaczego trzeba pisać System.out.println. Dowiemy się tego później.
- 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.
- Zapisany program musimy skompilować: javac Test.java (lub środkami IDE)
- W rezultacie otrzymamy plik Test.class
- 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.
|