400 likes | 596 Views
Wybrane elementy języka Java. Paweł Zdziarski. Wyjątki Reflection Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Nowe elementy Javy 1.5 Typy sparametryzowane Covariant return types „ autoboxing ” Pętla w stylu foreach Bezpieczne ( type-safe ) enumeracje
E N D
Wybrane elementy języka Java Paweł Zdziarski
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Wyjątki • Obsługa błędów semantycznych (runtime) w sposób efektywniejszy niż zwracanie kodu błędu metody • Podnoszenie wyjątku • Wywołanie metody podnoszącej wyjątek (throws) • Explicite: klauzulą throw
Obsługa wyjątków • Wyjątki są zgodnie z definicją języka precise – zoptymalizowana implementacja może wykonać część instrukcji naprzód, ale w przypadku zgłoszenia wyjątku musi zadbać o to, żeby w widocznym stanie wykonania (user-visible state) wyliczone były dokładnie te wyrażenia, które wystąpiły przed wyrażeniem podnoszącym wyjątek • Znajdowana jest klauzulacatch bloku try-catchdynamicznie zawierającego generujące wyjątek wyrażenie
Obsługa wyjątków • Jaki będzie rezultat wykonania try{ try { throw new Exception1(); } catch(Exception1 e){ System.out.println("Wewnetrzny catch: ” +e.toString()); throw new Exception2(); } finally {throw new Exception3();} }catch (Exception e) {System.out.println("Zewnetrzny catch: ” +e.toString());} • Blok try-catch-finallyma jeden atrybut – propagowany dalej wyjątek. Zgodnie z definicją języka, wewnętrzny blok w tym przykładzie będzie propagował wyjątek klasy Exception3, a więc • Wewnetrzny catch: mypackage18.Application1$Exception1 • Zewnetrzny catch: mypackage18.Application1$Exception3
Przyczyny powstania wyjątku • Błąd wykonania JVM – wyjątek zgłaszany synchronicznie • Operacja niezgodna z semantyką języka (np. java.lang.ArithmeticException ) • Błąd ładowania/linkowania programu ( NoClassDefFound) • Brak zasobu ( OutOfMemoryError, NoRouteToHostException ) • Jawne użycie klauzuli throw • Wyjątek podnoszony asynchronicznie • Może wystąpić potencjalnie w dowolnym punkcie wykonania bieżącego wątku • Inny wątek wywołuje stop() • Wewnętrzny błąd JVM ( VirtualMachineError )
Hierarchia wyjątków • checked (podklasy Exceptionnie będące RuntimeException) – muszą być wyspecyfikowane w klauzuli throws metody lub konstruktora; kompilator wymusza implementowanie obsługi takich wyjątków • Unchecked – kompilator nie wymusza na programiście obsługi (ani deklaracji throws) • RuntimeException (np. ArithmeticException, NullPointerException) - kompilator nie ma możliwości posłużenia się zaawansowanymi technikami dowodzenia poprawności programów i nie może przez to stwierdzić, czy dany ciąg instrukcji może wygenerować taki wyjątek lub nie • Error (np. OutOfMemoryError extends VirtualMachineError) – mamy do czynienia z „nienormalnym” (abnormal) stanem JVM
Errorjest osobną, dziedziczącą poThrowableklasą – dzięki temu wszystkie wyjątki, które da się jakoś obsłużyć, można „potraktować” konstrukcją catch (Exception e)
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Pakiet java.lang.reflect • Dostęp do obiektów reprezentujących konstrukcje samego języka • Obiekt Class(pakiet java.lang) reprezentuje załadowaną postać binarną klasy; użycie: public static Class forName(String className) throws ClassNotFoundException • Methodreprezentuje metodę klasy lub interfejsu Class c = Class.forName(nazwaKlasy); Method m[] = c.getDeclaredMethods(); for (int i = 0; i < m.length; i++) System.out.println(m[i].toString()); • Analogiczne użycie obiektów klas Field, Constructor • Interfejs Memberimplementowany przez Field, Constructor, Classi Method; udostępnia metodę getModifiers()
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Konstruktory • W kodzie konstruktora pierwsza instrukcja może, opcjonalnie, być explicite wywołaniem konstruktora. Wpp domyślnie super() jest pierwszą instrukcją kodu konstruktora • Explicite wywołania konstruktora • this(<lista_argumentow>) • super(<lista_argumentow>) • <wyrazenie>.super()
Wyrazenie.super() • Tzw. kwalifikowane wywołanie konstruktora nadklasy. Przykład ze specyfikacji języka: class Outer { class Inner{ }} class ChildOfInner extends Outer.Inner { ChildOfInner() { (new Outer()).super(); } }
Jak wykonywana jest konstrukcja wyrazenie.super() ? • Rozważamy konstrukcję class O { class S {} } class C extends O.S { C() { wyrazenie.super(argumenty); ciag_pozostalych_instrukcji_konstruktora } } • wyrazeniejest wyliczane. Jeśli jego wartością jest null, wywołanie konstruktora nadklasy kończy się wyjątkiem NullPointerException • Niech O będzie klasą bezpośrednią otaczającą statycznie (leksykalnie) klasę (C), w której konstruktorze wystąpiła konstrukcja wyrazenie.super • Jeśli wartością wyrazenianie jest obiekt klasy O ani podklasy klasy O, generowany jest błąd kompilatora • Wyrażenia argumentysą wyliczane, od lewej do prawej • Konstruktor jest wywoływany
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
GC i finalization • Środowisko uruchomieniowe Javy utrzymuje tablicę referencji do obiektów. • Referencja jest usuwana, gdy obiekt przestaje być widoczny • Jawne przypisanie null do zmiennej • Gdy nie ma więcej referencji do obiektu, • Finalization: użycie System.runFinalization()wywoła finalize()dla wszystkich obiektów czekających na postprzątanie • Garbage collection: użycie System.gc()jawnie uruchamia GC
Osiągalność referencji • Wyznaczanie zbioru referencji nieosiągalnych (unreachable) dla żadnego z wątków programu • Jeśli wszystkie referencje do obiektu pochodzą z obiektów nieosiągalnych, sam obiekt jest nieosiągalny • Uwaga: dodatkowe warunki jeśli używamy JNI
finalization • finalize()zaimplementowana w Object(jako pusta metoda), programista może ją przesłonić public class ProcessFile { protected void finalize() throws Throwable { try { file.close(); } finally { super.finalize(); }} • (ciekawostka) w J2ME: brak metod związanych z garbage collection; jawne przypisanie null może zasugerować GC możliwość posprzątania obiektu • „Reference Objects and Garbage Collection”, artykuł na stronie http://developer.java.sun.com/developer/technicalArticles/ALT/RefObj/
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
interface Action<E extends Exception> { void run() throws E; } class AccessController { public static <E extends Exception> void exec(Action<E> action) throws E { action.run(); } } public class Main { public static void main(String[] args) { try { AccessController.exec ( new Action<FileNotFoundException>() { public void run() throws FileNotFoundException { someFile.delete(); } } // new Action ... ); // AccessController.exec() }// koniec bloku try catch (FileNotFoundException e) { } } // koniec metody main() } // koniec class Main
Typy sparametryzowane i JVM • Programy używające generics są tłumaczone do bytecode’u Javy, wykonywanego przez JVM • Proces nazywany erasure polega na mapowaniu typów generycznych do typów niesparametryzowanych • Niech |T| oznacza erasure typu T, zachodzą wtedy m.in. • |T <T1,...,Tn>| = |T| • |T[]| = |T|[] • Dokładny opis mechanizmu erasure, tłumaczenia metod i wyrażeń w „Adding Generics to the Java Programming Language: Participant Draft Specification”, dostępnym np. ze strony java.sun.com
Typy sparametryzowane i tablice • Bez generics tablice mają następującą własność: Tablica referencji nadtypu jest nadtypem tablicy referencji podtypu • Wynika stąd np. to, że Object[] jest nadtypem String[] • Możemy dzięki temu m.in. napisać Object[] objArr = new String[10]; • Brak analogicznej własności w przypadku typów sparametryzowanych LinkedList<Object> objList = new LinkedList<String>(); daje błąd kompilacji
Dynamiczna informacja o typie i array store check • Tablice w Javie zawsze posiadają dynamiczną informację o typie przechowywanych w nich obiektów • W czasie wykonywania operacji na tablicach wykonywany jest tzw. array store check, który może spowodować zgłoszenie ArrayStoreException Object objArr = new String[10]; objArr[0] = new Object(); kompiluje się, ale w czasie wykonania zgłaszą wyjątek • Programista może zakładać, że tablica zawiera elementy tego samego typu (albo podtypów tego typu) • Store check nie jest w ogóle wykonywany w przypadku kolekcji typów sparametryzowanych, bo poniższy kod LinkedList<Object> objList = new LinkedList<String>(); w ogóle się nie skompiluje
Typy sparametryzowane i tablice • Tablice typów sparametryzowanych nie są dopuszczalne ze względów bezpieczeństwa • class Box<T> {final T x;Box(T x) { this.x = x;}}Box<String>[] niebezpiecznaTablica = new Box<String>[3];Object[] tablicaObiektow = niebezpiecznaTablica;tablicaObiektow[0] = new Box<Integer>(3); /*błąd nie znajdowany przez tzw. array store check */ • String s = niebezpiecznaTablica[0].x; // BOOM!
Typy sparametryzowane i rzutowanie • Kompilator „nie zna” typu sparametryzowanego przed uruchomieniem programu • zadanie utrzymania poprawności rzutowań do typu parametrycznego należy do programisty static Integer foo() {Double d = new Double(1.0);return (Integer) d;} • Powyższy kod nie kompiluje się z błędem inconvertible types (znaleziono Double, oczekiwano Integer)
Typy sparametryzowane i rzutowanie public class BadCast {static <T> T foo() {Double d = new Double( 1.0 );return ( T ) d;}public static void main( String[] args ) {System.out.println( BadCast.<Integer>foo() );}} • Powyższy program nie generuje ostrzeżeń kompilatora i wykonuje się dając... 1.0 !!! • Dla przypomnienia class Number extends Object class Integer extends Number class Double extends Number • Kompilator „zauważyłby” błąd przy rzutowaniu, jeśli w kodzie funkcji foo()napisalibyśmy po prostu return d; • Kompilator nie generuje błędu, jeśli napiszemy return (T) d;
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Covariant return types • Do wersji 1.4 języka przesłaniająca implementowaną w nadklasie metoda podklasy musiała mieć identyczną sygnaturę – w szczególności, zwracany typ • Poniższy kod nie kompiluje się w JRE 1.4.1_02 class Fruit implements Cloneable { Fruit copy() throws CloneNotSupportedException { return (Fruit) clone(); }} class Apple extends Fruit implements Cloneable { Apple copy() throws CloneNotSupportedException { return (Apple) clone(); }} • Wywołując clone() na obiekcie Apple dostajemy obiekt nadklasy Fruiti musimy niepotrzebnie rzutować w dół do Apple • Java 1.5 dopuszcza taką konstrukcję
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
System typów i autoboxing • Typy proste (primitives) i referencyjne (obiekty) • Czasem konstrukcja wymaga użycia typu referencyjnego: • Jako typu parametryzującego szablon • Jako obiektów kolekcji np. List • Konieczne rzutowania list.add(new Integer(1)); int i = ((Integer)list.get(0)).intValue(); • Autoboxing / autounboxing robi to automatycznie Integer integer = new Integer(1); integer += 1;
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Iterowanie po elementach kolekcji • Dotychczas (Java 1.4) używamy konstrukcji typu public void drawAll (Collection c) { Iterator itr = c.iterator(); while (itr.hasNext()) { ((Shape)itr.next()).draw(); } } • Używając typów parametrycznych, możemy zaoszczędzić sobie kodowania kilku rzutowań • public void drawAll (Collection<Shape> c) { • Iterator<Shape> itr = c.iterator(); • while (itr.hasNext()) { • itr.next().draw(); • } • }
Pętla „foreach” + generics • Nowa dopuszczalna postać pętli „for” public void drawAll(Collection<Shape> c) { for (Shape s:c) s.draw(); } • Rozwijane automatycznie do kodu for (Iterator<Shape> $i = c.iterator(); $i.hasNext();) { Shape s = $i.next(); s.draw(); }
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Bezpieczne (type-safe) typy wyliczeniowe • Dotychczas class Movement { public static final int UP = 0; public static final int LEFT = 1; public static final int DOWN = 2; public static final int RIGHT = 3; } • Wywołanie metody oczekującej parametru takiego typu wyliczeniowego z parametrem np. 5 nie wygeneruje błędu kompilacji • Nowa konstrukcja „enum” class Movement { public enum Direction {up, left, down, right} }
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Statyczne „import” • Po użyciu import static java.lang.Math.*; możemy pisać po prostu int i = abs(-1); zamiast kwalifikować dodatkowo nazwą pakietu int i = Math.abs(-1); • Potencjalne błędy kompilacji w przypadku powstającej w ten sposób dwuznaczności nazw
Wyjątki • Reflection • Tworzenie i zarządzanie obiektami • Garbage Collector i finalize() • Nowe elementy Javy 1.5 • Typy sparametryzowane • Covariant return types • „autoboxing” • Pętla w stylu foreach • Bezpieczne (type-safe) enumeracje • Statyczne import • Metody ze zmienną liczbą parametrów
Zmienna liczba argumentów public static int sum(int args...) { int sum = 0; for (int x : args ) /* użycie wcześniej przedstawianej konstrukcji „foreach” */ {sum += x; } return sum; }
Literatura i URLs • Wprowadzenie do użycia Generics http://developer.java.sun.com/developer/technicalArticles/releases/generics/ • Artykuł o tablicach typów sparametryzowanych http://www.langer.camelot.de/Articles/Java/JavaGenerics/ArraysInJavaGenerics.htm • The Java Language Specification, dostępna z java.sun.com