SPIS TEMATÓW
W części pierwszej czytaliście
1. Język Java
1.1. Elementarny program: tekst źródłowy, kompilacja,
interpretacja
1.2. Klasy:
definicja, dziedziczenie, tworzenie obiektów
1.3. Interfejsy
1.4. Pliki źródłowe i
pakiety
1.5. Polimorfizm
1.6. Obsługa wyjątków
1.7. Zarządzanie
pamięcią
1.8. Współbieżność
1.8.1 Synchronizacja wątków
1.9. Obiekty sieciowe
Druga część kursu
2. Aplety - programy
Javy na stronach WWW
3. Standardowe klasy
Javy
4. Servlety - programy
Javy na serwerze WWW
5. Podsumowanie
6. Programy - ćwiczenia
Aplet jest niewielkim programem Javy przeznaczonym do uruchamiania w obrębie
innej aplikacji - przeglądarki WWW lub Java Applet Viewer. Najprostszy aplet
drukujący jedno zdanie na ekranie może mieć następującą postać:
HelloWorld.java
import java.applet.*;
import java.awt.*;
public class HelloWorld extends Applet {
public void init() {
add(new Label("Hello World!"));
}
}
Podobnie jak aplikację, kompilujemy aplet poleceniem:
javac HelloWorld.java
w wyniku czego otrzymamy plik ze skompilowaną klasą o nazwie HelloWorld.class.
Następnie tworzymy prostą stronę HTML zawierającą TAG <applet> wywołujący aplet
Java i otwieramy tę stronę przeglądarką WWW.
HelloWorld.html
<html>
<head>
<title>HelloWorld</title>
</head>
<body>
<h1>Aplet HelloWorld</h1>
<applet code=HelloWorld.class width=200 height=50>
</applet>
</body>
</html>
Wynik działania apletu możemy również obejrzeć używając przeglądarki apletów
dostarczanej przez Suna razem z całym środowiskiem JDK:
appletviewer HelloWorld.html
Każdy aplet tworzony jest jako podklasa klasy java.applet.Applet, dostarczającej
interfejs do środowiska, w obrębie którego jest on uruchamiany i znajdującej się
na dole następującej hierarchii standardowych klas Javy:
java.lang.Object
|
+----java.awt.Component
|
+----java.awt.Container
|
+----java.awt.Panel
|
+----java.applet.Applet
W cyklu życia apletu występują cztery istotne zdarzenia. Gdy one następują,
wywoływane są odpowiednie metody klasy Applet, które mogą być przesłonięte przez
metody zdefiniowane w jej podklasie, jak w poniższym przykładzie:
public class MyApplet extends Applet {
. . .
public void init() { . . . }
public void start() { . . . }
public void stop() { . . . }
public void destroy() { . . . }
. . .
}
Metody te są wywoływane przez środowisko, w którym uruchamiany jest aplet, w
momentach wystąpienia następujących zdarzeń:
init - po załadowaniu apletu (lub jego przeładowaniu) w celu jego
zainicjowania,
start - w momencie uruchamiania apletu, po jego załadowaniu lub po ponownym
odwiedzeniu strony zawierającej aplet,
stop - w momencie zatrzymywania pracy apletu, gdy opuszczana jest strona lub
następuje wyjście z przeglądarki,
destroy - przed wyjściem z przeglądarki.
Aplet nie musi przesłaniać wszystkich z tych metod, może tylko niektóre albo
żadnej. W metodzie init powinno się zamieszczać kod, który normalnie powinien
być zawarty w konstruktorze klasy. Jednak w momencie wykonywania konstruktora
klasy apletu nie ma gwarancji, że jest już zdefiniowane całe jego środowisko.
Dlatego takie zadania jak na przykład jednokrotne załadowanie obrazów
wykorzystywanych przez aplet powinny być zawarte właśnie w metodzie init. W
metodach start i stop można na przykład odpowiednio uruchamiać i zatrzymywać
animację tak, żeby w czasie gdy użytkownik nie ogląda strony z apletem
niepotrzebnie nie zużywać zasobów komputera.
Aplet dziedziczy metody interfejsu użytkownika z nadrzędnych klas AWT (Abstract
Window Toolkit) Component, Container i Panel.
Z klasy Component dziedziczy metody paint i update, które odpowiadają za
graficzną reprezentację apletu na stronie przeglądarki. Metody te aplet może
przesłonić:
class MyApplet extends Applet {
. . .
public void paint(Graphics g) { . . . }
. . .
}
Z klasy Component aplet dziedziczy również metody obsługi zdarzeń, zarówno
dotyczące konkretnych wydarzeń, takie jak action czy mouseDown, jak i ogólną
metodę handleEvent przechwytującą wszystkie zdarzenia.
Aplet jest podklasą klasy Container, może więc zawierać inne obiekty klasy
Component, takie jak klawisze, etykiety, listy wyboru itd. Jak w innych
obiektach klasy Container wzajemne rozłożenie komponentów kontrolowane jest
poprzez klasę LayoutManager.
Aplety są uruchamiane w środowisku przeglądarki, która narzuca im pewne
ograniczenia związane z zachowaniem bezpieczeństwa. Różnią się one nieco
pomiędzy różnymi przeglądarkami. Aplet ściągnięty do przeglądarki poprzez sieć:
-
nie może czytać i pisać do plików znajdujących się na komputerze, który go
wykonuje,
-
nie może tworzyć połączeń sieciowych do komputerów innych niż ten, z
którego został ściągnięty,
-
nie może uruchamiać żadnych programów na komputerze, który go wykonuje,
-
może czytać tylko wybrane parametry systemowe,
-
nie może ładować bibliotek ani definiować metod w kodzie maszynowym.
Ograniczeniem (choć nie związanym z bezpieczeństwem) jest również graficzny
interfejs użytkownika apletu. Aplet może przedstawiać swoją reprezentację
graficzną tylko w obrębie prostokąta na stronie WWW o z góry zadanych wymiarach,
bądź w postaci generowanych okienek, które różnią się od tych generowanych przez
aplikacje.
Pliki składające się na aplet: klasy, pliki z grafikami, dźwiękami i inne
pliki pomocnicze mogą być połączone w jeden plik o formacie Java Archive (JAR),
co pozwala uzyskać wiele korzyści:
-
bezpieczeństwo - zawartość pliku JAR można podpisać cyfrowo; dzięki temu
mogą być złagodzone niektóre z wcześniej wymienionych ograniczeń związane z
dostępem do lokalnych zasobów dyskowych oraz z pełnym dostępem do sieci,
-
skrócenie czasu ładowania - cały aplet może ściągnięty przez przeglądarkę
w jednej transakcji HTTP bez konieczności otwierania nowego połączenia dla
każdego pliku,
-
kompresja - pliki są kompresowane zgodnie z formatem ZIP.
Do obsługi plików JAR służy standardowe narzędzie jar zawarte w pakiecie JDK.
Sposób jego użycia jest analogiczny do programu tar z systemu Unix.
|
Operacja |
Polecenie |
|
Tworzenie pliku JAR |
jar cf jar-file input-file(s) |
|
Oglądanie zawartości pliku JAR |
jar tf jar-file |
|
Rozpakowywanie pliku JAR |
jar xf jar-file |
|
Uruchamianie apletu spakowanego w postaci pliku JAR |
<applet code=AppletClassName.class archive="JarFileName.jar" width=width
height=height></applet> |
Uwaga. Internet Explorer używa własnego standardu kompresji - CAB.
Netscape Communicator obsługuje format JAR..
Tag <applet> na stronie HTML może więcej parametrów niż podane wcześniej w
prostym przykładzie. Bardziej złożone wywołanie może mieć następującą postać:
<applet code=AppletSubclass.class codebase="directory/" width=anInt
height=anInt align=left>
<param name=parameter1Name value=aValue>
<param name=parameter2Name value=anotherValue>
Wymagana jest przeglądarka obsługująca aplety Javy!
</applet>
Domyślnie przeglądarka szuka klas apletu oraz plików skompresowanych w tym
samym katalogu, z którego został ściągnięty plik HTML zawierający tag <applet>.
Można jednak wskazać inne miejsce podając parametr codebase. Wartością parametru
może być ścieżka względna wobec strony HTML, ścieżka bezwzględna lub w ogólności
dowolny URL. Klasy apletu mogą więc być ściągane z innego serwera niż strona
HTML. Opcje width i height określają rozmiary prostokąta na stronie
przeglądarki, w obrębie którego wizualizowany jest aplet. Opcja align określa
położenie apletu, podobnie jak dla tagu <img>: left, right, top, texttop, middle,
absmiddle, baseline, bottom, absbottom. Do apletu można przekazywać parametry
definiując tagi <param> w obrębie tagu <applet>. Wartości tych parametrów można
następnie odczytywać w kodzie apletu wywołując metodę klasy Applet
public String getParameter(String name)
Opcjonalny tekst w obrębie tagu <applet> jest pisany w okienku przeglądarki, gdy
ta nie potrafi obsługiwać Javy lub obsługa została wyłączona.
Nazwy standardowych pakietów Javy rozpoczynają się od prefiksu java. W skład
JDK 1.1 wchodzą następujące pakiety: java.applet, java.awt, java.beans, java.io,
java.lang, java.math, java.net, java.rmi, java.security, java.sql, java.text,
java.util. Zostaną one omówione pokrótce, najpierw te najczęściej
wykorzystywane.
W pakiecie java.lang zdefiniowana jest klasa Object, która jest klasą
nadrzędną wobec wszystkich innych klas Javy.
Pakiet zawiera także klasy opakowujące (kopertowe) zmienne podstawowych typów
języka Java takie jak Boolean, Byte, Integer, Long, Double, Character itd.,
definiując użyteczne metody operujące na zmiennych tych typów, metody konwersji
pomiędzy typami, zamianę na łańcuchy znaków i inne.
Klasa Math zawiera metody wykonujące podstawowe operacje numeryczne takie jak
funkcje eksponencjalne, logarytmiczne, trygonometryczne.
W pakiecie java.lang zawarte są dwie klasy obsługujące łańcuchy znaków: String i
StringBuffer. Klasa String używana jest do przechowywania i wykonywania operacji
na stałych łańcuchach; po utworzeniu obiektu tej klasy nie można zmienić jego
wartości. Klasa ta zawiera metody do sprawdzania poszczególnych znaków,
porównywania łańcuchów, wyodrębniania podłańcuchów, tworzenia kopii z zamianą na
małe albo duże litery. Klasa StringBuffer implementuje łańcuchy znaków, które
mogą być zmieniane. Podstawowe metody tej klasy to append dodająca znaki na
końcu bufora i insert wstawiająca znaki w określonym miejscu. Każdy obiekt
przydziela bufor na przechowywany łańcuch znaków. Jeżeli całkowita długość
łańcucha wzrośnie powyżej rozmiaru bufora, automatycznie przydzielany jest
większy.
Dostęp do zasobów systemowych można uzyskać poprzez niezależne od systemu API
zdefiniowane w finalnej klasie System oraz zależne od systemu API zawarte w
Runtime. Można uzyskać dostęp do parametrów systemowych, standardowych
strumieni: wyjściowego, wejściowego i błędów, metod ładowania bibliotek, odczytu
czasu komputera, szybkiego kopiowania tablic itd.
Klasa Thread definiuje wątki programu Javy pozwalające na jego współbieżną
pracę.
Pakiet java.io definiuje klasy implementujące operacje wejścia-wyjścia. W
pakiecie tym (jak również i w innych) występują klasy i metody pochodzące z JDK
1.0 równolegle z nowymi klasami wprowadzonymi wraz z wejściem JDK 1.1.
Pozostawiono stare klasy w celu zapewnienia zgodności dla dawniej pisanych
programów. Często przestarzałe (deprecated) konstrukcje są odradzane i
proponowane są ich udoskonalone odpowiedniki.
Podstawowymi klasami pozwalającymi czytać i pisać z plików, łańcuchów znaków i
innych strumieni, są:
Reader wraz z podklasami BufferedReader, CharArrayReader,
InputStreamReader, FileReader, StringReader oraz Writer wraz z podklasami
BufferedWriter, CharArrayWriter, OutputStreamWriter, FileWriter, PrintWriter,
StringWriter. Ich starsze odpowiedniki to InputStream i OutputStream wraz z ich
podklasami.
Strumień wejściowy znaków może być dzielony na jednostki logiczne - tokeny,
poprzez skonstruowanie na nim obiektu klasy StreamTokenizer. Można zdefiniować
składnię, według której wyróżniane będą tokeny typu słowo, liczba, znaczniki
końca linii i pliku oraz pomijane komentarze.
Zdefiniowane są również klasy wspierające obsługę struktury drzewa plików w
systemie - File oraz czytanie i pisanie do plików o dostępie swobodnym -
RandomAccessFile.
W pakiecie java.util można znaleźć m.in. szereg klas definiujących różne
struktury danych przechowujące inne obiekty. Klasa Vector implementuje tablicę
obiektów, która może rosnąć lub zmniejszać się w miarę jak obiekty są dodawane
lub usuwane. Tak jak w zwykłej tablicy dostęp do jej elementów może odbywać się
poprzez całkowity indeks, można również szukać wystąpienia obiektu w wektorze,
którego wartość jest równa podanemu. Wszystkie elementy wektora najwygodniej
jest przeglądać wykorzystując interfejs Enumeration:
Vector v;
for(Enumeration e = v.elements(); e.hasMoreElements() ;)
System.out.println(e.nextElement());
Każdy wektor stara się optymalizować zarządzanie rozmiarem zajmowanej
pamięci. Gdy wektor zajmuje całą przydzieloną pamięć, przed dodaniem kolejnego
elementu jego rozmiar jest automatycznie zwiększany o wartość capacityIncrement.
Program może jednak sam zwiększyć rozmiar wektora przed wstawieniem dużej porcji
danych, aby uniknąć wielu realokacji. Podklasą klasy Vector jest Stack
realizujący kolejkę LIFO obiektów z metodami push i pop.
Klasa Dictionary jest abstrakcyjnym rodzicem dowolnej klasy implementującej
odwzorowanie kluczy na wartości. Jej podklasą jest Hashtable, której konstrukor
posiada dwa parametry initialCapacity i loadFactor. Gdy liczba elementów w
tablicy mieszającej (hash table) osiąga wartość iloczynu tych parametrów,
rozmiar tablicy jest automatycznie zwiększany i przeliczane są pozycje
elementów. Większy współczynnik wypełnienia pozwala oszczędniej gospodarować
pamięcią kosztem dłuższego czasu potrzebnego do wyszukiwania elementów.
Efektywniej jest również zawczasu przydzielić odpowiedniej wielkości tablicę,
niż potem zdać się na automatyczne przeładowywania. Poniżej przedstawiono
przykład tablicy mieszającej obiektów typu Integer posiadających nazwy jako
klucze:
Hashtable numbers = new Hashtable();
numbers.put("one", new Integer(1));
numbers.put("two", new Integer(2));
numbers.put("three", new Integer(3));
Aby wydobyć element z tablicy można posłużyć się następującym kodem:
Integer n = (Integer)numbers.get("two");
if(n != null)
System.out.println("two = " + n);
Obiekty przechowywane w tablicy mieszającej muszą implementować metody
hashCode i equals dziedziczone z klasy java.lang.Object.
Przy użyciu klasy BitSet można tworzyć zbiory bitów i wykonywać na nich operacje
logiczne.
Klasa StringTokenizer pozwalająca dzielić łańcuchy znaków na tokeny jest
znacznie prostsza w użyciu niż klasa java.io.StreamTokenizer. Określa się w niej
tylko jakie znaki rozdzielają kolejne tokeny, domyślnie są to znaki z łańcucha "
\t\n\r", na przykład:
StringTokenizer st = new StringTokenizer("To jest tylko test");
while (st.hasMoreTokens())
System.out.println(st.nextToken());
Można również znaleźć klasy obsługujące daty - Date i Calendar, ustawiające
parametry specyficzne dla kraju, regionu lub języka - Locale, TimeZone.
W pakiecie java.util.zip znajdują się klasy pozwalające tworzyć i czytać pliki
skompresowane w formatach ZIP i GZIP.
Pakiet java.net zawiera klasy realizujące połączenia sieciowe zarówno na
poziomie gniazd, jak i adresów URL wskazujących zasoby w WWW. Podstawowe klasy
to Socket, URL, URLConection. Poniższy przykład pokazuje jak z apletu można w
prosty sposób przejść do wybranej strony WWW:
try {
URL task = new URL("http://www.task.gda.pl/");
getAppletContext().showDocument(task);
} catch (MalformedURLException e) {} // new URL() failed
Aplety i aplikacje Javy komunikują się z użytkownikiem wykorzystując klasy z
pakietu java.awt składające się na graficzny interfejs użytkownika AWT (Abstract
Window Toolkit). AWT dostarcza typowe komponenty graficzne takie, jak klawisze,
pola do wprowadzania tekstu, listy wyboru itd. poprzez klasy Button, Checkbox,
Choice, Label, List, Menu, Scrollbar, TextArea, TextField będące pochodnymi
klasy Component. Wykorzystując klasę Canvas, można rysować dowolne obrazy
graficzne na ekranie, a po dodaniu odpowiedniej obsługi zdarzeń można
zdefiniować dowolny własny komponent.
Wraz z JDK 1.1 został wprowadzony nowy model obsługi zdarzeń, który jest
znacznie bardziej elastyczny i wydajny w porównaniu z modelem z JDK 1.0. Stary
model dla zachowania zgodności jest również obsługiwany, choć jego użycie jest
odradzane. W modelu 1.1 AWT zdarzenia są generowane przez źródła, którymi mogą
być komponenty interfejsu użytkownika, myszka, klawiatura itd. Może być
wydelegowany jeden lub więcej odbiorców zdarzenia pochodzącego od określonego
źródła, który jest obiektem dowolnej klasy implementującej przynajmniej jeden z
interfejsów obsługi zdarzeń takich, jak ActionListener, KeyListener czy
MouseListener. Zasadę działania tego modelu można prześledzić w poniższym
przykładzie:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class URLButton extends Applet implements ActionListener {
public void init() {
Button button = new Button("TASK Home");
add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent event) {
. . .
}
}
Zastosowanie klas Applet i AppletContext z pakietu java.applet było już
demonstrowane we wcześniejszych przykładach. Definiują one środowisko, w którym
uruchamiane są aplety. Pozwalają również na komunikację pomiędzy apletami
znajdującymi się na tej samej stronie.
Środowisko budowania niezależnych od platformy, modyfikowalnych wizualnych
komponentów JavaBeans API zawarte jest w pakiecie java.beans. Wykorzystując
przeznaczone do tego narzędzia programistyczne, można wizualnie z komponentów
konstruować aplikacje, aplety, servlety lub komponenty złożone. Narzędzie tego
typu pozwala wybrać z zestawu potrzebny nam komponent, wstawić go do okienka
programu, zmodyfikować jego wygląd i zachowanie, zdefiniować interakcję z innymi
komponentami; wszystko to nie pisząc ani jednej linii kodu.
JDBC API (Java DataBase Conectivity) zdefiniowane w pakiecie java.sql
wprowadza jednolity standard dostępu do dowolnych relacyjnych baz danych. Klasy
z tego pakietu implementują połączenia z bazą danych, zapytania SQL, wyniki tych
zapytań itp. Dostęp do bazy odbywa się za pośrednictwem drivera, który może być
napisany całkowicie w Javie i wtedy może być np. ściągnięty jako część apletu,
może być też napisany z wykorzystaniem kodu maszynowego określonej platformy,
aby uzyskać dostęp do istniejących już bibliotek dostępu do baz danych. Drivery
JDBC można podzielić na cztery kategorie:
-
Pomost JDBC-ODBC, który zapewnia dostęp do bazy poprzez binarne drivery ODBC.
-
Driver w Javie zamieniający odwołania JDBC na odwołania do binarnego drivera
dostępowego konkretnej bazy danych znajdującego się na lokalnym komputerze.
-
Całkowicie w Javie napisany driver komunikujący się poprzez sieć z
oprogramowaniem pośredniczącym w dostępie do bazy (middleware). Protokół
dostępowy zależy od producenta tego oprogramowania, producent ten jest też
najczęściej dostawcą odpowiedniego drivera JDBC.
-
Całkowicie w Javie napisany driver komunikujący się poprzez sieć bezpośrednio
z serwerem bazy danych. Dostawcą drivera często jest producent bazy danych.
RMI (Remote Method Invocation) zawarty w java.rmi umożliwia tworzenie
rozproszonych aplikacji w Javie. Aplikacja taka złożona jest z serwera, który
tworzy obiekty posiadające metody mogące być wywoływane zdalnie, udostępnia
odnośniki do tak zdefiniowanych obiektów i czeka na wywołania tych metod przez
klientów; klient ściąga odnośniki do zdalnych obiektów i wywołuje ich metody.
RMI zapewnia mechanizm, poprzez który odbywa się komunikacja pomiędzy serwerem i
klientem oraz przesyłane są dane w obie strony.
W pakiecie java.math znajdują się klasy BigInteger i BigDecimal pozwalające
na tworzenie i operacje na liczbach całkowitych dowolnej precyzji. BigInteger
implementuje operacje arytmetyki modularnej, obliczanie największego wspólnego
podzielnika, generator liczb pierwszych, operacje na bitach itd. Z liczb
zdefiniowanych w tej klasie korzysta pakiet java.security wprowadzający
jednolity model kryptografii i ochrony danych. Znajdują się tam abstrakcyjne
klasy definiujące kody zapewniające integralność danych (message digests) oparte
na jednokierunkowych funkcjach mieszających (one-way hash functions), podpisy
cyfrowe oparte na parze kluczy jawnego i tajnego, zarządzanie kluczami i
certyfikaty. Jest to tylko ramowy szkielet koncepcji kryptograficznych, pod
który producenci oprogramowania mogą podłączać konkretne algorytmy i metody.
Razem z JDK Sun dostarcza jedynie klasy do cyfrowego podpisywania dokumentów
opartego na algorytmie DSA oraz obliczania kodów dokumentów metodami MD5 i
SHA-1.
W pakiecie java.text znajdziemy klasy obsługujące różne formaty danych
tekstowych, np. DateFormat obsługujący daty w różnych standardach czy
NumberFormat tworzący i analizujący różne formaty liczb.
Servlety są modułami, które są uruchamiane wewnątrz serwerów przetwarzających
zapytania i generujących odpowiedzi, takich jak np. rozszerzone o obsługę Javy
serwery WWW. Rozszerzają one funkcjonalność tych serwerów. Można w uproszczeniu
powiedzieć, że servlety dla serwerów są tym, czym aplety dla przeglądarek.
Servlety stanowią alternatywę dla skryptów CGI, umożliwiając łatwą metodę
dynamicznego tworzenia dokumentów HTML. Są one łatwiejsze do pisania dzięki
wykorzystaniu środowiska Javy. Są również szybciej wykonywane, gdyż wywołanie
servletu odbywa się nie poprzez uruchomienie nowego procesu, co jest kosztowne
ze względu na czas procesora i zasoby pamięciowe, lecz jako wątek. Co więcej,
kod wykonywalny dla servletu jest ładowany do serwera tylko raz, gdy po raz
pierwszy żądana jest usługa oferowana przez dany servlet lub automatycznie, gdy
zostanie zmieniony kod servleta. Potem servlet pozostaje w pamięci serwera i
może równolegle obsługiwać wiele zapytań z możliwością komunikacji pomiędzy
nimi.
Klasy implementujące funkcje servletów znajdują się w pakiecie javax.servlet
dostępnego z firmy Sun jako Java Servlet Development Kit. Zawierają one
abstrakcyjną klasę GenericServlet oraz interfejs Servlet, które definiują
podstawowe własności ogólnych servletów, czyli modułów przetwarzających
zapytania i generujących odpowiedzi. Jednak najczęściej korzysta się z podklas
znajdujących się w pakiecie javax.servlet.http definiujących servlety HTTP.
Klasa HttpServlet posiada m.in. takie metody jak doGet i doPost obsługujące
typowe zapytania CGI, czy uniwersalną metodę service obsługującą wszystkie
rodzaje zapytań. Najprostszy servlet może wyglądać następująco:
HelloWorldServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorldServlet extends HttpServlet {
public void doGet (HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
ServletOutputStream out = res.getOutputStream();
out.println("<html>");
out.println("<head><title>Hello World</title></head>");
out.println("<body>");
out.println("<h1>Hello World</h1>");
out.println("</body></html>");
}
}
Większość użytecznych servletów czyta parametry wejściowe przekazane im z
przeglądarki poprzez metody GET lub POST i na ich podstawie tworzy odpowiedni
dokument HTML. Prosty przykład takiego servletu przedstawiono poniżej.
Hello.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class Hello extends HttpServlet {
public void service(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter toClient = res.getWriter();
toClient.println("<html><head>");
toClient.println("<title>Hello</title>");
toClient.println("</head>");
toClient.println("<h1>Hello " +
req.getParameterValues("name")[0] + "!</h1>");
toClient.println("</body></html>");
toClient.close();
}
}
Powyższy servlet można wywołać na kilka sposobów. Można podać w przeglądarce
URL
http://www.task.gda.pl/servlet/Hello?name=Darek
i wywołać servlet przekazując parametry metodą GET. Można na stronie HTML
umieścić formularz:
<form action=http://www.task.gda.pl/servlet/Hello method=POST>
Podaj swoje imię: <input type=text name=name>
<input type=submit value=Wyślij>
</form>
i wywołać servlet przekazując parametry metodą POST. Można również
wykorzystać tag <servlet> na stronie HTML zapisanej w pliku z rozszerzeniem
shtml.
Hello.shtml
...
<servlet code=Hello>
<param name=name value=Darek>
</servlet>
...
Wtedy serwer WWW rozszerzony o funkcjonalność servletów zastąpi na stronie
HTML powyższy tag wynikiem generowanym przez servlet Hello i tak przetworzoną
stronę prześle do przeglądarki.
Dostępnych jest już wiele serwerów WWW obsługujących servlety Javy. Przykładami
mogą być Java Web Server firmy Sun lub darmowy dla celów niekomercyjnych JRun
firmy Live Software. Ten ostatni może pracować jako samodzielny serwer WWW, bądź
jako moduł rozszerzający funkcjonalność innych znanych serwerów WWW np. Apache,
Microsoft IIS, Netscape.
Zalety języka Java:
-
"ożywienie" WWW - swego rodzaju "lingua franca" Internetu
-
język pozwala łatwo pisać aplikacje sieciowe
-
przenośność - niezależność od platformy dzięki JVM; Java poprzez swój
B-kod staje się najbliższą aproksymacją (zbudowaną raczaej z software niż z
hardware, chociaż anonsowane są już "Java chips") jednego z najstarszych
marzeń przemysłu komputerowego: prawdziwie uniwersalna maszyna wirtualna
-
prawie czysta implementacja paradygmatu obiektowego
-
usunięcie arbitralnego pojęcia wskaźnika jak w C++
-
wsparcie dla zbierania nieużytków
-
brak samodzielnych (tj. definiowanych poza klasami) funkcji zewnętrznych
-
weryfikacja kodu w fazie kompilacji i w fazie wykonania na zgodność ze
standardem języka
-
dynamiczne ładowanie klas (w locie)
-
biblioteki klas dla GUI, sieciowe i WWW
Wady:
-
Brak definiowanych przez użytkownika przeciążonych operatorów
-
złamanie zasad obiektowości w standardowych bibliotekach klas: w klasach
opakowań (kopertowych), w klasach kolekcji i w klasach łańcuchów
-
Komunikaty są rzadko polimorficzne i rzadko trafiają na oczekiwaną
strukturę dziedziczenia
-
brak klas parametryzowanych (genericity, templates)
-
dziedziczenie tylko pojedyncze
-
usunięcie instrukcji assert znanej w C i C++
-
pogmatwana (zawikłana) struktura modularna z trzema wpływającymi wzajemnie
na siebie (interagującymi) koncepcjami (klasy, pakiety zagnieżdżone, pliki
źródłowe)
Plik Squares.java
class Squares {
/** Print out the squares of integers from 1 to 10 */
public static void main(String[] args) {
int x = 1;
System.out.println("Squares of integers from 1 to 10:");
while (x <= 10) {
System.out.println(x * x); // print x squared
x = x + 1; // add 1 to x
}
}
}
Plik StringDemo.java
class StringsDemo {
static public void main(String[] args) {
String myName = "Archibald";
myName = myName + " Tuttle";
System.out.println("Name = " + myName);
}
}
Plik PascalTriangle.java
import java.io.*;
public class PascalTriangle {
private int data[][];
/** Create a Pascal's triangle to specified depth. */
public PascalTriangle(int rows) {
data = new int[rows][];
for (int row = 0; row < rows; row++) {
data[row] = new int[row + 1];
if (row == 0)
data[row][0] = 1;
else
for (int col = 0; col <= row; col++) {
data[row][col] = 0;
// if not on right edge, add node up and right
if (col < row)
data[row][col] += data[row - 1][col];
// if not on left edge, add node up and left
if (col > 0)
data[row][col] += data[row - 1][col - 1];
}
}
}
/** Print this Pascal's triangle to given stream. */
public void print(PrintStream ps) {
for (int i = 0; i < data.length; i++) {
int[] row = data[i];
for (int j = 0; j < row.length; j++)
ps.print(row[j] + " ");
ps.println();
}
}
/** Create a Pascal's triangle of depth 12 and print it. */
public static void main(String[] args)
{
PascalTriangle pt = new PascalTriangle(12);
pt.print(System.out);
}
}
Plik EasyIn.java
// Simple input from the keyboard for all primitive types. ver 1.0
// Not thread safe, not high performance, and doesn't tell EOF.
// It's intended for low-volume easy keyboard input.
// An example of use is:
// EasyIn easy = new EasyIn();
// int i = easy.readInt(); // reads an int from System.in
// float f = easy.readFloat(); // reads a float from System.in
import java.io.*;
import java.util.*;
class EasyIn {
static InputStreamReader is = new InputStreamReader( System.in );
static BufferedReader br = new BufferedReader( is );
StringTokenizer st;
StringTokenizer getToken() throws IOException {
String s = br.readLine();
return new StringTokenizer(s);
}
boolean readBoolean() {
try {
st = getToken();
return new Boolean(st.nextToken()).booleanValue();
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readBoolean");
return false;
}
}
byte readByte() {
try {
st = getToken();
return Byte.parseByte(st.nextToken());
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readByte");
return 0;
}
}
short readShort() {
try {
st = getToken();
return Short.parseShort(st.nextToken());
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readShort");
return 0;
}
}
int readInt() {
try {
st = getToken();
return Integer.parseInt(st.nextToken());
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readInt");
return 0;
}
}
long readLong() {
try {
st = getToken();
return Long.parseLong(st.nextToken());
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readLong");
return 0L;
}
}
float readFloat() {
try {
st = getToken();
return new Float(st.nextToken()).floatValue();
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readFloat");
return 0.0F;
}
}
double readDouble() {
try {
st = getToken();
return new Double(st.nextToken()).doubleValue();
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readDouble");
return 0.0;
}
}
char readChar() {
try {
String s = br.readLine();
return s.charAt(0);
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readChar");
return 0;
}
}
String readString() {
try {
return br.readLine();
} catch (IOException ioe) {
System.err.println("IO Exception in EasyIn.readString");
return "";
}
}
// This method is just here to test the class
public static void main (String args[]){
EasyIn easy = new EasyIn();
System.out.print("enter char: "); System.out.flush();
System.out.println("You entered: " + easy.readChar() );
System.out.print("enter String: "); System.out.flush();
System.out.println("You entered: " + easy.readString() );
System.out.print("enter boolean: "); System.out.flush();
System.out.println("You entered: " + easy.readBoolean() );
System.out.print("enter byte: "); System.out.flush();
System.out.println("You entered: " + easy.readByte() );
System.out.print("enter short: "); System.out.flush();
System.out.println("You entered: " + easy.readShort() );
System.out.print("enter int: "); System.out.flush();
System.out.println("You entered: " + easy.readInt() );
System.out.print("enter long: "); System.out.flush();
System.out.println("You entered: " + easy.readLong() );
System.out.print("enter float: "); System.out.flush();
System.out.println("You entered: " + easy.readFloat() );
System.out.print("enter double: "); System.out.flush();
System.out.println("You entered: " + easy.readDouble() );
}
}
Plik Heritage.java
/**Program krotko demonstruje mechanizm korzystania z interfejsow*/
/**Glowna klasa programu, tworzy pozostale*/
public class Heritage
{
public static void main (String[] args) {
/*tworzymy obiekty i wywolujemy odpowiednie metody*/
Client our_client = new Client();
our_client.startingReport();
Server our_server= new Server (our_client);
our_server.execute(5);
}
}
/**Klasa klienta implementuje interfejs Message*/
class Client implements Message {
int counter=0;
/**zeruje licznik i wyswietla komunikat poczatkowy*/
public void startingReport()
{
counter=0;
System.out.println ("To jest raport poczatkowy klienta");
System.out.println ("Counter="+counter);
}
/**Zwieksza licznik i wyswietla jego wartosc - podajac, ze klasa
implementuje interfejs Message; zobowiazalismy sie, ze metoda o nazwie
giveReport bedzie istniala*/
public void giveReport()
{
counter++;
System.out.println ("Counter="+counter);
}
}
/**Klasa serwera korzysta z obiektu majacego zaimplementowany interfejs
Message*/
class Server
{
/*Zwrocmy uwage, ze typem obiektu jest nazwa interfejsu*/
Message object;
/**Konstuktor. Jako parametr podajemy dowolny obiekt majacy
zaimplementowany interfejs Message; w naszym przypadku bedzie to Client*/
Server (Message object)
{
this.object=object;
}
/** Wywolujemy metode giveReport obiektu z interfejsem Message times razy
*/
public void execute (int times)
{
for (int k=0; k<times; k++)
object.giveReport();
}
}
/**Interfejs Message, kazda klasa ktora go zaimplementuje musi posiadac
metode giveReport*/
interface Message
{
/**Tylko zobowiazanie, ze kazda klasa ktora implementuje ten interfejs
bedzie miala metode giveReport - jej dzialanie bedzie zalezec od danej klasy*/
void giveReport();
}
Plik Compare.java
/**Program krotko demonstruje mechanizm wyjatkow w Java*/
import java.io.*;
/**Klasa porownuje dwa pliki i zwraca rezultat porowanania na ekranie*/
public class Compare {
/**glowna i jedyna metoda aplikacji*/
public static void main (String[] args) {
/*nazwy plikow*/
String first_file = args[0];
String second_file = args[1];
/*obiekty reprezentujace pliki*/
RandomAccessFile first =null;
RandomAccessFile second =null;
/*Zawartosc plikow bedzie umieszczona w tych buforach*/
byte[] first_buffer;
byte[] second_buffer;
/*Dlugosci plikow*/
int first_size = -1;
int second_size = -1;
try {
System.out.println ("Ten program porowna dwa pliki");
System.out.println ("plik 1: "+first_file+" plik2: "+second_file);
try {
first = new RandomAccessFile(first_file, "r");
second = new RandomAccessFile(second_file, "r");
/*blok, w ktorym porownujemy*/
prawie_koniec:{ //etykieta
first_size = (int)first.length();
second_size = (int)second.length();
/*najpierw porownujemy dlugosci*/
if (first_size!=second_size)
{
System.out.println ("Rozne pliki");
break prawie_koniec;
}
/*jesli dlugosci identyczne, wczytujemy pliki do pamieci*/
first_buffer = new byte [first_size];
second_buffer = new byte [second_size];
first.readFully (first_buffer, 0, first_size);
second.readFully (second_buffer, 0, second_size);
/*porownujemy zawartosc plikow*/
for (int i=0; i<first_size; i++)
{
if (first_buffer[i]!=second_buffer[i])
{
System.out.println ("Rozne pliki");
break prawie_koniec;
}
}
System.out.println ("Pliki identyczne");
}
/*tutaj przechodzi break prawie_koniec*/
System.out.println ("---------------");
}
/*przechwytujemy wyjatki zwiazane z operacjami I/O*/
catch (IOException e)
{
System.out.println ("Blad I/O");
}
/*to sie zawsze wykona*/
finally
{
try
{
first.close();
second.close();
}
/*podczas zamykania plikow ignorujemy wyjatki*/
catch (Exception ignored) { }
System.out.println ("Koniec czesci wewnetrznej");
}
}
/*tu przechwytujemy wyjatki nie obsluzone w czesci wewnetrznej*/
catch (Exception e)
{
System.out.println ("Wystapil nastepujacy wyjatek przechwycony w main");
System.out.println (e);
System.out.println ("Uzycie programu: Compare pierwszy_plik drugi_plik");
}
/*na koniec zawsze wyswietlamy krotki komunikat*/
finally
{
System.out.println ("Koniec dzialania programu");
}
} //end main
}// end Compare
Bibliografia
[1] Cargill T. An Overview of Java for C++ Programmers. C++ Report, vo. 8,
No. 2, pp. 46-49, 1996.
[2] Lorenz M. Java as an Object-Oriented Language. SIGS Books & Multimedia, New
York 1996.
[3] Martin R. C++ and Java: A Critical Comparison. C++ Report, vo. 9, No. 1, pp.
42-49, 1997.
[4] Jain P. and Schmidt D.C. Experiences Converting a C++ Communication Software
Framework to Java. C++ Report, vo. 9, No. 1, pp. 51-66, 1997.
[5] Gosling J., Joy B, Steele G. The Java Language Specification. Addison-Wesley,
1996, http://java.sun.com/docs/books/jls/.
[6] The Java Tutorial. http://java.sun.com/docs/books/tutorial/, 1998.
[7] JDK1.1.6 Documentation. http://java.sun.com/products/jdk/1.1/docs/, 1998.
[8] Servlet Tutorial. http://www.task.gda.pl/java/ServletTutorial.html, 1998.