Java


7. Wyjątki, strumienie i wątki

7.1 Wyjątki

Dobry program = program odporny na błędy.

Konflikt odporność/czytelność

Wyjątki (exceptions) - elegancki sposób sprawdzania poprawności.

Element Javy, w którym pojawi się błąd, zgłasza wyjątek (throws exception).

0x08 graphic
Zgłoszony wyjątek musi być wychwycony (catched) na poziomie zgłoszenia lub wyższym. Obsługa wychwyconego wyjątku może obejmować komunikat o błędzie i ewentualne kroki zaradcze.

Wyjątki są obiektami klasy Throwable, zawartej w podstawowym pakiecie java.lang, lub obiektami jej klas potomnych (patrz schemat).

Klasa Error odpowiada wewnętrznym błędom w Wirtualnej Maszynie Javy. Błędy tego typu są rzadkie i mają fatalne skutki.

Wyjątki z klasy Exception są kontrolowane przez kompilator, który sprawdza, czy metoda zgłasza tylko te wyjątki, które zostały wymienione w jej deklaracji (nagłówku).

Wyjątki z klasy RunTimeException wskazują na błędy, które nie zostały wykryte przez kompilator. Np. NullPointerException sygnali­zuje odwołanie do nieistniejącego obiektu.

Duża klasa IOException obejmuje sytuacje szczególne występujące podczas operacji wejścia-wyjścia. Np. NumberFormatException sygnalizuje niewłaściwą postać liczby, a EOFException - nieoczeki­wane natknięcie się na koniec pliku.

AWTException obejmuje wyjątki związane z graficzną powłoką programu (okienka).

ClassNotFoundException pojawia się wtedy, gdy użyjemy błędnej nazwy klasy, lub mamy kłopoty z dostępem do niej.

W razie potrzeby programista może definiować własne wyjątki jako podklasy klasy Exception.


Fragment hierarchii wyjątków:

0x08 graphic


Wyjątkami z klas Error i RuntimeException zajmuje się system.

Pozostałe wyjątki muszą być uwzględnione w programie w następujący sposób:

a) zgłaszanie wyjątku -

b) wychwytywanie i obsługa wyjątku -

Każda metoda może zadeklarować chęć zgłaszania wyjątku:

void mojaMetoda ( ) throws mójWyjątek {
… // kod metody

throw new mójWyjatek ( );

}

Metoda nadrzędna może przechwytywać wyjątek zgłaszany przez metodę podrzędną -

void mojaSuperMetoda ( ) {

try { mojaMetoda ( ); }

catch ( mójWyjatek mw ) {

… // kod obsługujący wyjątek

}

}

lub przekazywać go wyżej -

void mojaSuperMetoda ( ) throws mójWyjatek {

mojaMetoda ( );

}

Aplikacja Dzielenie

class Dzielenie {

static int x, y;

… // nagłówek

static int dziel_1 ( int x, int y ) {

return x / y;

}

static int dziel_2 ( int x, int y ) throws przezZero {

if ( y == 0)

throw new przezZero ( );

else

return x / y;

}

public static void main ( String [ ] args )

{

nagłówek ( );

System.out.println ( dziel_1 ( 8, 4 ) );

System.out.println ( "-------------------");

try {

System.out.println ( dziel_2 ( 8, 0 ) ); }

catch ( przezZero pz ) { }

}

}

class przezZero extends Exception {

przezZero ( ) {

System.out.println ( "Dzielenie przez zero" );

}

}

7.2 Strumienie wejścia-wyjścia

Ze względu na to, że Java ma być niezależna od sprzętu, zadbano w niej o staranne oddzielenie opisu procedur wejścia/wyjścia od ich realizacji sprzętowej. Służą temu abstrakcyjne pojęcia strumieni wejścia/wyjścia:

0x08 graphic

0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic

W tym schemacie wczytanie danych z dowolnego sekwencyjnego źródła polega na:

  1. otwarciu strumienia wejściowego;

  2. wywołaniu metody read ( ) dla tego strumienia.

Analogicznie, zapisanie danych na dowolny sekwencyjny nośnik polega na:

  1. otwarciu strumienia wyjściowego;

  2. wywołaniu metody typu write ( ) dla tego strumienia.

Strumienie we/wy zawarte są w pakiecie java.io . Ograniczymy się do omówienia elementów tego pakietu operujących znakami, ponieważ elementy operujące na bajtach są analogiczne.

7.2.1 Strumienie wejścia

Klasą bazową do definiowania strumienia wejściowego dla znaków jest Reader. Klasa ta dostarcza metodę read ( ) pozwalającą wczytać znak. Metoda read ( ) zwraca wartość typu int, więc trzeba dokonać jawnej konwersji na typ znakowy.

Zwykle zamiast ogólnej klasy Reader stosowane są jej klasy potomne przystosowane do źródeł danych i zwiększające efektywność wczytywania:

CharArrayReader

pobiera dane z tablicy znaków

FileReader

pobiera dane z pliku dyskowego

BufferedReader

zwiększa szybkość wczytywania

StringReader

pobiera dane z łańcucha znaków

itp.

Definicje strumieni można zagnieżdżać podobnie jak filtry w Unix'ie:

Reader r = new Strumień3 ( new Strumień2
( new Strumień1 ( źródło )));

Często stosowanym filtrem jest DataInputStream, który udostępnia metody bezpośredniego wczytywania podstawowych typów danych:

readInt ( )

wczytanie liczby całkowitej

readDouble ( )

wczytanie liczby rzeczywistej

readChar ( )

wczytanie znaku

readLine ( )

wczytanie wiersza

itp.

Dobrą zasadą jest jawne zamykanie strumienia po zakończeniu operacji we/wy. Do tego celu służy metoda close ( ).

Większość strumieni wejściowych udostępnia metody:

skip ( long n )

pominięcie n znaków ( zwraca liczbę faktycznie pominiętych znaków typu
long )

mark ( )

zapamiętanie aktualnej pozycji

reset ( )

powrót do zapamiętanej pozycji (lub do pozycji początkowej)

available ( )

sprawdzenie, czy są znaki do czytania
(zwraca ich liczbę)

ready ( )

sprawdzenie, czy strumień jest dostępny

Najczęściej strumienie kojarzone są z plikami dyskowymi. Pakiet java.io zawiera klasę File, która pozwala wygodnie manipulować kartotekami i plikami. Obiekty tej klasy można tworzyć na 3 sposoby:

File f = new File ( File kartoteka, String nazwa );

File f = new File ( String ścieżka, String nazwa );

File f = new File (String ścieżka );

Jak widać, obiektem typu File może być zarówno plik jak i kartoteka. Pewien kłopot stanowi brak jednolitego separatora składowych ścieżki dostępu w różnych systemach (`/' w Unix'ie, `\' w Windows). Aby temu zaradzić, obiekt klasy File korzysta ze zmiennej file.separator klasy System.

W miarę możliwości należy unikać podawania stałych ścieżek dostępu do plików, bo narażamy się w ten sposób na błędy spowodowane zmianami w systemie plików.

Obiekty klasy File udostępniają cały szereg pożytecznych metod:

exists ( )

sprawdza, czy ten obiekt istnieje

canRead ( )

sprawdza, czy wolno czytać ten plik

canWrite ( )

sprawdza, czy wolno pisać na ten plik

length ( )

zwraca długość pliku (typu long)

renameTo ( File nazwa)

zmienia nazwę

delete ( )

kasuje

getName ( )

zwraca nazwę (typu String)

getPath ( )

zwraca ścieżkę (typu String)

list ( )

zwraca listę plików (typu List )

mkdir ( )

tworzy kartotekę

Właściwym strumieniem wejścia do czytania znaków z pliku dyskowego jest FileReader ( ). Zatem operację czytania z dysku przygotowujemy w taki sposób:

File f = new File ( ”dysk\ścieżka\plik” );

Reader r = new FileReader ( f );

lub w skrócie

Reader r = new FileReader ( new File ( ”dysk\ścieżka\plik”));

Standardowym strumieniem wejścia jest System.in. Odpowiada mu klawiatura komputera.

7.2.2. Strumienie wyjścia

Klasą bazową do definiowania strumienia wyjściowego dla znaków jest Writer. Klasa ta dostarcza metodę write ( ) pozwalającą zapisać znak.

Zwykle zamiast ogólnej klasy Writer stosowane są jej klasy potomne przystosowane do źródeł danych i zwiększające efektywność wczytywania:

CharArrayWriter

zapisuje dane do tablicy znaków

FileWriter

zapisuje dane do pliku dyskowego

BufferedWriter

zwiększa szybkość zapisywania

StringWriter

zapisuje dane do łańcucha znaków

itp.

Definicje strumieni można zagnieżdżać podobnie jak filtry w Unix'ie:

Writer r = new Strumień3 ( new Strumień2
( new Strumień1 ( źródło )));

Często stosowanym filtrem jest DataOutputStream, który udostępnia metody bezpośredniego wczytywania podstawowych typów danych:

writeInt ( )

zapisanie liczby całkowitej

writeDouble ( )

zapisanie liczby rzeczywistej

writeChar ( )

zapisanie znaku

writeLine ( )

zapisanie wiersza

itp.

Właściwym strumieniem wyjścia do zapisywania znaków do pliku dyskowego jest FileWriter ( ). Zatem operację zapisywania na dysk przygotowujemy w taki sposób:

File f = new File ( ”dysk\ścieżka\plik” );

Writer r = new FileWriter ( f );

lub w skrócie

Writer r = new FileWriter ( new File ( ”dysk\ścieżka\plik”));

Standardowym strumieniem wyjścia jest System.out. Odpowiada mu okno MS-DOS w systemie Windows, lub okno terminala w systemie Unix czy Linux. Za pomocą metody print ( ) możemy wyświetlić w tym oknie znak lub napis, a metoda println ( ) kończy wyświetlanie przesunięciem do nowego wiersza.

Aplikacja We_wy

import java.io.*;

class We_wy {

static final int MAX = 10;

static final char [ ] znakiStale =

{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

static void drukujZnaki ( char [ ] znaki ) {

System.out.println ( );

for ( int i = 0; i < MAX; i++ ) {

System.out.print ( " " + znaki [ i ] );

}

System.out.println ( );

}

static void kopiujZnaki ( char [ ] oryginal, char [ ] kopia ) {

CharArrayReader r = new CharArrayReader ( oryginal );

try { for ( int i = 0; i < MAX; i++ )

kopia [ i ] = ( char ) r.read ( );

}

catch ( IOException e ) {

System.out.println ( " Blad w czytaniu znakow" );

}

}

static void kopiujCzescZnakow ( char [ ] oryginal, char [ ] kopia,
long n ) {

CharArrayReader r = new CharArrayReader ( oryginal );

long sk;

try { if ( r.skip ( n ) != n ) {

System.out.println ( " Zabraklo znakow" );

return; }

}

catch ( IOException e ) {

System.out.println ( " Blad w pomijaniu znakow" );

}

try { for ( int i = ( int ) n ; i < MAX; i++ )

kopia [ i ] = ( char ) r.read ( );

}

catch ( IOException e ) {

System.out.println ( " Blad w czytaniu znakow" );

}

}

static void powielCzescZnakow ( char [ ] oryginal, char [ ] kopia,
long n, int razy ) {

CharArrayReader r = new CharArrayReader ( oryginal );

long sk;

try { if ( r.skip ( n ) != n ) {

System.out.println ( " Zabraklo znakow " );

return;

}

r.mark ( MAX ); // argument pomijany

for ( int k = 0; k < razy; k++ ) {

for ( int i = 0; i < MAX- ( int ) n ; i++ )

kopia [ (MAX - ( int ) n ) * k + i ] =
( char ) r.read ( );

r.reset ( );

}

}

catch ( IOException e ) {

System.out.println ( " Blad w czytaniu znakow" );

}

}

public static void main ( String args [ ] ) {

char [ ] znaki = new char [MAX];

drukujZnaki ( znakiStale );

kopiujZnaki ( znakiStale, znaki );

drukujZnaki ( znaki );

char [ ] znaki1 = new char [MAX];

long n = 4;

kopiujCzescZnakow ( znakiStale, znaki1 , n );

drukujZnaki ( znaki1 );

char [ ] znaki2 = new char [MAX];

powielCzescZnakow ( znakiStale, znaki2 , 6l , 2 );

drukujZnaki ( znaki2 );

}

}

Aplikacja Plik

import java.io.*;

import java.awt.*;

class Plik {

public static void main ( String args [ ] ) {

BufferedReader we = null;

BufferedWriter wy = null;

PrintWriter wy1 = null;

File kartoteka = new File ( "." ); // bieżąca kartoteka

String [ ] pliki = new String [ 100]; // lista plików

pliki = kartoteka.list ( ); // wczytujemy zawartość
for ( int i = 0; i < 10; i++ ) // bieżącej kartoteki

System.out.println ( pliki [ i ] ); // i wyświetlamy ją

try { we = new BufferedReader // otwieramy pierwszy

( new FileReader ( pliki [ 0 ] ) ); // plik do

} // czytania

catch ( IOException e ) {

System.out.println (" Brak pliku " + pliki [ 0 ] ); }

String s = "pusty";

try { s = we.readLine ( ); // wczytujemy wiersz

System.out.println ("s = " + s ); // i wyswietlamy go

}

catch ( IOException e ) {

System.out.println (" Brak zawartosci" );

}

try { we.close ( ); } // zamykamy strumień

catch ( IOException e ) {

System.out.println (" Nie moge zamknąć " );

}

// otwieramy plik testowy

File testowy = new File ( ".", "testowy.txt" );

try { wy1 = new PrintWriter

( new FileWriter ( testowy ) );

}

catch ( IOException e ) {

System.out.println (" Nie mogę otworzyć pliku " + testowy );

}

for ( int i = 0; i < 10; i++ )

wy1.println ( "to jest test" ); // zapisujemy 10 razy

wy1.close ( ); // i zamykamy strumien

}

}

Aplikacja Wiek

import java.io.*;

class Wiek {

String imie;

int wiek;

Wiek ( String imie, int wiek ) {

this.imie = imie;

this.wiek = wiek;

}

public static String czytajLancuch ( ) {

char c;

StringBuffer b = new StringBuffer ( );

System.out.print(" > "); // znak zachęty

try {

while (( c = (char) System.in.read ( )) != '\n' )

b.append ( c );

}

catch (java.io.IOException e) { }

return b.toString ( );

}

public static int czytajLiczbeCalkowita ( ) {

String s = czytajLancuch ( );

int i;

s = s.substring ( 0,( s.length ( ) - 1 ));

try { i = Integer.parseInt ( s ); }

catch ( NumberFormatException e ) {

System.out.println ( "To nie jest liczba calkowita" );

i = 0;

}

return i;

}

static boolean sprawdzImie ( String imie ) throws
BladImienia {

char c = '0';

Character ch = new Character ( c );

for ( int i = 0; i < imie.length()-1; i++ ) {

c = imie.charAt ( i );

if ( ! ch.isLetter ( c )) throw new BladImienia ( c );

}

return true;

}

static String czytajImie ( ) {

String imie = "puste";

boolean ok = false;

while ( ! ok ) {

System.out.print( '\n' + " Imie " );

imie = czytajLancuch ( );

try { ok = sprawdzImie ( imie ); }

catch ( BladImienia e ) { }

}

return imie;

}

static boolean sprawdzWiek ( int wiek ) throws BladWieku {

if ( wiek < 0 ) throw new BladWieku ( );

else if ( wiek == 0 ) return false;

else return true;

}

int czytajWiek ( ) {

int wiek = 0;

boolean ok = false;

while ( ! ok ) {

System.out.print( '\n' + " Wiek " );

wiek = czytajLiczbeCalkowita ( );

try { ok = sprawdzWiek ( wiek ); }

catch ( BladWieku e ) { }

}

return wiek; }

void pisz ( ) {

System.out.println ( '\n' + " Dane osobowe:");

System.out.println ( " Imie - " + imie );

System.out.println ( " Wiek - " + wiek + '\n');

}

static boolean akceptuj( ) {

System.out.print ( '\n' + " Akceptujesz? ( t/n ) " );

String tn = czytajLancuch ( );

if ( tn.charAt ( 0 ) == 't' ) return true;

else return false;

}

public static void main ( String args [ ] ) {

boolean ok = false;

Wiek osoba = new Wiek ( "Jan", 25 );

while ( !ok ) {

osoba.imie = osoba.czytajImie ( );

osoba.wiek = osoba.czytajWiek ( );

osoba.pisz ( );

ok = akceptuj ( );

}

}

}

//------------------------------------------------------------

class BladImienia extends Exception {

BladImienia ( char c ) {

System.out.println("Znak " + c + " nie jest litera");

}

}

//------------------------------------------------------------

class BladWieku extends Exception {

BladWieku ( ) {

System.out.println("Wiek nie moze byc ujemny");

}

}

7.3. Tworzenie równoległych wątków

Współczesne systemy operacyjne pozwalają uruchamiać równolegle kilka zadań (multitasking), np. edytor tekstu i odtwarzacz płyty kompaktowej. Podobne działanie wewnątrz zadania nazywa się przetwarzaniem wielowątkowym (multithreading). Często stosuje się je w graficznych formach komunikacji z użytkownikiem.

0x08 graphic

0x08 graphic

Tworzenie programów wielowątkowych w Javie jest łatwe. Można to robić na dwa sposoby:

  1. rozszerzając klasę Thread;

  2. korzystając z interfejsu Runnable.

Nowy wątek tworzymy jako obiekt klasy Thread:

Thread wątek = new Thread ( );

Uruchamiamy go za pomocą metody start ( ), a zatrzymujemy - za pomocą metody stop ( ).

Uruchomiony wątek wykonuje metodę run ( ). Implementacja tej metody w klasie Thread jest pusta, należy ją zatem przesłonić własną metodą.

Wątek można usypiać za pomocą metody sleep ( long time ). Argu­men­tem tej metody jest czas uśpienia w milisekundach.

Alternatywnym sposobem uruchamiania wątków jest wykorzytanie interfejsu Runnable. Interfejs ten zawiera nagłówek jedynej metody

public void run ( );

Obiekty typu Runnable można przekazywać jako argumenty konstruktora klasy Thread:

new Thread ( Runnable object );

Utworzony w ten sposób obiekt posiada własny wątek i wykonuje metode run ( ) obiektu object.

Zastosowanie obu wersji uruchamiania wątków pokażemy na przykładzie aplikacji, która uruchamia 2 wątki: jeden z nich wyświetla co pewien czas napis "ping", drugi - napis "PONG".

Aplikacja Ping1

class Ping1 extends Thread {

String word; // jakie słowo wypisać

int delay; // jak długo czekać

static boolean stop = false; // kiedy skończyc

Ping1 ( String whatToSay, int delayTime ) {

word = whatToSay;

delay = delayTime;

}

public void run ( ) {

try {

while ( !stop ) {

System.out.print ( word + " " );

sleep ( delay ); // czekaj

}

}

catch ( InterruptedException e ) {

return; // zakończ ten wątek

}

}

public static void main ( String [ ] args ) {

new Ping1 ( "ping", 33 ).start ( );

new Ping1 ( "PONG", 100 ).start ( );

Stop st = new Stop ( );

st.start ( );

stop = st.czytajX ( );

}

}

//------------------------------------------------

import java.io.*;

class Stop extends Thread {

boolean stop = false;

public void run ( ) {

if ( stop ) return;

}

boolean czytajX ( ) {

char zn = '0';

try {

zn = (char)System.in.read ( );

}

catch ( IOException e ) { }

if ( zn == 'x' ) return true;

else return false;

}

public static void main ( String [ ] args ) {

Stop st = new Stop ( );

st.start ( );

st.stop = st.czytajX ( );

}

}

Aplikacja Ping2

class Ping2 implements Runnable {

String word; // jakie słowo wypisać

int delay; // jak długo czekać

static boolean stop = false; // kiedy skończyc

Ping2 ( String whatToSay, int delayTime ) {

word = whatToSay;

delay = delayTime;

}

public void run ( ) {

try {

Thread.sleep ( delay ); // czekaj

}

public static void main ( String [ ] args ) {

Runnable ping = new Ping2 ( "ping", 33 );

Runnable pong = new Ping2 ( "PONG", 100 );

new Thread ( ping ).start ( );

new Thread ( pong ).start ( );

Stop st = new Stop ( );

st.start ( );

stop = st.czytajX ( );

}

}

Adam Borkowski Język programowania „Java” 7-1

Adam Borkowski Język programowania „Java” 7-24

Adam Borkowski Język programowania „Java” 7-3

EOFException

NumberFormatException

RuntimeException

ClassNotFound
Exception

AWTException

IOException

Exception

Error

Throwable

• • •

• • •

• • •

strumień
wejścia

przetwa-rzanie

strumień
wyjścia

wyświetlanie
formularza

odbiór
danych

wyświetla-nie formularza

odbiór danych



Wyszukiwarka

Podobne podstrony:
Java Media FreamWork
java 2
Projekt java
JAVA tablice
Inzynieria oprogramowania w ujeciu obiektowym UML wzorce projektowe i Java iowuje
Java Przewodnik dla poczatkujacych Wydanie V javpp5
zasady grupy, java, javascript, oprogramowanie biurowe, programowanie, programowanie 2, UTK, systemy
praktyczny kurs java NX2M5DXARLEHY6NHXOS5FJHKLI6ACRLQQSDRS2Q
notatki java 08
Praktyczny kurs Java
Java praca domowa 10
java 3 id 226367 Nieznany
Java Coding rules
java object serialization speci Nieznany
java script TY2DWI33KJCAKQBCFPFEFQ736KLTSQZ3F6JQE2Q
JP SS 4 start Java
notatki java 05
java swing
notatki java 07
helion java cwiczenia zaawansow Nieznany

więcej podobnych podstron