29 czerwca 2008

SCJP – Operatory

Ledwie zdążyłem wyrazić swą radość – w artykule "SCJP - Odśmiecacz, czyli mechanizm przywracania pamięci" – z powodu zakończenia przerabiania trzeciego rozdziału Książki, a tymczasem już kończymy rozdział czwarty. Zgadza się! Cały rozdział czwarty streszczony w jednym, dzisiejszym artykule. A będzie dziś o operatorach wszelkiej maści.

Operatory przypisania opisałem zasadniczo w artykule "SCJP - Operatory przypisania dla typów prymitywnych". Na egzaminie, oprócz zwykłego operatora przypisania ‘=’ występują podobno jeszcze tylko operatory ‘+=’, ‘-=’, ‘*=’ oraz ‘/=’. Jedyną rzeczą, jaką można w tym kontekście dodać jest, że prawa strona wyrażeń z operatorami przypisania jest zawsze wyliczana jako pierwsza, tak więc wyrażenie ‘x *= 1 + 2’ jest równoważne wyrażeniu ‘x = x * (1 + 2)’ a nie ‘x = x * 1 + 2’.

Operatory arytmetyczne, mogące pojawić się na egzaminie SCJP to ‘+’, ‘-‘, ‘*’, ‘/’ i ‘%’ czyli reszta z dzielenia oraz operatory inkrementacji i dekrementacji ‘++’ oraz ‘--‘. Na komentarz zasługuje operator ‘+’. Oprócz tego, że jest on operatorem dodawania, może także być stosowany jako operator konkatenacji String’ów. Pamiętajmy, że jeśli choć jeden z operandów jest String’iem, to ‘+’ nie jest operatorem dodawania, tylko operatorem konkatenacji. Pamiętajmy także o kolejności wykonywania działań. Dla przykładu, instrukcja

System.out.println("2 plus 2 to " + 2 + 2);

powoduje wyświetlenie napisu "2 plus 2 to 22". Ale już instrukcja

System.out.println("2 razy 2 to " + 2 * 2);

daje wynik oczekiwany, tj. "2 razy 2 to 4".

Pierwszeństwo mają zawsze operatory unarne, a więc ‘++’ i ‘--‘. Następnie wykonywane są operacje ‘*’, ‘/’ oraz ‘%’ a operatory ‘+’ i ‘-‘ wiążą najsłabiej. Poza tym, jeśli kolejność wykonywania działań nie jest w pełni wyznaczona przez pierwszeństwo operatorów to są one wykonywane od lewej do prawej. W wyrażeniu >>"2 plus 2 to " + 2 + 2<< jako pierwsza wykona się więc operacja konkatenacji >>"2 plus 2 to " + 2<< której wynikiem jest "2 plus 2 to 2". Następnie, wykonana będzie konkatenacja >>"2 plus 2 to 2" + 2<< a więc w rezultacie otrzymamy napis "2 plus 2 to 22". Ponieważ jednak operator ‘*’ wiąże silniej niż ‘+’, w wyrażeniu >>"2 razy 2 to " + 2 * 2<< w pierwszej kolejności zostanie wyliczona wartość dla >>2 * 2<<, a potem wykonana będzie konkatenacja >>"2 razy 2 to " + 4<<.

Na SCJP pojawiają się także operatory logiczne. Są to ‘&’, ‘|’ i ‘^’ – które są operacjami logicznymi dla operandów typu logicznego a operacjami bitowymi dla operandów typu całkowitoliczbowego – oraz ‘&&’, ‘||’ i ‘!’. Różnica między operatorami ‘&’ i ‘|’ oraz ‘&&’ i ‘||’ zastosowanymi do operandów typu logicznego jest taka, że te pierwsze wyliczają wartość wyrażeń logicznych zachłannie a te drugie leniwie.

Operatory relacyjne ‘<’, ‘<=’, ‘>’, ‘>=’ mogą być stosowane tylko do porównywania wartości typów numerycznych i typu ‘char’, przy czym za wartość dla typu ‘char’ przyjmuje się kod Unicode znaku. Operatory ‘==’ oraz ‘!=’ można stosować dodatkowo do porównywania wartości typu logicznego i typów referencyjnych.

Operator ‘instanceof’ służy do testowania, czy referencja odnosi się do obiektu danego typu. Referencja jest operandem lewostronnym a nazwa klasy bądź interfejsu prawostronnym. Mówiąc precyzyjniej, wynikiem wyrażenia ‘x instanceof SomeClass’ jest ‘true’ gdy instancja ‘x’ IS-A ‘SomeClass’. Co to oznacza IS-A opisywałem w artykule "SCJP - Związki typu IS-A oraz HAS-A". A co, jeśli ‘x’ ma wartość ‘null’? Czy w rezultacie otrzymamy ‘NullPointerException’? Nie, wyrażenie ‘null instanceof SomeClass’ ma wartość ‘false’ i nic złego się nie dzieje. Trzeba jeszcze zaznaczyć, że operatora ‘instanceof’ nie możemy użyć, jeśli nie ma szans na powodzenie, tj. jeśli typ referencji ‘x’ nie należy do tej samej hierarchii dziedziczenia co klasa ‘SomeClass’. Nie spełnienie tego warunku jest błędem czasu kompilacji. Przykład poniżej.

public class Test {
public static void main(String[] args) {
Integer x = 5;

if(x instanceof Long) // błąd kompilacji!
System.out.println("cuda się zdarzają");
}
}

25 czerwca 2008

SCJP - Odśmiecacz, czyli mechanizm przywracania pamięci

Dzisiejszym artykułem zamkniemy wreszcie trzeci rozdział Książki – patrz artykuł "Przygotowania do SCJP czas zacząć". Jak sami autorzy piszą, był to rozdział monstrum; cieszę się więc, że mamy to już za sobą. Kolejny rozdział dla odmiany jest króciutki. Będzie okazja poprawić sobie humor, że nie idzie to aż tak wolno. Będzie dzisiaj o odśmiecaniu pamięci (ang. garbage collection).

Obiekty w Javie, tak jak wszystko w przyrodzie mają swój początek i koniec. Cykl jest prosty – tworzymy, używamy i… porzucamy. Zwróćmy uwagę na ostatnie słowo; porzucamy, ale nie niszczymy. Niszczeniem zajmuje się odśmiecacz (ang. garbage collector). Naturalnie, odśmiecacz niszczy tylko obiekty porzucone, a więc takie, które już nigdy nie będą mogły zostać użyte. Ktoś mógłby powiedzieć, że niepotrzebne obiekty to takie, na które nie wskazuje żadna referencja. Jest to prawda, ale tylko częściowa. Rzeczywiście, obiekty na które nie wskazuje żadna referencja są już bezużyteczne i podlegają odśmiecaniu, ale nie jest to warunek konieczny. Zerknijmy na poniższy przykład.

public class Test {
public static void main(String[] args) {
SomeClass oneInstance = new SomeClass();

oneInstance.otherInstance = new SomeClass();

oneInstance = null;
}
}

class SomeClass {
public SomeClass otherInstance;
}

W pierwszej linii metody ‘main(…)’ tworzymy nową instancję klasy ‘SomeClass’ i przypisujemy ją do zadeklarowanej referencji. W drugiej linii tworzymy kolejny obiekt i przypisujemy go do zmiennej instancyjnej obiektu utworzonego uprzednio. Zauważmy, że póki co oba obiekty są osiągalne z aktywnego wątku aplikacji – pierwszy obiekt jest osiągalny bezpośrednio, poprzez referencję ‘oneInstance’ a drugi pośrednio. W trzeciej linii metody ‘main(…)’ przypisujemy do zmiennej ‘oneInstance’ wartość ‘null’. Tym samym powodujemy, że żadna z dwu utworzonych uprzednio instancji nie jest już osiągalna z aktywnego wątku aplikacji. Co prawda żadna referencja nie wskazuje już na obiekt utworzony jako pierwszy, ale na drugi z obiektów cały czas wskazuje referencja ‘otherInstance’. Mimo to, oba obiekty są już bezużyteczne i podlegają procesowi odśmiecania. Definicja obiektu porzuconego, a więc podlegającego procesowi odśmiecania jest więc taka, że jest to obiekt który nie jest bezpośrednio ani pośrednio osiągalny z żadnego aktywnego wątku wykonania.

Pozostało jeszcze odpowiedzieć na pytanie – kiedy uruchamiany jest proces odśmiecacza? Generalnie rzecz biorąc… nie wiadomo – zależy to od konkretnej implementacji JVM. Co prawda mamy do dyspozycji metodę ‘java.lang.Runtime.getRuntime().gc()’, która służy do zgłoszenia prośby o uruchamianie odśmiecania, ale to tylko tyle – zgłoszenie prośby. Wywołanie tej metody wcale nie musi zakończyć się podjęciem jakiejkolwiek akcji, jest to tylko drobna sugestia, którą kierujemy do JVM, jednak, gdy już JVM zdecyduje się na podjęcie akcji oczyszczania pamięci to robi to synchronicznie – proces kończy się, zanim nastąpi powrót z wywołania. Ekwiwalentem dla wywołania ‘java.lang.Runtime.getRuntime().gc()’ jest ‘java.lang.System.gc()’.

Ze względu na brak możliwości założenia czegokolwiek na temat sposobu działania i momentu uruchomienia odśmiecacza – oraz braku gwarancji, że dany obiekt w ogóle zostanie kiedykolwiek usunięty z pamięci – język Java nie oferuje znanych z skądinąd destruktorów. Oferuje jednak pewną namiastkę – metodę ‘finalize()’. Jest to metoda zdefiniowana w klasie ‘Object’. Nie jest to metoda finalna, więc może być nadpisana (ang. overridden) w dowolnej podklasie. Znaczenie tej metody jest zbliżone do destruktorów w tym sensie, że jest to metoda wywoływana automatycznie przez proces odśmiecacza tuż przez usunięciem obiektu z pamięci, tyle, że jest jedno ale. Spójrzmy na poniższy przykład.

public class ResurrectionClass {
public static Set<Object> collection;
}

class SomeClass {

@Override
public void finalize() {
ResurrectionClass.collection.add(this);
}
}

Kod ten jest jak najbardziej poprawny. Jako że metoda ‘finalize()’ to metoda jak każda inna, to możliwe jest umieszczenie w niej kodu, który powoduje, że dana instancja zaczyna być osiągalna z aktywnego wątku a więc nie może być usunięta. I nie jest. Nie czyni to jednak instancji tej klasy nieśmiertelnymi, bowiem metoda ‘finalize()’ wywoływana jest dla każdego usuwanego obiektu tylko i wyłącznie raz! Proces odśmiecania wywoła metodę ‘finalize()’ za pierwszym razem, gdy próbuje usunąć obiekt z pamięci i wtedy możliwe jest wykonanie operacji pokazanej powyżej, jednak już za drugim razem metoda ta nie jest wywoływana i obiekt jest nieuchronnie niszczony.

24 czerwca 2008

SCJP - Przeciążanie metod raz jeszcze

Dzisiejszy artykuł zatytułowałem "SCJP - Przeciążanie metod raz jeszcze", jako że o przeciążaniu (ang. overloading) już pisałem, w artykule "SCJP - Przeciążanie metod". Tym razem mam nadzieję wyczerpać temat.

Jak napisałem w poprzednim artykule – przeciążanie metod to definiowanie wielu metod o tej samej nazwie a różniących się listą argumentów. No dobrze, ale skoro możemy mieć kilka metod o tej samej nazwie, to skąd wiadomo, która z nich zostanie wywołana? O tym właśnie jest dzisiejszy artykuł. Zacznijmy od prostego przykładu.

class Test {
public static void main(String[] args) {
Test test = new Test();

char c = 1;
byte b = 1;
float f = 1;

test.someOp(c);
test.someOp(b);
test.someOp(f);
}

void someOp(short x) {
System.out.println("someOp(short x)");
}

void someOp(long x) {
System.out.println("someOp(long x)");
}

void someOp(double x) {
System.out.println("someOp(double x)");
}
}

Dla typów prostych zasady są następujące: jeśli nie ma metody odpowiadającej idealnie typom parametrów wywołania, to wybierana jest ta metoda, której argumenty są jak najmniej "większe", przy czym "najmniejszy" jest typ ‘byte’ i kolejno ‘short’, ‘int’, ‘long’, ‘float’, ‘double’. Parametry typu ‘char’ traktowane są jakby były typu ‘int’. Wynikiem uruchomienia programu będzie więc:

someOp(long x)
someOp(short x)
someOp(double x)

Co będzie jednak, gdy damy kompilatorowi wybór między "większym" typem prostym a idealnie odpowiadającym typem opakowującym, tak jak to pokazano w kolejnym przykładzie?

class Test {
public static void main(String[] args) {
Test test = new Test();

int i = 1;

test.someOp(i);
}

void someOp(double x) {
System.out.println("someOp(double x)");
}

void someOp(Integer x) {
System.out.println("someOp(Integer x)");
}
}

Kompilator wybierze "większy" typ prosty. Taka jest zasada: konwersja do bardziej "pojemnego" typu prostego ma pierwszeństwo przed opakowywaniem (ang. in-boxing). Tak samo jest z argumentami wielo-arnymi (ang. var-args, variable-arity arguments). Jeśli tylko jest taka możliwość, to kompilator wybierze metodę nie zawierającą wielo-arnych argumentów. A co, jeśli kompilator ma do wyboru tylko opakowanie typu prostego w instancję odpowiadającej mu klasy albo wywołanie metody z argumentem wielo-arnym? Zerknijmy na kolejny przykład.

class Test {
public static void main(String[] args) {
Test test = new Test();

test.someOp(1L);
}

void someOp(long... x) {
System.out.println("someOp(long... x)");
}

void someOp(Long x) {
System.out.println("someOp(Long x)");
}
}

Kompilator zawsze preferuje opakowanie (ang. in-boxing). Uruchomienie powyższego programu spowoduje wyświetlenie napisu "someOp(Long x)". Podsumowując: jeśli tylko się da, kompilator wybiera konwersję typów prostych do innych, "pojemniejszych" typów prostych. W dalszej kolejności, kompilator próbuje znaleźć metodę, która wymaga opakowania w obiekty a dopiero, gdy to zawiedzie, rozważa metody używające argumentów wielo-arnych.

23 czerwca 2008

SCJP - In-boxing i out-boxing

W artykule "SCJP - Klasy opakowujące typów prostych" opisałem klasy opakowujące dla typów prostych. Wspomniałem tam też, że począwszy od Javy 5 nie musimy owego pakowania i rozpakowywania wykonywać samodzielnie, gdyż zrobi to za nas kompilator. Dziś będzie właśnie o tym, o in-boxingu i out-boxingu.

Co to jest in-boxing i out-boxing wyjaśnię na przykładzie. Poniżej pokazana klasa implementuje dwie metody równoważne pod względem wykonywanych operacji, z tą różnicą, że druga z nich używa in- oraz out- (auto-) boxingu a pierwsza nie.

public class Test {
private Integer counter;

public void noAutoBoxing() {
if(counter == null)
counter = new Integer(0);

counter = new Integer(counter.intValue() + 1);
}

public void useAutoBoxing() {
if(counter == null)
counter = 0; // in-boxing

counter++; // out-boxing, inkrementacja, in-boxing
}
}

Zaobserwujmy przy okazji jedną ciekawą rzecz – dzięki zastosowaniu typu obiektowego dla zmiennej ‘counter’, tj. ‘Integer’ a nie ‘int’ mamy możliwość odróżnienia zmiennej nie zainicjalizowanej od zmiennej posiadającej wartość ‘0’, czy inną wybraną wartość. Czasem taka możliwość jest wygodna.

Metoda ‘equals(…)’ dla typów opakowujących działa zgodnie z oczekiwaniami, tj. obiekty uważane są za równe, jeśli są tego samego typu i reprezentują tą samą wartość, ale za to operatory równości (==) i różności (!=) zadziwiają. Zerknijmy na poniższy przykład.

public class Test {
public static void main(String[] args) {
Integer a = 1024; // odpowiada 'Integer a = new Integer(1024);'
Integer b = 1024;

if(a != b)
System.out.println("a i b to różne obiekty");

Integer c = 16;
Integer d = 16;

if(c == d)
System.out.println("c i d to ten sam obiekt");
}
}

O dziwo, zostaną wypisane obydwie sentencje, tj. "a i b to różne obiekty", ale "c i d to ten sam obiekt". Hmmm? Zgadza się, jest to zgodne ze specyfikacją języka, a powodem jest jak można się domyślać chęć optymalizacji zużycia pamięci. Zasada jest taka: jeśli opakowywana (przez automatyczny in-boxing) jest wartość typu ‘boolean’, ‘byte’ lub ‘short’ czy ‘integer’ z przedziału -128 do 127, albo ‘char’ z przedziału \u0000 do \u007f to używana jest dla danego typu i danej wartości zawsze ta sama instancja obiektu – to wyjaśnia, dlaczego "c i d to ten sam obiekt". Co do tego, że "a i b to różne obiekty" to sprawa nie jest jasna. W specyfikacji języka Java wcale nie jest powiedziane, że ‘a’ i ‘b’ muszą odwoływać się do różnych obiektów – przeciwnie, powiedziane jest, że dobrze byłoby gdyby te same wartości zawsze dawały tą samą instancję. To tyle, jeśli chodzi o SCJP a w życiu jak to w życiu; bywa różnie. Powyższy kod uruchomiony na JVM od Sun’a w wersji 1.5.0_11-b03 działa prawidłowo, tj. zgodnie ze specyfikacją, ale już uruchomiony na BEA JRockit w wersji 1.5.0_10-b03 nie, tj. zmienne ‘c’ i ‘d’ wskazują na różne obiekty, co jest wbrew specyfikacji. Czyżby błąd? Dla zainteresowanych – zagadnienia te są omówione w specyfikacji w rozdziale 5.1.7. Poniżej stosowny wyjątek:

"If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2."

19 czerwca 2008

Nowe repozytorium wtyczki m2eclipse

Może mi się coś pomieszało, ale mógłbym przysiąc, że jeszcze nie tak dawno tak zwanym "update sitem" dla wtyczki integrującej Maven'a z Eclipsem było http://m2eclipse.codehaus.org/update/... a najnowsza wersja wtyczki dostępnej w tym repozytorium nie powala swoimi możliwościami. W każdym razie, aktualnym "update sitem" jest http://m2eclipse.sonatype.org/update/. Zainstalowałem nową wersję i świat od razu wygląda lepiej.

SCJP - Klasy opakowujące typów prostych

Dzisiejszy artykuł traktuje o klasach opakowujących dla typów prostych (ang. wrapper classes). Co prawda artykuł piszę z perspektywy przygotowań do SCJP – patrz "Przygotowania do SCJP czas zacząć" – ale coraz częściej zauważam, że seria czytana jest także przez osoby SCJP nie zainteresowane, więc może tak czy inaczej warto? Zapraszam.

Każdy typ prosty z języka Java posiada swój odpowiednik w postaci klasy w pakiecie 'java.lang'. Dla typu ‘int’ jest to ‘java.lang.Integer’, dla typu ‘char’ 'java.lang.Character' a dla pozostałych klasy nazywają się tak samo jak typ prosty, tyle, że nazwa klasy zaczyna się wielką literą. Dla przykładu, dla typu ‘float’ będzie to ‘java.lang.Float’. Do czego służą te klasy i dlaczego ich potrzebujemy? Z dwu powodów. Po pierwsze, aby móc używać wartości prymitywnych w operacjach, które wymagają obiektów. Zerknijmy na poniższy przykład.

public class Test {
public static void main(String[] args) {
Storage storage = new Storage();

int x = 1;

storage.addToStorage(new Integer(x));
}
}

class Storage {
private Set<Object> storage;

public void addToStorage(Object obj) {
this.storage.add(obj);
}
}

Deklarując kolekcję – zbiór – w klasie ‘Storage’ mieliśmy intencję taką, aby móc w tej kolekcji przechowywać dowolne byty. Tyle, że obiekty języka Java to nie są wszystkie byty jakie nas interesują – są jeszcze przecież wartości prymitywne, które obiektami nie są. Aby więc móc dodać wartość zmiennej ‘x’ typu ‘int’ do kolekcji musimy utworzyć obiekt, który tą wartość będzie reprezentował. W powyższym przykładzie jest to konstrukcja ‘new Integer(x)’. Co prawda odkąd mamy w Javie (a mamy począwszy od Javy 5) "in-boxing" i "out-boxing" (o których będzie mowa w kolejnych artykułach) nie musimy robić tego explicite, ale nie zmienia to faktu, że taka operacja musi być wykonywana.

Omawiane klasy – oprócz tego, że są obiektowymi opakowaniami dla typów prostych – udostępniają szereg przydatnych operacji konwersji. Jako operację konwersji możemy również postrzegać samą operację konstrukcji. Żadna z omawianych klas nie udostępnia konstruktora bez-argumentowego. Wszystkie za to udostępniają konstruktor jednoargumentowy z argumentem typu prostego odpowiadającego klasie, czyli np. ‘Boolean(boolean b)’ czy ‘Character(char c)’. Dodatkowo, wszystkie klasy oprócz ‘Character’ udostępniają konstruktor akceptujący obiekt typu ‘String’ – a więc jest to konwersja z typu napisowego. Wyjątkowo, klasa ‘Float’ implementuje jeszcze konstruktor, który akceptuje wartości typu ‘double’. Bardzo ważną rzeczą jest, że obiekty klas opakowujących reprezentują jedną konkretną wartość danego typu. Oznacza to, że wartość reprezentowana np. przez obiekt klasy ‘Integer’ nigdy nie może być zmieniona – nie ma operacji ‘setValue(int val)’ czy ‘increase()’. Z tego też powodu nie ma konstruktorów bezargumentowych – liczba czy wartość logiczna, albo litera nieposiadające wartości nie mają sensu, a późniejsze ustawienie tej wartości nie jest możliwe. Ilustruje to dobitnie poniższy przykład.

public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = i;

if(i == j) // warunek jest spełniony
System.out.println("zmienne i oraz j reprezentują ten sam obiekt");

i = i - 1; // równoważne z instrukcją ‘i = new Integer(i.intValue() - 1)’

if(i == j) // warunek już nie jest spełniony
System.out.println("zmienne i oraz j nadal reprezentują ten sam obiekt");
}

Rezultatem uruchomienia tego programu będzie wypisanie tylko pierwszego ze zdań, albowiem po operacji ‘i = i - 1’ referencja ‘i’ nie odnosi się już do tego samego obiektu. Wartości reprezentowanej przez obiekt, do którego odnosi się zmienna ‘i’ nie da się zmienić, zatem trzeba utworzyć nowy obiekt. Instrukcja ‘i = i - 1‘ jest de facto równoważna instrukcji ‘i = new Integer(i.intValue() - 1)’. Na zakończenie tematu konstruktorów jeszcze słowo komentarza o konwersji z typu napisowego, a więc o konstruktorach akceptujących obiekt typu ‘String’. Wszystkie one – dla typów numerycznych, a więc z wyłączeniem klasy ‘Boolean’ – zgłaszają wyjątek 'NumberFormatException' w przypadku, gdy konwersja nie jest możliwa. Taki wyjątek otrzymamy np. jeśli spróbujemy uruchomić konstruktor ‘new Integer("jeden")’ – niestety, Java nie umie jeszcze czytać po polsku. W innych językach też nie umie, ogranicza się do czytania cyfr. Analogiczna konwersja dla typu ‘Boolean’ zawsze kończy się sukcesem, tj. konstruktor tej klasy nie zwraca wyjątków. Obiekt klasy ‘Boolean’ reprezentujący wartość ‘true’ uzyskamy z napisu "true", przy czym nie ważna jest wielkość liter, zaś obiekt reprezentujący wartość ‘false’ z każdego innego napisu, oraz gdy zamiast obiektu klasy ‘String’ przekażemy wartość ‘null’ (!). Uwaga! Instrukcja ‘new Boolean("1")’ także powoduje utworzenie obiektu reprezentującego wartość ‘false’.

Bliskim kuzynem konstruktorów są metody ‘valueOf(…)’. Są to statyczne metody-fabryki klas opakowujących, których zadaniem jest tworzenie instancji tychże klas. Jaka jest więc różnica między tymi metodami a konstruktorami? Różnica głównie tkwi w tym, że metody ‘valueOf(…)’ nie zawsze tworzą nowy obiekt. Zamiast tego – w celu optymalizacji – używane są istniejące, ale już nie wykorzystywane instancje. Szczegóły nie są dla nas ważne, ale trzeba zapamiętać, że jeśli nie zależy nam na utworzeniu NOWEGO obiektu to powinniśmy używać tych metod zamiast konstruktorów. Pomijając względy wydajnościowe – metody ‘valueOf(…)’ dają te same możliwości tworzenia obiektów, co konstruktory (poza tym, że nie ma metody ‘Float.valueOf(double d)’, ale to szczegół) a oprócz tego, pozwalają na parsowanie liczb zapisanych w systemie innym niż dziesiętny. Zerknijmy na poniższy przykład.

public static void main(String[] args) {
System.out.println(Integer.valueOf("100", 2));
}

Rezultatem uruchomienia tej metody będzie wypisanie liczby ‘4’, albowiem w systemie dziesiętnym taka jest właśnie wartość liczby ‘100’ zapisanej w systemie dwójkowym. Metody ‘valueOf(String value, int radix)’, służące do konwersji z jednoczesną zmianą systemu liczbowego są zaimplementowane tylko dla typów całkowitoliczbowych, a więc dla typów: Byte, Short, Integer i Long.

Bardzo podobne do metod ‘valueOf(…)’ są metody ‘parseXXX(…)’ gdzie ‘XXX’ to nazwa odpowiedniego typu prymitywnego. Są to również metody statyczne do konwersji ze String’ów, tyle, że do typów prymitywnych a nie instancji klas opakowujących. Metody ‘parseXXX(…)’ występują w dwu odmianach: jedno-argumentowej i dwu-argumentowej. Wariant jedno-argumentowy – z argumentem typu String – zaimplementowany jest we wszystkich klasach poza klasą ‘Character’; wariant dwu-argumentowy tylko w klasach reprezentujących typy całkowitoliczbowe. Pierwszy argument to liczba a drugi (typu ‘int’) to podstawa systemu liczbowego (ang. radix), w którym ta liczba jest zapisana.

Sprawę tworzenia obiektów z wartości prymitywnych oraz konwersji String’ów na obiekty i wartości prymitywne mamy już z głowy. Została jeszcze kwestia przejścia w drugą stronę, a więc pobieranie wartości prymitywnej z obiektu oraz konwersja wartości reprezentowanej przez obiekt na ‘String’. Aby przejść od typu prymitywnego do String’a można użyć statycznej metody ‘valueOf(…)’ z klasy ‘String’.

Aby pobrać wartość prymitywną reprezentowaną przez daną instancję klasy typu opakowującego należy użyć jednej z metod postaci ‘xxxValue()’. Naturalnie, w odróżnieniu od wcześniej omawianych metod są to metody instancji. W klasie ‘Boolean’ zdefiniowano tylko metodę ‘booleanValue()’, podobnie w klasie ‘Character’ jest tylko metoda ‘charValue()’ ale w pozostałych klasach, tj. w klasach reprezentujących typy numeryczne jest już komplet metod numerycznych, tj. ‘byteValue()’, ‘shortValue()’, ‘intValue()’, ‘longValue()’, ‘floatValue()’ oraz ‘doubleValue()’. Można zatem obiekt klasy, dajmy na to, ‘Byte’ przekonwertować wywołaniem jednej funkcji do zmiennej typu ‘float’.

Została jeszcze konwersja z obiektu na ‘String’ i będzie po wszystkim… a nie było lekko. Przede wszystkim, instancje klas opakowujących są obiektami, a więc mamy do dyspozycji metodę instancyjną toString(). Dodatkowo, wszystkie typy numeryczne implementują jedno argumentową statyczną metodę ‘toString(…)’ która akceptuje odpowiadającą typem (np. ‘int’ w klasie ‘Integer’, ‘float’ w klasie ‘Float’ itd.) wartość prymitywną a zwraca ‘String’. W klasie ‘Integer’ oraz ‘Long’ jest jeszcze dwu-argumentowy wariant statycznej metody ‘toString(…)’. Drugi, dodatkowy argument typu ‘int’ określa system liczbowy, w którym zapisana ma być liczba. Trochę jakby nadmiarowo, klasy ‘Integer’ oraz ‘Long’ implementują też jednoargumentowe metody ‘toBinaryString(…)’, ‘toOctalString(…)’ oraz ‘toHexString(…)’, które robią dokładnie to samo, co wspomniane przed chwilą metody dwuargumentowe ‘toString(…)’, tyle, że system liczbowy określony jest w nazwie metody a nie w postaci drugiego argumentu.

9 czerwca 2008

Projekt JAX-WS z użyciem Eclipse Ganymede

Dałem się wciągnąć Jackowi – przystępuje do konkursu "Ganymede Around the World Contess". Zobaczymy, na co stać najnowsze dziecko Eclipse – Ganymede. W ramach testu zaimplementuję WebService bazujący na technologii JAX-WS. Niech będzie taki, jaki zaimplementowałem kiedyś używając Maven’a 2 i opisałem w artykule "JAX-WS i Maven 2 w podejściu Contract First Development". Tym razem również zacznę od WSDL’a. Na jego bazie wygeneruję klasy JAXB i interfejs serwisu, który następnie zaimplementuję.

Zaczynam od pobrania Eclipse Ganymede RC2 w wersji "for Java EE Developers". Żeby było ciekawiej, tj. żeby test rzeczywiście był testem idę na pełny żywioł. Będę na bieżąco pisał co robię, bez uprzedniego ćwiczenia czy jest to słuszna ścieżka i czy będzie działać.

Tworzymy więc nowy projekt – wybieramy kolejno ‘New -> Project -> Web -> Dynamic Web Project’. Zmieniamy wersję (specyfikacji Servlet’ów) na 2.5, wprowadzamy nazwę i… no właśnie, musimy wybrać serwer, na którym uruchomimy aplikację.



Ponieważ mamy świeżą instalację Eclipse’a musimy zdefiniować nowy serwer. W tym celu klikamy guzik ‘New’ w sekcji ‘Target Runtime’. Wybieramy Apache Tomcat v6.0, klikamy ‘Next’ i tu pierwsza miła niespodzianka – jeśli nie mamy jeszcze Tomcata na dysku możemy go sobie pobrać z sieci bezpośrednio z poziomu IDE. Co prawda ja mam, ale klikam ‘Download and Install’, żeby przetestować czy działa.



No i działa! Trochę dziwnie – serwer pobierany jest w osobnym wątku i dopóki proces się nie skończy widzimy komunikat błędu mówiący o niepoprawnej instalacji Tomcata – ale działa! Pierwszy plus dla nowej edycji. Przyjrzyjmy się teraz strukturze utworzonego projektu.



Uwagę zwraca tajemnicza gałąź ‘JavaScript Libraries’. Nie wiem, co to jest i do czego służy, ale zapewne nie ma nic wspólnego z WebService’ami więc zostawiam temat na później. W każdym razie jest to kolejna nowość Eclipse Ganymede (och… ciężko zapamiętać tę nazwę).

WebService będziemy implementować zaczynając od kontraktu – w pierwszej kolejności tworzymy więc WSDL’a. Korzystamy w tym celu z kreatora ‘New -> Other -> Web Services -> WSDL’. Akceptujemy proponowane wartości zmieniając ewentualnie nazwę pliku. Ja zmieniłem na ‘math.wsdl’.



Do stworzenia WSDL’a używam wbudowanego edytora. Nie zauważyłem tu zmian. Fakt, że nie wiele to oznacza, jako że słabo ten edytor znałem, ale na pierwszy rzut oka jest to to samo. Tak czy inaczej, edytor jest niezły i już po kilku kliknięciach mamy to, czego chcieliśmy.



Przechodzimy do kroku generowania kodu. Wybieramy opcję ‘New -> Other -> Web Services -> Web Service’. No i tu niestety wpadka. Eclipse jak nie wspierał JAX-WS tak nie wspiera. Jedyne opcje, jakie mamy do wyboru to ‘Axis’ i ‘Axis2’. Pomyślałem, że Eclipse jest na tyle mądry, że wie, iż Tomcat nie implementuje specyfikacji JAX-WS i stąd to ograniczenie, ale niestety, to samo jest z Geronimo 2.1.



A skąd na liście dostępnych serwerów wziął się Geronimo? Czyżby nowy Eclipse standardowo wspierał ten serwer aplikacyjny? Niestety nie, trzeba zainstalować odpowiedni adapter oraz osobno sam serwer. W sukurs przychodzi bardzo fajna funkcjonalność wyszukiwania i instalowania tego typu rozszerzeń. Co prawda nie jest to nowość Ganymede, ale ja odkryłem ją przy okazji pisania tego artykułu więc muszę wspomnieć. Wybieramy opcję ‘New -> Server’ i w oknie dialogowym klikamy na link ‘Download additional server adapters’. Eclipse przeszukuje bliżej nie określone zdalne repozytoria i po chwili widzimy listę dostępnych adapterów. W chwili pisania tego artykułu są to różne wersje Geronimo i WAS’a CE oraz GlassFish, Jetty a nawet WLS.



Wracając to tematu – póki co z JAX-WS nam się nie udaje, ale bynajmniej nie planuje się jeszcze poddać w tym momencie. Postanowiłem wypróbować nowy mechanizm pobierania uaktualnień i wtyczek (ang. plugin) w nadziei, że może tam znajdę coś ciekawego. Wybieram więc opcję ‘Help -> Software Updates’ a tu niemiła niespodzianka – nie działa. Jestem zdziwiony nie na żarty – nie spodziewałem się polec tak szybko.



Pierwsze co znalazłem, to że jest to znany błąd o numerze 224658. Niestety, błędu nie usunięto, ale sprawę uznano za zamkniętą. Zadowolono się podaniem jakiegoś wyjaśnienia, dlaczego błąd ten nie zostanie naprawiony. Zainteresowanych odsyłam do Bugzilli i lektury dyskusji pod wspomnianym zgłoszeniem błędu. Panowie od Eclipse’a – gratuluję poczucia humoru! Problemu nie udało się rozwiązać w ciągu 20 minut, więc na tym kończę. Co do wsparcia dla JAX-WS również zawód – trzeba jeszcze dużo zrobić, żeby dogonić NetBeans’a a tymczasem zupełnie nic się w tym temacie nie dzieje.

Coś mi się zdaje, że nie wygram głównej nagrody w konkursie Eclipse’a:) Chyba nie zrobiłem im za dobrej reklamy. Ciekaw jestem, czy dostanę koszulkę, która jak napisano przysługuje każdemu… no chyba, że wcześniej wyczerpią się zapasy.

8 czerwca 2008

SCJP - Bloki inicjalizacyjne

Dziś będzie o blokach inicjalizacyjnych (ang. initialization blocks). Kolejny kroczek w ciągnących się przygotowaniach do SCJP zapowiedzianych w artykule "Przygotowania do SCJP czas zacząć".

Bloki inicjalizacyjne występują w dwu rodzajach: jako bloki instancyjne i bloki statyczne. Blok inicjalizacyjny to fragment kodu, który wykonuje się – w przypadku bloków statycznych – gdy ładowana jest klasa, bądź – w przypadku bloków instancyjnych – gdy tworzona jest instancja. Zerknijmy na poniższy przykład.

public class Test {
public Test() {
System.out.println("constructor");
}

static {
System.out.println("static init block");
}

{
System.out.println("instance init block");
}

public static void main(String[] args) {
System.out.println("start main");

Test test = new Test();

System.out.println("end main");
}
}

Kod znajdujący się między konstruktorem a metodą ‘main(…)’ to właśnie bloki inicjalizacyjne. Ten oznaczony słówkiem kluczowym ‘static’ to blok statyczny. Ten drugi to blok instancji. Bloki statyczne – jak już powiedzieliśmy – wykonywane są w momencie ładowania klasy a więc zawsze jest to kod, który wykona się jako pierwszy. O blokach instancyjnych powiedzieliśmy sobie, że wykonują się w momencie tworzenia instancji, ale w momencie tworzenia instancji klasy wykonują się też konstruktory. Jaka jest zatem kolejność? Wpierw konstruktor czy blok inicjalizacyjny? Otóż ani to, ani to. Jak pisałem w artykule "SCJP - Konstruktory i inicjalizacja" pierwszą instrukcją konstruktora jest zawsze wywołanie konstruktora nadklasy albo innego konstruktora tej samej klasy. Właśnie zaraz po tym wywołaniu – przed wykonaniem jakiejkolwiek innej instrukcji konstruktora – wykonywany jest kod bloków inicjalizacyjnych instancji. Efekt uruchomienia powyższego programu będzie zatem taki:

static init block
start main
instance init block
constructor
end main

W poprzednim akapicie pisałem o blokach inicjalizacyjnych w liczbie mnogiej i bynajmniej nie był to mój błąd. W każdej klasie może występować dowolna ilość bloków inicjalizacyjnych. O kolejności ich wykonania decyduje kolejność w jakiej są one zapisane, patrząc od góry do dołu. Spójrzmy na kolejny przykład.

class SuperClass {
public SuperClass() {
System.out.println("superclass constructor");
}

{
System.out.println("superclass instance init block");
}
}

public class Test extends SuperClass {
public Test() {
System.out.println("constructor");
}

static {
System.out.println("static init block");
}

{
System.out.println("instance init block #1");
}

{
System.out.println("instance init block #2");
}

public static void main(String[] args) {
Test test = new Test();
}
}

Jak widzimy pojawiła się większa ilość bloków inicjalizacyjnych oraz nadklasa. Przekonajmy się, że kolejność wywoływania kodu bloków inicjalizacyjnych versus konstruktory jest taka właśnie jak to opisałem. Uruchomienie tego programu spowoduje wypisanie następującej sekwencji.

static init block
superclass instance init block
superclass constructor
instance init block #1
instance init block #2
constructor

A co się stanie, jeśli kod bloku inicjalizującego spowoduje wyjątek, np. odwołamy się do niewłaściwego indeksu tablicy? Naturalnie wyjątek zostanie zwrócony… w postaci opakowanej w java.lang.ExceptionInInitializerError.

5 czerwca 2008

Książka o EJB 3.0 autorstwa Jacka Laskowskiego

Właśnie ukazała się książka "WebSphere Application Server Version 6.1 Feature Pack for EJB 3.0", której jednym z czterech autorów jest Jacek Laskowski. Zachęcam do pobrania i lektury. Książka zawiera masę ultra-aktualnej wiedzy - same nowinki, w tym:

  • wprowadzenie do EJB 3.0
  • wprowadzenie do JPA
  • MDB i JMS
  • WebServices w oparciu o EJB 3.0
  • testowanie EJB 3.0
  • bezpieczeństwo, transakcje
  • specyfika WAS'a i RAD'a w kontekście EJB 3.0

4 czerwca 2008

Implementacja zaślepek serwisów danych z użyciem Spring’a

Wyobraźmy sobie, że implementujemy pewien system, którego elementem są serwisy danych. Zanim jednak zaimplementujemy te serwisy porządnie, chcielibyśmy mieć jakieś zaślepki (ang. stubs), chociażby po to, aby umożliwić innym członkom zespołu implementację i testowanie innych komponentów. W zasadzie chyba nie trzeba sobie tego wyobrażać, jest to częsty scenariusz, więc wystarczy sobie przypomnieć.

Implementujemy więc klasę, która bazuje na danych obecnych w pewnej kolekcji, np. w Map’ie. Pozostaje jeszcze jeden problem – jak wypełnić tą kolekcję danymi i się przy tym nie napracować. Załóżmy dla ustalenia uwagi, że nasz serwis wygląda tak jak pokazano poniżej.

public class SomeDataService implements DataServiceInterface {

private Map<String, Map<String, List<MyData>>> dataStore;

public void setDataStore(Map<String, Map<String, List<MyData>>> dataStore) {
this. dataStore = dataStore;
}

// implementacja operacji na powyższej strukturze danych
}

public class MyData {

private String dataSetName;

// kolejne zmienne i operacje set/get
}

To, czego zwykłem aż do tej pory używać w takich okolicznościach to biblioteka XStream, o której pisałem w artykule "Serializacja do XML i deserializacja przy użyciu XStream". Pare dni temu jednak coś mnie tknęło. Myślę sobie – używam przecież i tak Spring’a, a ten ma możliwość inicjalizacji zmiennych. Kilka chwil później czytam już dokumentację i widzę, że rzeczywiście, możliwości są duże i jak najbardziej da się inicjalizować także kolekcje. Poniżej fragment konfiguracji Spring’a, pokazujący jak to zrobić dla naszego przypadku.

<bean id="dataService" class="pl.package.SomeDataService">
<property name="dataStore">
<map>
<entry key="keyA">
<map>
<entry key="subkeyA">
<list />
</entry>
<entry key="subkeyB">
<list>
<bean class="pl.package.MyData">
<property name="dataSetName" value="setA" />
</bean>
</list>
</entry>
</map>
</entry>
</map>
</property>
</bean>

SCJP - Inicjalizacja tablic

W artykule "SCJP – Obiekty typu tablicowego" rozpocząłem temat tablic; dziś będzie kontynuacja. Będzie o składni, która pozwala na wygodną inicjalizację tablic.

Jak już sobie powiedzieliśmy tablice w języku Java są obiektami. Gdy potrzebujemy zmiennej typu prostego wystarczy, że zadeklarujemy taką zmienną i przypiszemy jej pewną wartość. Gdy potrzebujemy obiektu deklarujemy zmienną referencyjną, tworzymy ten obiekt i przypisujemy referencję do wspomnianej zmiennej. Jeśli tym obiektem jest tablica to dodatkowo chcielibyśmy przypisać pewne wartości elementom tej tablicy. Zerknijmy wpierw na poniższy kod, który deklaruje, tworzy i inicjalizuje tablicę "na piechotę".

public static void main(String[] args) {
int[] myTable = new int[4];

myTable[0] = 1;
myTable[1] = 2;
myTable[2] = 3;
myTable[3] = 4;
}

W pierwszej linii kodu deklarujemy zmienną referencyjną o nazwie ‘myTable’ zdolną do przechowywania referencji do obiektów typu ‘int[]’. Tworzymy także obiekt tablicy, który przechowuje cztery zmienne typu ‘int’. Referencję tego obiektu przypisujemy do zadeklarowanej zmiennej. Elementy tablic zawsze inicjowane są domyślnymi wartościami, tak więc wszystkie cztery elementy tablicy będą miały wartość ‘0’. Nie tego chcemy, więc w kolejnych liniach kodu przypisujemy inne wartości. Mamy co prawda to, czego chcieliśmy, ale powyższy sposób nie jest specjalnie wygodny – da się to samo zrobić dużo zgrabniej. Popatrzmy na kolejny przykład, który robi dokładnie to samo tyle, że z użyciem specjalnej składni inicjalizacji tablic.

public static void main(String[] args) {
int[] myTable = {1, 2, 3, 4};
}

Do zmiennej ‘myTable’ przypisujemy zatem wartość wyrażenia ‘{1, 2, 3, 4}’. Ponieważ jesteśmy w kontekście inicjalizacji zmiennej referencyjnej typu tablicowego kompilator wie, że chodzi o utworzenie tablicy. Po typie zmiennej referencyjnej rozpoznaje też, że chodzi o tablicę zmiennych typu ‘int’. Jedyne, czego jeszcze potrzebuje do utworzenia obiektu tablicy to rozmiar, ale to wiadomo na podstawie ilości wartości podanych w nawiasach klamrowych. Elementy tablicy inicjalizowane są właśnie tymi wartościami. Jak napisałem na początku tego akapitu kompilator wie, że chodzi o utworzenie tablicy ponieważ jesteśmy w kontekście inicjalizacji zmiennej referencyjnej typu tablicowego. W ogólności, nawiasy klamrowe obejmujące listę wartości nie są instrukcją konstrukcji tablicy. Z tego powodu język Java udostępnia jeszcze jedną konstrukcję, której znaczenie nie jest już zależne od kontekstu. Przykład poniżej.

public class Test {
public static void main(String[] args) {
printArray(new int[] {1, 2, 3, 4});
}

public static void printArray(int[] someArr) {
for(int x : someArr)
System.out.println(x);
}
}

Zerknijmy na wywołanie metody ‘printArray(…)’ w metodzie ‘main(…)’. Jak widać argumentem metody ‘printArray(…)’ jest tablica. Wywołując tą metodę nie przekazujemy jednak referencji do uprzednio utworzonej tablicy, przeciwnie, tablicę tworzymy w momencie wywołania metody (naturalnie tworzona jest ona tuż przed, ale ja mówię o zapisie). Zwróćmy uwagę, że tworząc tablicę w ten sposób nie podajemy explicite jej wymiaru – nie ma w nawiasach kwadratowych żadnej liczby. Rozmiar tablicy jest dedukowany na podstawie liczby elementów wymienionych między nawiasami klamrowymi. Próba określenia wprost wymiaru tablicy kończy się błędem kompilacji. Za pomocą analogicznych składni możemy także inicjalizować tablice wielowymiarowe. Zerknijmy na poniższy przykład.

public static void main(String[] args) {
byte[][] myTable = { { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 } };

for (byte[] table : myTable) {
for (byte x : table)
System.out.print(x + " ");

System.out.println();
}
}

W powyższym przykładzie tablica ‘myTable’ jest tablicą dwuwymiarową – mówiąc precyzyjniej tablicą tablic. Zawiera ona trzy tablice: { 1, 2 }, { 1, 2, 3 } oraz { 1, 2, 3, 4 }. Efektem uruchomienia programu będzie mozaika liczb pokazana poniżej.

1 2
1 2 3
1 2 3 4

Naturalnie, w analogiczny sposób możemy inicjalizować tablice obiektów. W szczególności, obiektami tymi mogą być tablice, choć oczywiście nie muszą. Poniżej przykład.

public class Test {
public static void main(String[] args) {
Number[][] myTable = { new Byte[] { 1, 2 }, new Integer[] { 1, 2, 3 }, {1, 2} };

for (Number[] table : myTable) {
for (Number x : table)
System.out.print(x + " ");

System.out.println();
}
}
}

A na koniec zagadka – zachęcam do zmierzania się z nią, jeśli chcesz mieć pewność, że w pełni rozumiesz temat tablic. Poniżej przedstawiono cztery deklaracje, druga jest błędna (nie kompiluje się) a pozostałe poprawne. Pytanie jest proste – dlaczego?

Object[] tableA = { new String[] { "some text" } };

Object[] tableB = { { "some text" } }; // błąd!

Object[][] tableC = { new String[] { "some text" } };

Object[][] tableD = { { "some text" } };

1 czerwca 2008

Foto-relacja z konferencji JAVArsovia 2008

JAVArsovia 2008 już za nami. Dziękujemy naszym sponsorom: sponsorowi głównemu, firmie Sun Microsystems; firmom: Google, IBM, e-point, HP, Javatech, Javart i JBoss. Dziękujemy naszemu patronowi – wydziałowi MIM UW. Dziękujemy władzom wydziału Biologii UW za udostępnienie komfortowych sal wykładowych. A poniżej my, organizatorzy.



Dziękujemy prelegentom – to w końcu o to chodziło, aby usłyszeć coś ciekawego o technologiach. Dziękujemy uczestnikom konferencji… a było to nie bagatela blisko 210 osób. Z organizatorami, prelegentami i gośćmi specjalnymi – liderami zaprzyjaźnionych JUG’ów może nawet i 230.



Widownia jak widać dopisała. Sale były zajęte do ostatniego miejsca. Niektóre prelekcje przyciągnęły nawet więcej chętnych niż sala była w stanie pomieścić. Ale to nikogo nie zniechęcało, tylko jeszcze bardziej podgrzało atmosferę.



Dla każdego znalazło się coś miłego, każdy mógł wybrać interesujący go temat – prelekcje odbywały się równolegle w trzech aulach, z czego największa mogła pomieścić aż do 300 osób i bynajmniej nie świeciła pustkami.



A w przerwach posilaliśmy się smakowitymi przekąskami – organizatorzy jako pierwsi przetestowali walory smakowe potraw.



Bardzo ciekawie było na panelu dyskusyjnym dotyczącym przyszłości i rozwoju społeczności polskiej skupionej wokół Javy. Przeważnie na serio, ale humory dopisywały.



A na koniec wielka impreza integracyjna – zwarty, 80-cio osobowy peleton szturmujący pub nieco przeraził panów ochroniarzy ale wszystko się wyjaśniło i już po chwili raczyliśmy się piwkiem i grillem.