Programowanie obiektowe /Java/
Laboratorium nr 10
1
Pomyłka
„Podstawową ideologią Javy1 jest założenie, że ’źle sformułowany kod nie zostanie wykonany’ ”
Przykład: Istnieją dwie publiczne klasy w dwóch plikach M ain.java i T est.java: 1
package pl.kielce.tu.lab10;
2
3
public class Main {
4
public static void main(String[] args) {
5
Test t = new Test();
6
}
7
}
Przykład 1: src/pl/kielce/tu/lab10/Main.java {link}
1
package pl.kielce.tu.lab10;
2
3
public class Test {
4
public Test() {
5
}
6
}
Przykład 2: src/pl/kielce/tu/lab10/Test.java {link}
Kompilacja i uruchomienie:
javac Test.java
javac Main.java
java -cp . Main
Aplikacja działa poprawnie. Co stanie się jeśli konstruktor klasy T est uczynimy prywatnym, a następnie przez ’pomyłkę’ skompilujemy tylko klasę T est (zapominając o klasie M ain) i uruchomimy aplikację?
2
Wyjątek
„Wyjątek (exception) 1 – Specjalny byt programistyczny powodujący przerwanie lub zawieszenie normal-nej nitki sterowania programu i przejście do specjalnego fragmentu programu zajmującego się obsługą tego wyjątku. (. . . )”
„W sytuacji wyjątkowej 1 nie można kontynuować przetwarzania, ponieważ w aktualnym kontekście nie ma dostępu do informacji koniecznej do rozwiązania problemu. Wszystko, co można zrobić, to wyjść z aktualnego kontekstu i przekazać problem dalej. Tak się właśnie dzieje gdy zgłaszany jest wyjątek.”
W jaki sposób podobne problemy zostały rozwiązane w językach, w których nie ma wyjątków (np. w C)?
„Klasa Throwable opisuje wszystko co może być zgłoszone jako wyjątek. Istnieją dwa rodzaje obiektów Throwable (. . . ). Error reprezentuje błędy kompilacji i systemu (. . . ). Exception jest podstawowym typem, jaki może być wyrzucony z dowolnej metody klasy biblioteki standardowej Javy i własnej metody lub w wyniku innych błędów przy wykonaniu.”
Wyjątki dzielimy na:
- niesprawdzalne (nieweryfikowalne, niekontrolowane, ang. unchecked) – wyjątek RuntimeException oraz klasy potomne,
- sprawdzalne (weryfikowalne, kontrolowane) – inne wyjątki.
Hierarchia klas:
1Thinking in Java, Bruce Eckel, Wydanie IV, Helion, 2006
1
java.lang.Object
extended by java.lang.Throwable
extended by java.lang.Exception
java.lang.Object
extended by java.lang.Throwable
extended by java.lang.Exception
extended by java.lang.RuntimeException
java.lang.Object
extended by java.lang.Throwable
extended by java.lang.Error
Tworzenie klas własnych wyjątków:
1
package pl.kielce.tu.lab10;
2
3
public class MyException extends Exception {
4
}
Przykład 3: src/pl/kielce/tu/lab10/MyException.java {link}
1
package pl.kielce.tu.lab10;
2
3
public class MyRuntimeException extends RuntimeException {
4
}
Przykład 4: src/pl/kielce/tu/lab10/MyRuntimeException.java {link}
Blok prób (ang. try) 1 – „blok przechwytujący wyjątki”.
„Procedury obsługi wyjątku 1 następują bezpośrednio po bloku try i są oznaczane poprzez słowo kluczowe catch”
Sekcja f inally – „fragment kodu, który chcielibyśmy wykonać niezależnie od tego, czy wbloku try zostanie zgłoszony wyjątek”
Zgłaszanie wyjątków: ”(...) można zgłosić każdy rodzaj obiektu, który można wyrzucić tj. dziedziczący po klasie T hrowable” np. throw new M yException().
Przechwycenie wszystkich wyjątków:
try{
...
}
}catch(Exception e){
e.printStackTrace();
}
„W metodzie przeciążonej można zgłaszać jedynie te wyjątki, które zostały podane w specyfikacji z klasy bazowej. (. . . ) Można zaniechać wyrzucania jakichkolwiek wyjątków pomimo ich obecności w klasie bazowej.”
1
package pl.kielce.tu.lab10;
2
3
import java.io.FileNotFoundException;
4
import java.util.*;
5
6
public class TestCatch {
7
public static void main(String[] args) throws Exception {
8
Random r = new Random(new Date().getTime() >>> (new Date().getTime() % 4)); 9
try {
10
switch (r.nextInt() % 4) {
11
case 0:
12
System.out.print("0. ");
13
throw new MyException();
2
14
case 1:
15
System.out.print("1. ");
16
throw new MyRuntimeException();
17
case 2:
18
System.out.print("2. ");
19
throw new NullPointerException();
20
default:
21
System.out.print("3. ");
22
throw new FileNotFoundException();
23
}
24
} catch (MyException e) {
25
System.out.println("e.getClass().getSimpleName() = " + e.getClass().getSimpleName()); 26
} catch (FileNotFoundException e) {
27
System.out.println("e.getClass().getSimpleName() = " + e.getClass().getSimpleName()); 28
} catch (Exception e) {
29
// obsługa wszystkich pozostałych wyjątków
30
System.out.println("e.getClass().getSimpleName() = " + e.getClass().getSimpleName()); 31
System.out.println("e.getMessage() = " + e.getMessage()); 32
// zwraca szczegółową informację
33
System.out.println("e.getLocalizedMessage() = " + e.getLocalizedMessage()); 34
System.out.println("e.printStackTrace()");
35
e.printStackTrace(System.out); // drukuje komunikat oraz stos wywołań 36
System.out.println("e.getCause() = " + e.getCause()); // zwraca przyczynę 37
}
38
}
39
}
Przykład 5: src/pl/kielce/tu/lab10/TestCatch.java {link}
Co się stanie jeżeli wyjątek nie zostanie wyłapany?
Która z poniższych definicji jest poprawna (dlaczego?): 1
package pl.kielce.tu.lab10;
2
3
public class TestThrows {
4
public void a() {
5
throw new MyException();
6
}
7
8
public void b() {
9
throw new MyRuntimeException();
10
}
11
12
public void c() throws Exception {
13
throw new MyException();
14
}
15
16
public void d() throws Exception {
17
throw new MyRuntimeException();
18
}
19
20
public void e() throws RuntimeException {
21
throw new MyException();
22
}
23
24
public void f() throws RuntimeException {
25
throw new MyRuntimeException();
26
}
27
28
public void g() throws MyException {
29
throw new MyException();
30
}
3
31
32
public void h() throws MyException {
33
throw new MyRuntimeException();
34
}
35
36
public void i() throws MyRuntimeException {
37
throw new MyException();
38
}
39
40
public void j() throws MyRuntimeException {
41
throw new MyRuntimeException();
42
}
43
44
public void k() throws MyException, MyRuntimeException {
45
return;
46
}
47
}
Przykład 6: src/pl/kielce/tu/lab10/TestThrows.java {link}
Co pojawi się na ekranie?
1
package pl.kielce.tu.lab10;
2
3
import java.util.Random;
4
5
public class TestFinally {
6
static void method() throws MyException {
7
Random r = new Random();
8
if (r.nextInt() % 2 == 0) {
9
System.out.println("method() throw");
10
throw new MyException();
11
} else {
12
System.out.println("method() return");
13
return;
14
}
15
}
16
17
public static void main(String[] args) {
18
try {
19
method();
20
System.out.println("main() return");
21
return;
22
} catch (MyException e) {
23
System.out.println("main() catch " + e);
24
} finally {
25
System.out.println("main() finally");
26
}
27
}
28
}
Przykład 7: src/pl/kielce/tu/lab10/TestFinally.java {link}
3
Typowe sposoby obsługi wyjątków
Sposoby obsługi wyjątków2:
2Java. Obsługa wyjątków, usuwanie błędów i testowanie kodu, Stephen Stelting, Helion, 2005
4
3.1
Zapis błędu lub związanej z nim informacji do dziennika Informacja o błędzie może zostać wyświetlona na konsoli lub przekierowana do pliku. W bardziej zaawan-sowanych rozwiązaniach należałoby użyć JavaLoggingAP I. W przykładzie poniżej informacja o wyjątku jest wyświetlana na konsoli przy pomocy System.out, zapisywana w pliku przy pomocy przekierowanego System.err oraz wyświetlana w oknie dialogowym.
1
package pl.kielce.tu.lab10;
2
3
public class MyClass {
4
static String getLanguage() throws Exception {
5
throw new Exception("My exception");
6
}
7
}
Przykład 8: src/pl/kielce/tu/lab10/MyClass.java {link}
1
package pl.kielce.tu.lab10;
2
3
import java.io.FileNotFoundException;
4
import java.io.PrintStream;
5
import javax.swing.JOptionPane;
6
7
public class TestException1 {
8
9
static {
10
try {
11
System.setErr(new PrintStream("log.txt"));
12
} catch (FileNotFoundException ignored) {
13
ignored.printStackTrace();
14
}
15
}
16
17
public static void main(String[] args) {
18
try {
19
MyClass.getLanguage();
20
} catch (Exception e) {
21
e.printStackTrace(System.out);
22
e.printStackTrace(System.err);
23
JOptionPane.showMessageDialog(null, e);
24
}
25
}
26
}
Przykład 9: src/pl/kielce/tu/lab10/TestException1.java {link}
3.2
Zwrócenie się do użytkownika z prośba o podjecie odpowiedniej decyzji 1
package pl.kielce.tu.lab10;
2
3
import javax.swing.JOptionPane;
4
5
public class TestException2 {
6
7
public static void main(String[] args) {
8
try {
9
MyClass.getLanguage();
10
} catch (Exception e) {
11
int answer = JOptionPane.showConfirmDialog(null, "End?", "Confirm Dialog", JOptionPane.←֓
YES_NO_OPTION, JOptionPane.ERROR_MESSAGE);
5
12
if (answer == JOptionPane.YES_OPTION)
13
System.out.println("End");
14
else
15
System.out.println("Not end");
16
}
17
}
18
}
Przykład 10: src/pl/kielce/tu/lab10/TestException2.java {link}
3.3
Użycie wartości domyślnych lub alternatywnych
1
package pl.kielce.tu.lab10;
2
3
public class TestException3 {
4
5
final private static String DEFAULT_LANGUAGE = "EN"; 6
private static String language;
7
8
public static void main(String[] args) {
9
try {
10
language = MyClass.getLanguage();
11
} catch (Exception e) {
12
language = DEFAULT_LANGUAGE;
13
}
14
}
15
}
Przykład 11: src/pl/kielce/tu/lab10/TestException3.java {link}
3.4
Przekazanie sterowania do innej części aplikacji
1
package pl.kielce.tu.lab10;
2
3
public class TestException4 {
4
5
public static void main(String[] args) {
6
try {
7
MyClass.getLanguage();
8
} catch (Exception e) {
9
reset();
10
}
11
}
12
13
static void reset(){
14
//...
15
}
16
}
Przykład 12: src/pl/kielce/tu/lab10/TestException4.java {link}
3.5
Konwersja wyjątku do innej postaci
Załóżmy, że klasa powinna implementować interfejs M yInterf ace, a wewnątrz metody method() powinna wywołać M yClass.getLanguage(), która może zgłosić wyjątek. Dodanie frazy throws Exception do sygna-tury metody method() nie jest możliwe ponieważ wtedy nie będzie się ona zgadzać z sygnaturą metody w interfejsie. W takim przypadku możemy użyć wyjątku niesprawdzalnego, który jako przyczynę będzie miał
ustawiony nasz wyjątek (throw new RuntimeException(e)).
6
1
package pl.kielce.tu.lab10;
2
3
public interface MyInterface {
4
public void method();
5
}
Przykład 13: src/pl/kielce/tu/lab10/MyInterface.java {link}
1
package pl.kielce.tu.lab10;
2
3
public class TestException5 implements MyInterface {
4
5
@Override
6
public void method() { // !!! throws Exception {
7
try {
8
MyClass.getLanguage();
9
} catch (Exception e) {
10
throw new RuntimeException(e);
11
}
12
}
13
14
public static void main(String[] args) {
15
TestException5 t = new TestException5();
16
t.method();
17
}
18
}
Przykład 14: src/pl/kielce/tu/lab10/TestException5.java {link}
3.6
Zignorowanie problemu
Jest bardzo mało przypadków, kiedy można zignorować wystąpienie wyjątku dlatego najlepiej nie stosować tej techniki.
1
package pl.kielce.tu.lab10;
2
3
public class TestException6 {
4
5
public static void main(String[] args) {
6
try {
7
MyClass.getLanguage();
8
} catch (Exception ignore) {
9
}
10
}
11
}
Przykład 15: src/pl/kielce/tu/lab10/TestException6.java {link}
3.7
Powtórzenie operacji
W poniższym przypadku trzykrotnie wywoływana jest metoda getLanguage(). Pomiędzy poszczególnymi wywołaniami można zastosować odstęp czasowy (T imeU nit.SECON DS.sleep(1)). Jeżeli metoda nie zgłosi-
łaby wyjątku instrukcja break spowoduje przerwanie wykonywania pętli.
1
package pl.kielce.tu.lab10;
2
3
import java.util.concurrent.TimeUnit;
4
5
public class TestException7 {
7
6
public static void main(String[] args) {
7
int counter = 0;
8
while (counter++ < 3) {
9
try {
10
MyClass.getLanguage();
11
break;
12
} catch (Exception e) {
13
System.err.println("Exception no. " + counter); 14
e.printStackTrace();
15
try {
16
TimeUnit.SECONDS.sleep(1);
17
} catch (InterruptedException e2) {
18
e2.printStackTrace();
19
}
20
}
21
}
22
}
23
}
Przykład 16: src/pl/kielce/tu/lab10/TestException7.java {link}
3.8
Wywołanie operacji alternatywnej
1
package pl.kielce.tu.lab10;
2
3
public class TestException8 {
4
5
public static void main(String[] args) {
6
try {
7
MyClass.getLanguage();
8
} catch (Exception e) {
9
getLanguageFromFile();
10
}
11
}
12
13
static void getLanguageFromFile() {
14
// ...
15
}
16
}
Przykład 17: src/pl/kielce/tu/lab10/TestException8.java {link}
3.9
Przygotowanie aplikacji do zamknięcia
Przed zamknięciem aplikacji może wystąpić konieczność:
- zamknięcia otwartych plików,
- zamknięcia otwartych połączeń sieciowych, bazodanowych itp.,
- zapisanie danych,
- poinformowanie innych aplikacji, systemów itp. o tym fakcie.
1
package pl.kielce.tu.lab10;
2
3
public class TestException9 {
4
5
public static void main(String[] args) {
8
6
try {
7
MyClass.getLanguage();
8
} catch (Exception e) {
9
//zamknięcie otwartych plików,
10
//zamknięcie otwartych połączeń sieciowych, bazodanowych itp 11
//zapisanie danych,
12
//poinformowanie innych aplikacji, systemów itp. o tym fakcie.
13
14
//zamknięcie aplikacji
15
//jako parametr podaje się "kod statusu"
16
//wartości inne niż 0 znaczają nienormalne zamknięcie aplikacji 17
System.exit(1);
18
}
19
}
20
}
Przykład 18: src/pl/kielce/tu/lab10/TestException9.java {link}
4
RTTI
„Informacje o typie w czasie wykonania (ang. run-time type information, RTTI) pozwalają na identyfikację typów i wykorzystywanie informacji o nich w czasie działania programu.”
Obiekt
Class
zawiera
informacje
o
m.in.
konstruktach
(np.
getConstructors(),
getDeclaredConstructors()), metodach (np. getM ethods(), getDeclaredM ethods()) oraz atrybutach (polach) klasy (np. getF ields(), getDeclaredF ields()).
Szczegóły dotyczące klasy Class można znaleźć na stronie:
http://download.oracle.com/javase/6/docs/api/java/lang/Class.html
1
package pl.kielce.tu.lab10;
2
3
import java.lang.reflect.Constructor;
4
import java.lang.reflect.Field;
5
import java.lang.reflect.Method;
6
7
public class TestRTTI {
8
public int i = 1234;
9
static {
10
System.out.println("Static init");
11
}
12
13
public TestRTTI() {
14
System.out.println("Constructor TestRTTI()"); 15
}
16
17
public int getI() {
18
System.out.println("Method getI()");
19
return i;
20
}
21
22
public static void main(String[] args) throws Exception {
23
Class<?> t = null;
24
t = Class.forName("TestRTTI");
25
// Static init
26
Constructor<?>[] c = t.getConstructors();
27
System.out.println("Constructors");
28
for (int i = 0; i < c.length; i++)
29
System.out.println(c[i]);
30
// Constructors
31
// public TestRTTI()
32
9
33
TestRTTI instance = (TestRTTI) c[0].newInstance((Object[])null); 34
// Constructor TestRTTI()
35
36
Method[] m = t.getMethods();
37
System.out.println("Methods");
38
for (int i = 0; i < m.length; i++)
39
System.out.println(m[i]);
40
// Methods
41
// public static void TestRTTI.main(java.lang.String[]) ...
42
// public int TestRTTI.getI()
43
// public final void java.lang.Object.wait() ...
44
// public final void java.lang.Object.wait(long,int) ...
45
// public final native void java.lang.Object.wait(long) ...
46
// public native int java.lang.Object.hashCode()
47
// public final native java.lang.Class java.lang.Object.getClass() 48
// public boolean java.lang.Object.equals(java.lang.Object) 49
// public java.lang.String java.lang.Object.toString()
50
// public final native void java.lang.Object.notify()
51
// public final native void java.lang.Object.notifyAll() 52
// Method getI()
53
m[0].invoke(instance, (Object[]) null);
54
// Method getI()
55
Field[] f = t.getFields();
56
System.out.println("Fields");
57
for (int i = 0; i < f.length; i++)
58
System.out.println(f[i]);
59
// Fields
60
// public int TestRTTI.i
61
f[0].set(instance, 5678);
62
System.out.println(f[0].get(instance));
63
// 5678
64
}
65
}
Przykład 19: src/pl/kielce/tu/lab10/TestRTTI.java {link}
5
Operator instanceOf
Operator instanceof zwraca prawdę jeśli obiekt można rzutować na podany typ.
1
package pl.kielce.tu.lab10;
2
3
import java.util.Random;
4
5
public class TestInstanceOf {
6
7
static Random r = new Random();
8
9
public static Object makeNew() {
10
switch (r.nextInt() % 2) {
11
case 0:
12
return new String("TEST");
13
default:
14
return new Integer(123);
15
}
16
}
17
18
public static void main(String[] args) throws Exception {
19
for (int j = 0; j < 10; j++) {
20
Object o = makeNew();
10
21
if (o instanceof String) {
22
String s = (String) o;
23
System.out.println("String == " + o.getClass().getSimpleName() + " " + s.←֓
toLowerCase());
24
} else if (o instanceof Integer) {
25
Integer i = (Integer) o;
26
System.out.println("Integer == " + o.getClass().getSimpleName() + " " + i.←֓
floatValue());
27
} else {
28
throw new RuntimeException("Object of unknown class"); 29
}
30
}
31
}
32
}
Przykład 20: src/pl/kielce/tu/lab10/TestInstanceOf.java {link}
6
Metoda isInstance()
„Metoda isInstance() klasy Class pozwala na dynamiczne testowanie typu obiektu.”
1
package pl.kielce.tu.lab10;
2
3
import java.util.Random;
4
5
public class TestIsInstance {
6
7
static Random r = new Random();
8
9
public static Object makeNew() {
10
switch (r.nextInt() % 2) {
11
case 0:
12
return new String("TEST");
13
default:
14
return new Integer(123);
15
}
16
}
17
18
public static void main(String[] args) throws Exception {
19
for (int j = 0; j < 10; j++) {
20
Object o = makeNew();
21
if (String.class.isInstance(o)) {
22
String s = (String) o;
23
System.out.println("String == " + o.getClass().getSimpleName() + " " + s.←֓
toLowerCase());
24
} else if (Integer.class.isInstance(o)) {
25
Integer i = (Integer) o;
26
System.out.println("Integer == " + o.getClass().getSimpleName() + " " + i.←֓
floatValue());
27
} else {
28
throw new RuntimeException("Object of unknown class"); 29
}
30
}
31
}
32
}
Przykład 21: src/pl/kielce/tu/lab10/TestIsInstance.java {link}
11
Przykładowa treść laboratorium
Proszę zapoznać się z podstawowymi klasami wyjątków zdefiniowanymi w pakietach:
- java.lang (http://download.oracle.com/javase/6/docs/api/java/lang/package-summary.html)
- java.util (http://download.oracle.com/javase/6/docs/api/java/util/package-summary.html).
Proszę stworzyć aplikację przedstawiającą wszystkie sposoby (9) obsługi wyjątków przedstawione w tej instrukcji laboratoryjnej.
Proszę stworzyć aplikację demonstrującą użycie RTTI (operator instanceOf , metod: isInstance(), f orN ame(), getConstructors(), getM ethods() oraz getF ields()).
12