Wybrane elementy języka Java Paweł Zdziarski
Jan 04, 2016
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 klauzula catch bloku try-catch dynamicznie 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-finally ma 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ęcWewnetrzny catch: mypackage18.Application1$Exception1Zewnetrzny 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 Exception nie 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
Error jest osobną, dziedziczącą po Throwable klasą – dzięki temu wszystkie wyjątki, które da się jakoś obsłużyć, można „potraktować” konstrukcją catch (Exception e)
OutOfMemoryError
ThreadDeath VirtualMachineError
Throwable
Error
NullPointerException
RuntimeException
Exception
MyExceptionIOException
IllegalArgumentException
ArithmeticException
SQLException
• 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
• Method reprezentuje metodę klasy lub interfejsuClass 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 Member implementowany przez Field, Constructor,
Class i 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 }}• wyrazenie jest 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ą wyrazenia nie jest obiekt klasy O ani podklasy klasy O, generowany jest
błąd kompilatora• Wyrażenia argumenty są 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
• Powyższy program nie generuje ostrzeżeń kompilatora i wykonuje się dając...1.0!!!
• Dla przypomnieniaclass Number extends Objectclass Integer extends Numberclass 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;
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() ); } }
• 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_02class 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
Fruit i 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 typupublic 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