Java

advertisement
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ę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 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
Throwable
Exception
SQLException
Error
RuntimeException
MyException
IOException
ThreadDeath VirtualMachineError
NullPointerException
ArithmeticException
OutOf MemoryError
IllegalArgumentException
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)
• 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 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 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
public
static
Double
return
}
class BadCast {
<T> T foo() {
d = new Double( 1.0 );
( 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
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 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
Download