ĆWICZENIE 11/2009
1. Wyjątki (exceptions)
Java posiada bardzo wygodny i skuteczny mechanizm obsługi błędów. Wyjątek (exception) jest to błąd, który pojawia się w trakcie wykonywania programu. W Javie wszystkie wyjątki są reprezentowane przez klasy wyjątków Exception i Error wyprowadzone z klasy Throwable. Wyjątki typu Error są związane z błędami w VMJ i są poza kontrolą programisty. Błędy realizacji programu wywodzą się z podklasy Exception. Ważną podklasą Exception jest klasa Runtime Exception, która służy do reprezentowania różnych typów błędów.
Poniższy przykład pokaże zasadę budowania metod odpowiedzialnych za obsługę błędów w programach Javy. Wykorzystamy do tego konstrukcję try-catch-finally. Blok try zawiera fragment kodu źródłowego w którym może pojawić się błąd. Definiując ten blok nie precyzujemy o obsługę jakich błędów chodzi. Określamy nim jedynie obszar wrażliwy na błędy, które chcemy obsłużyć. Jeżeli w trakcie działania programu wystąpi błąd, działanie procedur bloku try zostanie zakończone, a uruchomione zostaną procedury w odpowiadającym mu bloku catch, których może być kilka. Każdy z bloków catch odpowiedzialny jest za obsługę dokładnie jednego rodzaju wyjątków (dzielenie przez zero, niepowodzenie otwarcia pliku etc.).
Blok finally wykonywany jest zawsze czy wyjątek wystąpił, czy nie. Co więcej - jest on także wykonywany wtedy, gdy któryś z bloków catch będzie chciał zakończyć działanie danej funkcji.
void Demo()
{
try
{
// implementacja metody "Demo()"
// w tej części może ale nie musi następować kontrola wystąpienia błędów
}
catch (Exception1 w1)
{ // obsługa wyjątku w1
}
catch (Exception2 w2)
{ // obsługa wyjątku w2
}
catch (Exception3 w3)
{ // obsługa wyjątku w3
}
// ...
finally //nie musi wystąpić
{
// zakończenie działania metody;
// tu znajdują się operacje, które bezwzględnie muszą zostać wykonane (np. zamknięcie pliku)
}
}// koniec metody Demo()
Standardowa obsługa wyjątku polega na wyświetleniu nazwy wyjątku oraz zawartości stosu, co umożliwia znalezienie w programie miejsca wystąpienia błędu:
catch (Exception e)
{
System.err.println( e.getMessage());
e.printStackTrace();
}
W sytuacji wystąpienia wyjątku, którego program nie może przechwycić, Java Virtual Machine kończy działanie programu.
//Program 11.1
class Wyjatek1 {
public static void main(String args[]) {
int tab1[] = { 4, 8, 16, 32, 64, 128, 256 };
int tab2[] = { 2, 0, 8, 4, 0};
for ( int i=0; i<tab1.length; i++ ) {
try {
System.out.println(tab1[i]+"/"+tab2[i]+" jest "+ tab1[i]/tab2[i]);
}
catch( ArithmeticException w)
{
System.out.println(" Nie wolno dzielić przez zero!");
}
catch ( ArrayIndexOutOfBoundsException w)
{
// przechwycenie wyjątku
System.out.println("Indeks tablicy poza zakresem");
}
}
}
}
2. Wyjątki podklas, klas nadrzędnych
Klasą nadrzędną dla wszystkich wyjątków jest klasa Throwable. Instrukcja catch dla klasy nadrzędnej będzie wychwytywać także wszystkie wyjątki każdej podklasy. Należy zatem w pierwszych sekwencjach catch umieścić obsługę wyjątków podklasy a w następnej części obsługę klasy nadrzędnej, która przechwyci wszystkie pozostałe wyjątki.
// Program 11.2
import java.io.*;
import java.util.Vector; //klasa Vector tworzy kolekcję elementów
public class Liczby {
private Vector vector1;
private static final int size = 10;
public Liczby () { //utworzenie tablicy liczb całkowitych
vector1 = new Vector(size);
for(int i=0; i< size; i++ )
vector1.addElement( new Integer(i));
}
public void zapiszLiczby() {
PrintWriter p = null;
try {
System.out.println(" Wejscie instrukcji try");
p = new PrintWriter( new FileWriter("c:/PlikWyjsciowy.txt"));
for( int i=0;i < size; i++)
p.println("Oto liczba: "+ i + " = " + vector1.elementAt(i));
}
catch (ArrayIndexOutOfBoundsException e ) {
System.err.println(" Przechwycenie wyjatku ArrayIndexOutException: "+
e.getMessage());
}
catch ( IOException e ) {
System.err.println("Przechwycenie wyjatku IOException: "+
e.getMessage());
}
finally {
if ( p != null) {
System.out.println("Zamkniecie PrintWriter");
p.close();
}
else {
System.out.println("PrintWriter "+ "not open ");
}
}
}
public static void main (String args[]) {
Liczby licz = new Liczby();
licz.zapiszLiczby();
}
} // koniec klasy Liczby
W programie tworzony jest obiekt klasy Vector o nazwie vector1 o 10 elementach będących liczbami 0 do 9. Metoda zapiszLiczby() zdefiniowana w klasie Liczby służy do zapisywania tego ciągu do pliku PlikWyjsciowy.txt. Instrukcje System.err.println wykorzystują komunikaty generowane przez metodę getMessage() z klasy Throwable.
Aby zapisać dane do pliku należy stworzyć obiekt klasy FileWriter i skojarzyć go z plikiem. Jeśli zamienimy go w obiekt klasy PrintWriter, to będzie nadawał się do czytania tak jak każdy plik tekstowy. W celu znacznego przyspieszenia operacji zapisywania do pliku można zastosować klasę BufferedWriter.
Do samodzielnego wykonania:
1. Dokonać modyfikacji pliku w taki sposób aby spowodować powstanie wyjątków:
a) Przekroczenie zakresu tablicy.
b) IOException związany z brakiem dostępu do pliku.
3. Deklarowanie metod mogących zgłaszać wyjątki
W celu zaznaczenia, że metoda może zgłosić wyjątek (ale nie musi), należy umieścić słowo kluczowe throws za sygnaturą metody i podać za nim nazwę klasy wyjątku który może zostać obsłużony. Gdy w deklaracji metody podana zostanie nazwa nadklasy całej grupy klas wyjątków, oznacza to, że metoda może zgłosić dowolny wyjątek z klasy potomnej:
public boolean mojaMetoda ( int x, int y) throws Wyjatek1, Wyjatek2, Wyjatek3 {
.............
}
Wyjątki te będą musiały zostać obsłużone w metodzie, która bezpośrednio wywołała metodę zgłaszającą wyjątek.
Zgłaszanie wyjątków wymaga utworzenia nowego obiektu odpowiedniej klasy. Po stworzeniu obiektu za pomocą instrukcji throw można zgłosić wyjątek:
public Demo() throws DemoException
{
// ...
if (WystąpiłBłąd)
throw new DemoException("Jestem wyjątkowy wyjątek!!!");
// ...
}
Po zgłoszeniu wyjątku metoda kończy działanie, nie wykonuje żadnego kodu i nie zwraca żadnej wartości.
//Program 11.3
public class WywolajWyjatek
{
static public void main(String args[]) throws Exception
{
Liczba liczba = new Liczba();
liczba.dziel(1);
}
}
class Liczba
{
int m_i = 10;
int dziel(float i) throws Exception
{
if (i%2 != 0)
throw new Exception("Liczba nieparzysta!");
if (i == 0)
throw new Exception("Dzielenie przez zero!");
return (int)(m_i/i);
}
}
W metodzie dziel() klasy Liczba, za pomocą frazy throw new Exception("..."), generujemy wyjątek poprzez utworzenie obiektu typu Exception i przerywamy wykonanie metody. W powyższym przykładzie założono, że nieprawidłowa jest sytuacja gdy zmienna 'i' jest liczbą nieparzystą lub jest równa zeru. W obu przypadkach generowany jest wyjątek, choć z innym komentarzem. W definicji metody dziel() użyto frazy throws Exception, jej użycie informuje maszynę wirtualną Javy i metodę wołającą, że metoda może generować wyjątek typu Exception. Użycie tej frazy jest obowiązkowe. Każda metoda, która woła metodę dziel() musi mieć blok catch obsługujący wyjątek albo informację throws Exception, że może być źródłem wyjątku pochodzącego z metody, którą woła w swoim ciele. Przykładem tego jest metoda main() z klasy WywolajWyjatek, która nie ma obsługi wyjątku a tylko frazę throws Exception. Wiadomo, że wyjątek może wystąpić w programie właściwie w każdym momencie jego wykonania.
Nie jest wymagane użycie frazy throws NazwaKlasyWyjątku w nagłówku deklaracji metody dla błędów klasy RunTimeException lub jej podklas. Umieszczenie frazy throws dla tych przypadków jest jednak dobrym pomysłem, szczególnie wtedy, gdy sami generujemy jeden z powyższych wyjątków w swojej metodzie.
4. Definiowanie swoich własnych klas wyjątków
Wyjątki są oprogramowane takimi samymi klasami jak wszystkie inne klasy standardowych bibliotek Javy. Można tworzyć zatem swoje własne klasy także do obsługi wyjątków. Klasa taka powinna być klasą potomną jednej z klas wyjątków Javy. Przy tworzeniu nowej klasy wyjątku szukamy istniejącego wyjątku zbliżonego do tego jaki chcemy utworzyć. Jeżeli nie ma takiego wyjątku to tworzymy swój wyjątek jako klasę potomną klasy Exception - bazowej wszystkich jawnych wyjątków. Klasy wyjątków posiadają zazwyczaj dwa konstruktory. Pierwszy konstruktor jest bezparametrowy, natomiast drugi wymaga podania argumentu w postaci łańcucha znaków. Użycie konstruktora nadklasy super() zapewnia umieszczenie tego łańcucha w odpowiednim miejscu:
public class mojWyjatek extends Exception {
public mojWyjatek() { }
public mojWyjatek( String msg) {
super (msg);
}
}
Program 11.4 prezentuje przykład, w którym zdefiniowano klasę wyjątków NaszWyjatek, która jest podklasą klasy Exception.
//Program 11.4
class NaszWyjatek extends Exception
{
NaszWyjatek()
{ }
NaszWyjatek(String s)
{
super("\n***\n\tNic sie nie stalo to tylko: " + "NaszWyjatek\n***\n\t"+s);
}
}
//Zdefiniujemy teraz klasę Wyjatek definiującą 4 wyjątki: NaszWyjatek, operację dzielenia przez zero, odwołania do nieistniejącego obiektu, odwołania do elementu tablicy poza jej zakresem.
public class Wyjatek
{
static void pauza() throws Exception
{ System.out.println( "PAUZA");
}
public static void main(String[] args) throws Exception
{
String wyjatki[] ={"dzielenie","null","test","tablica"};
for (int i = 0; i < 4; i++)
{
try
{
wygeneruj(wyjatki[i]);
System.out.println("Wyjatek przy operacji typu:\"" + wyjatki[i] + "\" nie zostal wygenerowany");
}
catch (Exception e)
{
System.out.println("Przy operacji typu \"" + wyjatki[i] + "\" wystapil wyjatek: \n" + e.getClass() + "\n z następującą informacją: " + e.getMessage());
}
}
pauza();
}
static int wygeneruj(String s) throws NaszWyjatek
{
try
{
if (s.equals("dzielenie"))
{
int i = 0;
return i/i;
}
if (s.equals("null"))
{
s = null;
return s.length();
}
if (s.equals("test"))
{ throw new NaszWyjatek("Test sie powiodl"); }
if (s.equals("tablica"))
{
int t[] =new int[5] ;
return t[6];
}
return 0;
}
finally
{
System.out.println("\n[wygeneruj(\"" + s +"\") zakonczone]");
}
}
}
//Program 11.5
import java.awt.*;
import java.awt.event.*;
public class Zamiana extends Frame implements ActionListener
{
TextField argument;
Label wynik;
Button zamiana;
public Zamiana() // konstruktor klasy bazowej
{
super("Zamiana temperatur ver.1.0");
setSize(400, 80); // ustawienie rozmiaru okna
setLayout(new FlowLayout());
setBackground(Color.lightGray); // ustawienie koloru tła
argument = new TextField(2); // pole tekstowe - argument do zamiany
add(argument);
add(new Label("st. Celsjusza to"));
zamiana = new Button("Zamiana");
zamiana.addActionListener(this); // zdef. obiektu odbierającego zdarzenie
add(zamiana);
wynik = new Label(" st. Fahrenheita");
add(wynik);
MenuBar menuBar = new MenuBar(); // tworzenie menu
setMenuBar(menuBar);
Menu menu = new Menu("Plik");
MenuItem mi = new MenuItem("Zamknij", new MenuShortcut('1'));
menu.add(mi);
menu.addActionListener(this);
menuBar.add(menu);
setVisible(true);
// obsługa zdarzenia (zamknięcie okna)
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
// definicja metody obsługującej zdarzenie (naciśnięcie przycisku)
public void actionPerformed(ActionEvent e)
{
String label = e.getActionCommand();
if (label.equals("Zamiana"))
{
try {
int tempFahr = (int)((Double.parseDouble(argument.getText())) * 1.8 + 32);
wynik.setText(tempFahr + " st. Fahrenheita");
}
catch (NumberFormatException ev) {
System.out.println("Blad argumentow!? Wpisz poprawne wartosci!");
}
}
else if (label.equals("Zamknij"))
{
System.exit(0);
}
}
public static void main(String[] args)
{
new Zamiana();
}
}
Zadanie do samodzielnego wykonania:
2. Napisać program graficzny zawierający definicję i obsługę własnej klasy wyjątków (które nie wystąpiły w programach z ćwiczenia).
INFORMATYKA I ROK
STUDIA STACJONARNE I STOPNIA
semestr letni, r. a. 2008/2009
INFORMATYKA II - laboratorium Java
4