David Štrupl
Minulý díl našeho seriálu jsme skončili popisem tříd pro práci se souborovým systémem, konkrétně třídou File.
Druhou třídou pro práci se soubory je třída RandomAccessFile. Tato třída umožňuje manipulaci se souborem, která neprobíhá sekvenčně (jako v případě použití proudů). Objekt třídy RandomAccessFile vytvoříme podobně jako v předchozích příkladech ze jména souboru. Jako druhý argument uvedeme způsob otevření souboru -- buď hodnotu "r" při otevření pouze pro čtení nebo hodnotu "rw" pro čtení i zápis:
RandomAccessFile raf = new RandomAccessFile("mujSoubor.txt", "rw");
Po úspěšném otevření se po souboru můžeme pohybovat pomocí metod:
- public void seek(long pozice),
- public int skipBytes(int n).
Kromě těchto metod můžeme použít metody pro čtení a zápis, které jsou stejné jako ve třídách DataInputStream a DataOutputStream, např. readInt, writeInt apod.
Práce se sítí
Knihovnu pro práci se sítí najdeme v balíku java.net. Nejprve se podívejme na třídy, které slouží k reprezentaci adres na Internetu -- URL a InetAddress.
Třída InetAddress reprezentuje IP adresu počítače. Objekty třídy InetAddress nevytváříme pomocí volání new a konstruktorů, ale pomocí jedné z následujících metod:
- public static InetAddress[] getAllByName(String),
- public static InetAddress getByName(String),
- public static InetAddress getLocalHost().
Všechny tyto metody mohou způsobovat výjimku UnknownHostException, kterou musíme ošetřit, chceme-li je zavolat. Pokud se nám podaří vytvořit objekt třídy InetAddress, můžeme se ptát na IP adresu pomocí metod:
- public String getHostAddress(),
- public byte[] getAddress().
URL a URLConnection
Zajímavější třídou pracující s Internetem je třída URL. Objekty této třídy reprezentují URL a můžeme je vytvořit pomocí volání několika druhů konstruktorů. Nejjednodušší je zadat jako parametr textovou reprezentaci URL:
URL mojeURL = new URL("http://www.sun.com");
Všechny konstruktory této třídy mohou způsobovat výjimku MalformedURLException -- je proto nutné při ji vždy při vytváření nových objektů této třídy ošetřit.
Máme-li úspěšně vytvořen objekt třídy URL, můžeme si přečíst data reprezentovaná tímto URL pomocí otevření vstupního proudu z tohoto URL:
InputStream is = mojeURL.openStream();
Po otevření vstupního proudu můžeme číst data stejně jako z každého jiného vstupního proudu. Ukážeme si nyní celý příklad, který vypíše na standardní výstup data ze zadaného URL:
import java.io.*;
import java.net.*;
public class VypisURL {
public static void main(String arg[]) {
try {
URL mojeURL = new URL(arg[0]);
InputStream is=mojeURL.openStream();
int i = 0;
while (i != -1) {
i = is.read();
if (i != -1)
System.out.write(i);
}
is.close();
} catch (MalformedURLException x) {
System.out.println("Spatne URL");
} catch (IOException x) {
System.out.println("Chyba pri cteni");
}
}
}
Po přeložení tohoto příkladu jej spustíme s parametrem zadávajícím URL, jehož obsah chceme vypsat (také je dobré použít program more, aby nám výsledek "neujel"):
java VypisURL http://www.idg.cz | more
Pokud chceme i něco zapisovat na dané URL, můžeme vytvořit objekt třídy URLConnection pomocí volání metody URL:
public URLConnection openConnection() throws IOException
Objekt typu URLConnection reprezentuje spojení oběma směry, tj. můžeme použít jeho metody:
- public InputStream getInputStream() throws IOException,
- public OutputStream getOutputStream() throws IOException.
Výstupní proud můžeme použít např. k zapsání HTTP hlavičky pro náš dotaz.
Socket a ServerSocket
Druhou možností, jak pracovat se vzdáleným počítačem, je použití třídy Socket. Objekt této třídy reprezentuje jeden konec TCP spojení na vzdálený počítač. Pokud se nám podaří vytvořit Socket, můžeme opět dostat vstupní a výstupní proud spojení na vzdálený počítač. Ukážeme si to na příkladu, který opět otevře komunikaci se vzdáleným počítačem:
Socket s = new Socket("www.idg.cz", 80);
InputStream is=s.getInputStream();
OutputStream os=s.getOutputStream();
Pomocí tohoto volání jsme opět získali dva vstupní proudy -- jeden pro zápis a jeden pro čtení.
Na závěr povídání o sítích si ukážeme, jak jednoduché je v Javě napsat server, který poslouchá na nějakém portu. K tomuto účelu použijeme třídu ServerSocket. ServerSocket umožňuje velice jednoduché vytvoření serveru -- stačí si vybrat port a napsat následující:
import java.io.*;
import java.net.*;
public class STest {
public static void main(String arg[]) {
try {
ServerSocket ss = new ServerSocket(5555, 5);
while (true) {
Socket s = ss.accept();
OutputStream os = s.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("Vitam vas na serveru.");
ps.close();
}
} catch (IOException x) {
System.out.println("Chyba pri io.");
}
}
}
V konstruktoru pro ServerSocket jsme vybrali port 5555, druhý parametr znamená, kolik spojení jsme ochotni nechat čekat ve frontě. Pokud se nám podařilo tento příklad přeložit, můžeme vyzkoušet jak funguje např. pomocí Telnetu. Po spuštění našeho programu spustíme ještě Telnet a připojíme se na localhost na port 5555. Pokud vše funguje, vypíše se nám zadaný nápis a spojení se ukončí. Vzhledem k tomu, že v programu je nekonečná smyčka, musíme jej ukončit pomocí kláves CTRL-C.
Jak jsme viděli, je práce se sítí v Javě poměrně jednoduchá -- na vytvoření jednoduchého klienta i serveru nám stačí několik řádek.
Vícevláknové aplikace (Multithreading)
Význam pojmu vlákno je podobný staršímu pojmu proces. Mezi pojmy vlákno a proces je několik drobných rozdílů, kterými se zde nebudeme zabývat. Při běhu vícevláknového programu, podobně jako při vytváření více procesů, dochází k běhu několika částí programu současně.
Podpora vícevláknových (multithreaded) aplikací je obsažena v balíku java.lang. Nejdůležitější třídou pro práci s vlákny je třída Thread. Ukážeme si, jak vytvořit nové vlákno a jak toto vlákno spustíme. Pro vytvoření vlákna máme dvě možnosti: vytvořit potomka třídy Thread nebo implementovat rozhraní Runnable. Vytvoření potomka třídy Thread ukazuje následující příklad:
class MojeVlakno extends Thread {
public void run() {
for (int i=0; i<1000; i++) {
// vypocet
}
}
}
public class TestVlaken {
public static void main(String args[]) {
MojeVlakno mv1 = new MojeVlakno();
mv1.start();
// pokracovani mainu
}
}
Vytvořili jsme potomka třídy Thread, který přepisuje jedinou metodu -- metodu run. Tato metoda je zavolána, když je nové vlákno spuštěno. Spuštění zajistíme zavoláním metody start. Celý postup vytvoření vlákna je ilustrován v metodě main třídy TestVlaken: nejprve vytvoříme objekt třídy MojeVlakno a ke spuštění použijeme metodu start.
Druhý často používaný způsob vytváření vláken je použití rozhraní Runnable. Jeho použití nás zbavuje nutnosti vytvářet potomka třídy Thread. Ukážeme si použití rozhraní Runnable na příkladu appletu, který ve své metodě start vytvoří nové vlákno pro výpočet:
public class VlAplet extends java.applet.Applet implements Runnable {
public void run() {
for (int i=0; i<1000; i++) {
// vypocet
}
}
public void start() {
Thread mv1 = new Thread(this);
mv1.start();
}
}
Implementovat rozhraní Runnable znamená připsat do své třídy metodu run. Pokud to uděláme, můžeme v konstruktoru třídy Thread použít naši vlastní třídu, jako dodavatele metody run. Vytvoření nového vlákna pomocí konstruktoru s parametrem znamená, že v okamžiku spuštění tohoto vlákna se spustí metoda run objektu, který jsme zadali jako parametr. V našem příkladu je použito odkazu na náš applet pomocí klíčového slova this.
Při psaní vícevláknových aplikací je někdy třeba použít synchronizaci jednotlivých vláken. K tomuto účelu obsahuje jazyk Java klíčové slovo synchronized. Použití tohoto klíčového slova a několika synchronizačních metod obsažených ve třídě Object však necháme na nějakou podrobnější publikaci, než je tento seriál.
Další možnosti programů v Javě
Balík standardních knihoven dodávaných jako součást JDK obsahuje ještě několik zajímavých částí, které jsou důležité pro psaní rozsáhlejších aplikací v Javě. Tyto knihovny zde nebudeme podrobně rozebírat, pouze se seznámíme s tím co obsahují. Podrobnější výklad je nad rámec tohoto seriálu. Zájemce odkazuji na dokumentaci k JDK a na další publikace o Javě.
Knihovny, jejichž valná část je nová ve verzi JDK 1.1, jsou obsaženy v následujících balících:
-java.beans -- Rozhraní JavaBeans definuje formát tzv. komponent, které mohou být použity v integrovaných vývojových prostředích jako stavební kameny aplikace. Využití JavaBeans je důležité při vizuálním navrhování aplikací.
-java.math -- Tato knihovna obsahuje objekty realizující matematické výpočty v libovolné přesnosti.
-java.rmi -- rmi je zkratka slov Remote Method Invocation. Tento poměrně rozsáhlý balík umožňuje psaní distribuovaných aplikací pomocí vzdáleného vyvolávání metod.
-java.security -- Definuje rozhraní pro bezpečnost a šifrování.
-java.sql -- Jde o balík obsahující rozhraní JDBC (Java DataBase Connectivity). Slouží pro připojení Java aplikací k SQL serverům.