Stronnice Chlebika – Newbie Java Blog

wrzesień 22, 2009

Thinking in Java

Zaszufladkowany do: Java, Recenzje — chlebik @ 8:39 am

thija4

Ostatnimi czasy odnoszę wrażenie, że ilość dyskusji typu “Czy uczyć się Javy, a jeśli tak to z czego?” jakby wzrosła. Do tego w każdej z nich co i rusz wspomina się o książce Bruce’a Eckela . Jednakże mam wrażenie, że jakoś żaden z dyskutantów nie zwraca uwagi na podstawową cechę tej książki – nie jest ona przeznaczona dla początkujących!

Przyznam szczerze, że sam uległem temu przekonaniu, gdyż Thinking in Java była moją pierwszą książką o Javie. Je też lubię posiadać cegłę, która zdaje się skrywać odpowiedzi na wszystkie pytania, którą można też podprzeć regał kiedy się przekrzywi :)

Problem polega na tym, iż Java sama z siebie jest językiem, w którym próg wejścia jest o niebo wyższy, niż w przypadku choćby języków skryptowych (choć może powinienem powiedzieć, iż dotyczy to bardziej JEE). W PHP, na którym się trochę znam, wystarczy zainstalować WAMPa ( jedna paczka instalacyjna, domyślne ustawienia ), stworzyć plik index.php oraz wpisać przysłowiowe:

echo "dupa";

I viola – “Mamo, tato, umiem programować!”. W przypadku Javy (rozwiązań webowych) bez solidnego IDE i kreatora projektów zajmie to naprawdę sporo czasu. I tutaj tkwi problem – w porównaniu do wspomnianego PHP po przejściu 200-300 stron z książki Eckela wiemy, że gdzieś dzwoni, ale nie do końca w którym to kościele. Natomiast po 300 stronach książki o “PHP&MySQL&Apache&CSS&JS” można na szybko stworzyć znośną stronę, którą można pokazać klasie w szkole, czy wrzucić jako stronkę produktu na aukcji Allegro.

Eckel o tym doskonale wie i nie próbuje iść pod prąd. Poza samym Swingiem, Flexem oraz bodajże SWT nie porusza zagadnień dodatkowych frameworków czy bibliotek. Pozostaje w cichym i ciepłym bagienku Javy SE i nie chce niczego więcej ( choćby bajeranckiego interfejsu we Flexie). I tutaj właśnie tkwi siła tej książki! Siła, którą odkryłem podczas przygotowań do SCJP – czytanie kolejnych rozdziałów z interesujących mnie tematów było wręcz… zabawą? Kiedy osiągnie się pewien poziom znajomości języka Thinking in Java zaczyna nabierać o wiele większego uroku – przestaje być opasłą knigą pełną kodu pisanego drobnym druczkiem, ale składnicą wiedzy, do której każdy powinien zaglądać. Taką biblią dla programisty – może nie trzeba jej czytać codziennie – jednakże dobrze przejrzeć od czasu do czasu. Zwłaszcza kiedy się czegoś nie wie.

lipiec 25, 2009

ProgramBash czyli o programowaniu na wesoło

Zaszufladkowany do: Java, JavaServer Faces, ProgramBash — chlebik @ 12:58 am

Przed urlopem umieściłem informację o nowym projekcie, który zamierzam zrealizować w ramach nauki Javy. Padło na JSF, Hibernate i RichFaces. Powstała kategoria ProgramBash, która jest jednocześnie nazwą aplikacji. A o co w niej konkretnie chodzi? Wszyscy znają portale takie jak http://bash.org.pl, czy http://demotywatory.pl – ja z kolei wpadłem na pomysł by użyć podobnego konceptu do publikacji śmiesznych/ciekawych fragmentów kodu, albo też sytuacji z życia zespołów programistycznych. Nie jest to zbyt skomplikowane, ale na pewno jest dobrym pomysłem na “edukacyjną” aplikację.

Oczywiście jak zawsze trzeba wyjść od wyglądu. Ja tak jakoś mam, że w projektach, które tworzę na bloga lubię pracować od razu na gotowym szablonie graficznym – od samego początku mogę publikować ładne screeny i mam większe pojęcie o tym jak ostatecznie będzie wyglądać aplikacja. Tym razem wybrałem ciekawy layout, który pobrałem ze strony free-css-templates.com o nazwie redbusiness. Mając zaś gotowy layout pora na przemyślenie ogólnej koncepcji serwisu. Generalnie na pewno będzie możliwość dodawania wpisów (odkrywcze), ich kategoryzacji, przeglądania według różnych kryteriów oraz oczywiście oceniania. Zasadniczo nie wyjdzie to w jakiś znaczący sposób poza CRUDa, choć może zerknę edukacyjnie w stronę webservice. Dla własnego rozwoju rzecz jasna.

Pierwotnie celem tego wpisu było stworzenie krok po kroku funkcjonującej strony głównej, skonfigurowanie Hibernate w stopniu umożliwiającym korzystanie z danych zawartych w bazie, a także zrobienie czegoś ładnego z RichFaces. Jednakże jestem przeciwny publikacji kolubryn na kilkadziesiąt stron, zatem ten wpis na razie zostanie poświęcony tylko ogarnięciu widoku i layoutu, a także zapoznanie się z pakietem Apache Tiles.

Operować zaczniemy na przykładzie, który już wcześniej stworzyłem. Jak już pisałem wcześniej JSF ma to do siebie, że w czystej postaci niewiele jest w stanie zdziałać. Dotyczy to również domyślnych metod renderowania widoku i ogarnięcia czegoś tak prozaicznego jak layout. W mojej książce mądrzy ludzie napisali, iż mamy w tej materii do wyboru dwie drogi. Pierwsza z nich zakłada tworzenie kodu w formie old-JSP, a mi brzydko się ona kojarzy z instrukcją require_once z PHP. W sumie i nic dziwnego, gdyż metoda ta zakłada stworzenie oddzielnych widoków – np. górnej belki, menu, czy stopki. Zaś następnie należy takowe importować (poprzez używanie znacznika c:import z biblioteki JSTL). Nie jest to dobre rozwiązanie.

Drugie podejście wiąże się z użyciem pakietu Apache Tiles. Cały ten pakiet służy właśnie temu, aby przede wszystkim za pomocą tzw. “kafelków” (stąd nazwa), zarządzać widokami w aplikacji javowej. Należy odwiedzić oficjalną stronę pakietu i ściągnąć najnowszą wersję – 2.3.1. Następnie wypakować wszystkie pliki JAR z archiwum (z głównego katalogu oraz podkatalogu /lib) i wrzucić do katalogu WEB-INF/lib w naszej aplikacji. Tak twierdzi dokumentacja. Następnie trzeba poinformować naszą aplikację (poprzez plik web.xml) o serwlecie, który będzie nam potrzebny. Do tego pliku dopisujemy taki kod:

<servlet>
<servlet-name>tiles</servlet-name>
<servlet-class>org.apache.tiles.web.startup.TilesServlet</servlet-class>
<init-param>
<param-name>
 org.apache.tiles.definition.DefinitionsFactory.DEFINITIONS_CONFIG
</param-name>
<param-value>
 /WEB-INF/tiles-defs.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

Run aplikacji… ZONK. Oczywiście krzyk, że nie potrafi odnaleźć takiej klasy jak org.apache.tiles.web.startup.TilesServlet. PPM na elemencie library w projekcie i dodanie ww. plików JAR załatwiło sprawę. Apache Tiles wstępnie skonfigurowane.

Zabawa z konfiguracją kafli dopiero się zaczyna. Pomysł generalnie jest taki – tworzymy na razie jeden plik z layoutem przeznaczony dla użytkowników niezalogowanych w serwisie. Taki plik layoutu będzie nam renderował górne menu, główną zawartość konkretnej strony oraz stopkę. Nad wyhaczeniem co z czym spędziłem chyba łącznie 3 godziny. Moja książka do JSF jedno, dokumentacja co innego, a praktyka pokazała jeszcze co innego. Masakra. Generalnie jak widze najlepszym przyjacielem jest NetBeans, który podpowiada co w danej sytuacji/tagu można wpisać – podpowiedzi z definicji działają. Koniec gadania – oto co trzeba zrobić:

  • XML – czyli musimy dopisać odpowiednie zapisy konfiguracyjne w pliku WEB-INF/tiles-defs.xml
  • wrzucić CSS oraz obrazki do niego
  • zaimplementować wygląd kafelka w formie pliku(ów) JSP
  • wrzucić wygląd do konkretnej strony JSP, którą chcemy wyświetlić

Jak wygląda layout już wiadomo. Plik konfiguracyjny powinien zatem wyglądać mniej więcej tak:

<tiles-definitions>
<definition name="guestLayout" template="/WEB-INF/layout/guestLayout.jsp">
<put-attribute name="upperMenu" value="/WEB-INF/layout/upperMenuTemplate.jsp" />
<put-attribute name="content" />
<put-attribute name="footer" value="/WEB-INF/layout/footerTemplate.jsp" />
</definition>
</tiles-definitions>

W tym kroku tworzymy definicję – czyli określamy pewien abstrakt widoku, który otrzymuje stosowną nazwę, wiążemy go z konkretnym widokiem istniejącym gdzieś w formie pliku JSP, a także nadajemy mu atrybuty, którym również przypisujemy nazwy i wartości (kolejne odwołania do plików JSP). Uwaga! Nalezy zwrócić uwagę, że wszystkie ścieżki podajemy bezwzględnie (zaczynamy je znakiem ‘/’), a korzeniem jest folder web. Może pojawić się pytanie – dlaczego elementy layoutu umieszczamy w katalogu WEB-INF? To dość proste – by nie można było się do nich dostać pojedynczo poprzez przeglądarkę.

Stworzyłem plik guestLayout.jsp. Pytanie kolejne! Jakie ścieżki wpisać w sekcji HEAD naszej strony layoutu? Choćby by dobrać się do plików CSS lub JSa? Ano i tutaj mała niespodzianka – podajemy ścieżkę względem katalogu web, jednakże nie traktując go jako korzenia. Oto jak to wygląda u mnie:

	<link rel="stylesheet" type="text/css" href="css/style.css" media="screen" />

Rzecz jasna utworzyłem sobie folder css, aby nie wprowadzać niepotrzebnego bałaganu. CSS na pewno mamy już podpięty, ale co w takim razie zrobić z grafiką w projekcie? Obrazki z layoutu wrzuciłem również do katalogu web do utworzonego specjalnie w tym celu podkatalogu images. By jednakże były one przez nasz plik CSSa widoczne, musimy ich adresowanie z poziomu reguł CSSa potraktować relatywnie. Czyli wygląda to choćby tak:

#wrap #menu li { line-height: 26px; background: #F6F6F6 url(../images/li-line.gif) no-repeat bottom left;}

Uporawszy się z tymi zagadnieniami pora spojrzeć na plik guestLayout.jsp. U mnie wygląda on tak:

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>

<%@taglib uri="http://richfaces.org/a4j" prefix="a4j"%>
<%@taglib uri="http://richfaces.org/rich" prefix="rich"%>

<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<f:view>
<html>
    <head>
        <title>ProgramBash</title>
        <meta http-equiv="Content-Language" content="Polish" />
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="author" content="Michał 'Chlebik' Piotrowski" />
        <meta name="Robots" content="index,follow" />
        <link rel="stylesheet" type="text/css" href="css/style.css" media="screen" />
    </head>

    <body>

        <tiles:insertAttribute name="upperMenu" />

        <div class="content">

            <div id="main">

                <div id="left_side">

                    <div class="rs">
                         <tiles:insertAttribute name="content" />
                    </div>
                </div>

                <tiles:insertAttribute name="footer" />

            </div>
    </body>
</html>
</f:view>

Nie ma tu niczego odkrywczego z wyjątkiem elementów <tiles: >. Ano służą one w tym miejscu do “wrzucenia” pewnych konkretnych atrybutów – czyli w naszym przypadku po prostu kolejnych widoków, które znajdują się w plikach JSP. Atrybuty o nazwach upperMenu oraz footer zostały przyporządkowane do plików JSP w deklaracji definicji widoku w pliku tiles-defs.xml. Jednakże pozostawiliśmy tam wolne miejsce dla atrybutu content, tzn. nie przypisaliśmy mu żadnego widoku. I tak miało być! Albowiem ten atrybut będzie reprezentował bieżący widok, który jest różny dla każdego requestu (no może nie każdego, większości).

Docelowo operowałem na pliku welcomeJSF.jsp, który leży sobie od samego początku w katalogu web. Wystarczyło przeredagować go na taką formę:

<%@taglib uri="http://richfaces.org/a4j" prefix="a4j"%>
<%@taglib uri="http://richfaces.org/rich" prefix="rich"%>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<tiles:insertDefinition name="guestLayout" flush="true">
<tiles:putAttribute name="content" value="/glowna/content.jsp" />
</tiles:insertDefinition>

I przy posiadaniu takiej struktury plików:

strukturaProjektuJSF

Ostatecznie mym oczom ukazał się taki jakże miły każdemu sercu widok:

glownaProgramBash

To tyle na dziś. Wygląda to bardzo łatwo i skromnie, ale przyznaję, że trochę czasu nad rozkminianiem tego spędziłem. Zadziwiająco mało momentami Google wie na temat JSF i ewentualnych jego bugów czy współpracy z innymi technologiami. Oby się to zmieniło.

lipiec 7, 2009

SCJP, podejście dziesiąte

Zaszufladkowany do: Java, Life, SCJP — chlebik @ 11:23 pm

Jak zwykle po powrocie z urlopu nic się nie chce. Czas w pracy dłuży się niemiłosiernie, do tego ta zwariowana od kilku tygodni pogoda nie polepsza nastroju. Zbierałem się trzy dni zanim postanowiłem znów zawalczyć z tematyką SCJP. Choć to i dobrze, gdyż oto podejście dziesiąte i jak na razie koniec. Ostatni rozdział w podręczniku poświęcony kompilatorowi miałem okazję przerobić już wcześniej. Popełniłem wówczas kilka błędów i temat zostawiłem do późniejszy. Jednakże teraz po powrocie z urlopu ponownie zapoznałem się z zawartością tych kilkunastu stron i tym razem test poszedł o niebo lepiej.

Mimo to należy do pytań dotyczących kompilacji podchodzić ostrożnie, podobnie jak do pytań dotyczących operatorów. Pozornie łatwe potrafią tak naprawdę bardzo łatwo wywieść na manowce. Zwłaszcza dlatego, iż obecnie raczej całą sprawę z kompilacją, classpathem i innymi tego typu rzeczami załatwia nam IDE, zatem raczej nikt nie jest za pan brat z dyrektywami kompilatora czy zabawą z szukaniem klas (choć może się mylę?). Oto lista kilku rzeczy, na które warto zwrócić uwagę:

  • importy statyczne – oczywiście koniecznym jest ich użycie za pomocą słów kluczowych static import (w takiej kolejności). Jednakże mniej oczywistym zapisem jest to, iż możemy importować w ten sposób nawet pojedyncze stałe i metody.
  • asercje – było o nich w rozdziale piątym, ale nie zaznaczyłem tam rzeczy najistotniejszej. Otóż nalezy pamiętać, że asercje zostały wprowadzone już w wersji 1.4! I dlatego też wywoływanie kompilatora i wirtualnej maszyny w ten sposób:
    javac -source 1.4 plik.java
    java -ea plik
    

    Spowoduje, że kod, w którym występują niespełnione asercje (zwracające wartość false) spowoduje wygenerowanie błędu podczas wykonania programu (czyli po prostu asercje będą działały). Wykonania, nie kompilacji! Powtórzmy – błędy wykonania (run), to co innego niż błędy kompilacji (compile).

  • classpath – czego tak naprawdę dotyczy ta dyrektywa dla kompilatora/JVM? Otóż jej zadaniem jest głównie znalezienie wszystkich klas, których kompilowana/uruchamiana klasa będzie potrzebowała. To jest główne zadanie dla classpath. Pamiętać należy również o tym, iż w przypadku kompilacji (polecenie javac) podanej nazwy pliku do kompilacji poszukuje się domyślnie w bieżącym katalogu. W przypadku uruchamiania pliku tak nie jest! No i rzecz ostatnia – podanie wartości dla classpath powoduje nadpisanie zmiennej systemowej (o ile rzecz jasna istnieje)
  • pliki JAR – archiwa są dość proste do zrozumienia, co więcej, na egzaminie nie ma pytań dotyczących ich tworzenia i zarządzania. Natomiast na pewno trzeba wiedzieć, że po utworzeniu pliku JAR z konkretnego katalogu, nawet po dodaniu go do classpath do klas zawartych w archiwum należy odwoływać się w kodzie poprzez podanie pełnej nazwy klasy (łącznie z nazwą pliku JAR). Oto przykład:
    test |
    plik.uzywajacy.klasy.z.jara
    tutaj.utworzymy.plik.jar
    katalog.do.zjarowania |
    podkatalog1
    podkatalog2 |
    plik.java

    Odwołując się do pliku w archiwum JAR, które utworzyliśmy w katalogu test należy podawać pełną ścieżkę. A zatem nasz plik w katalogu test, w którym chcielibyśmy wykorzystać klasę z archiwum musi odwoływać się do niej poprzez zapis PLIK_JAR/katalog.do.zjarowania/podkatalog2/plik – pomimo dodania pliku JAR do classpath.

czerwiec 26, 2009

SCJP, podejście dziewiąte

Zaszufladkowany do: Java, SCJP — chlebik @ 9:50 am

Przedostatni rozdział podręcznika dotyczy zagadnienia równie rozbudowanego i skomplikowanego jak kolekcje. Konkretnie chodzi o wątki – rzecz, której za bardzo nie można się nauczyć “chałupniczo”, gdyż prawdziwą potęgę wielowątkowej aplikacji widać w… no… wielowątkowej aplikacji :) A takich raczej nikt nie klepie po godzinach ku radości własnej i pracy u podstaw.

Autorzy zaznaczają, że to, co przedstawili w tym rozdziale jest zaledwie wierzchołkiem góry lodowej jeśli chodzi o aplikacje wielowątkowe, zatem i nalezy podchodzić do zdobytej wiedzy ostrożnie. Jednakże i tak jest się czego nauczyć, jak i jest z czego stworzyć dziesiątki podchwytliwych pytań. Generalnie do pozytywnego uporania się z zadaniami potrzebne są dwie rzeczy – bliska znajomość z API wątków, konstruktorów oraz wywoływanych metod, wyrzucanych wyjątków i takich tam. Z drugiej zaś strony należy zrozumieć blokady (locki), aby wiedzieć kiedy i jaki wątek jest dostępny, uśpiony, ma możliwość odwołania się do wskazanego obiektu, czy co tam jeszcze w ogóle przyjdzie do głowy. Oto lista rzeczy, z którymi lepiej pozostawać w bliskiej komitywie.

  • usypianie wątku – o metodzie sleep trzeba wiedzieć dwie rzeczy. Raz – jest metodą statyczną klasy Thread. Dlatego też zawsze usypia działanie bieżącego wątku. Do tego wyrzuca ona InterruptedException, zatem musimy wywołanie metody zawrzeć w klauzuli try..catch, albo też przekazać obsługę wyjątku wyżej.
  • metoda join() – podobnie jak powyższa wyrzuca wyjątek InterruptedException. Nie jest jednakże metodą statyczą. Spójrzmy na taki kod:
    public static void main( String[] args ) {
    Thread t = new Thread( new Runnable() {
    public void run() {
        System.out.println( "Początek pętli" );
        for( double i = 0; i < 1000000000; i++ ) { }
        System.out.println( "Koniec pętli" );
    } } );
    
    System.out.println( "Chlebik 1:" );
    t.start();
    System.out.println( "Chlebik 2:" );
    try {
        t.join();
    } catch( Exception e ) { }
    System.out.println( "Chlebik 2:" );
    }
    

    Obecnie wykonywany wątek (ten z metody main) nie wyświetli napisu Chlebik 2: dopóki nie zostanie zakończone działanie wątku reprezentowanego przez obiekt t!

  • metoda wait() – odstaje troszeczkę od powyższego towarzystwa, gdyż jest ona właściwa dla wszystkich obiektów w Javie (pochodzi z klasy Object). Nie jest statyczna, zaś jej wywołanie nie powoduje wyrzucenia wyjątku. W parze z nią idą dwie inne metody z klasy Object – notify() i notifyAll(). Wszystkie są oznaczone jako final, zatem nie potrzeba w ich przypadku karkołomnych zabaw jak z np. metodą equals.

    Idąc dalej – metody te mogą być wywołane tylko i wyłącznie w kontekście synchronized! Próby użycia poza tymże kontekstem skutkują wyrzuceniem IllegalMonitorStateException (i to nie jest sprawdzalny wyjątek więc nie trzeba definiować jego łapania). Metody te służą do zarządzania blokadami obiektu (dlatego są elementami klasy Object). Metoda wait pozwala na wstrzymanie działania wątku, który posiada blokadę obiektu, aż do wywołania przez ten obiekt metody notify, albo notifyAll. Nie za bardzo podejmuję się więcej tłumaczyć to pisząc – myślę, że kod powie więcej (wzięty z podręcznika):

    class ThreadA {
         public static void main(String [] args) {
         ThreadB b = new ThreadB();
         b.start();
    
         synchronized(b) {
             try {
             System.out.println("Waiting for b to complete...");
             b.wait();
             } catch (InterruptedException e) {}
             System.out.println("Total is: " + b.total);
             }
         }
     }
    
     class ThreadB extends Thread {
     int total;
    
     public void run() {
         synchronized(this) {
             for(int i=0;i<100;i++) {
             total += i;
              }
         notify();
         }
      }
    
     }
    
  • kiedy blokada istnieje, a nie kiedy nie – to temat bardzo istotny. Oto cytat z Thinking in Java w wersji 4.

    Ważne jest, aby zrozumieć, że wywołanie metody sleep() nie zwalnia blokady obiektu, tak samo jak nie czyni tego wywołanie yield(). Z drugiej strony, wywołanie wait() zainicjowane w obrębie synchronizowanej metody wymusza zawieszenie wątku i zwolnienie blokady danego obiektu.

  • metoda getId() – na to się trochę wkurzyłem. Ni stąd, ni zowąd wyskoczyły mi pytania o tę metodę. I co? Jajco! (cytat ze znanego polskiego filmu). Może to i metoda, aby w pytaniach testowych poruszać zagadnienia, których nie było w konkretnym rozdziale. Ale to nie lepiej by było po prostu dać listę metod, o które potencjalnie jeszcze mogą paść pytania? Uczenie się całego API na pamięć to chyba nie jest cel egzaminacyjny? No nic, koniec narzekania – rzecz w metodzie getId().

    Dokumentacja mówi, że zwraca ona unikatowy identyfikator wątku (prymityw typu long). Spójrzmy na ten kod:

    
       // Obiekty a-a3 są tego samego typu jak ten poniżej
    
     Thread a4 = new Thread( new Runnable() {
           public void run() {
               for( int i = 0; i < 100000; i++ ) {
                  if( i == 99999 ) System.out.println('.' + Thread.currentThread().getId());
       }} } );
    
       System.out.println( Thread.currentThread().getId() );
        a.start(); System.out.println( a.getId() );
        a2.start(); System.out.println( a2.getId() );
        a3.start(); System.out.println( a3.getId() );
        a4.start(); System.out.println( a4.getId() );
    

    Oto efekt działania:
    1
    8
    9
    10
    11
    54
    55
    56
    57

    I tak raz za razem – kolejność cyfr oczywiście bywa różna (poza pierwszymi trzema-czterema) – wiadomo, nieokreśloność wywoływania wątków. Tak to wygląda. Dwie kwestie – jak widać wątek metody main() ZAWSZE (przynajmniej u mnie) ma numer 1. Pozostałe – w miarę równo i oczywiście idąc w górę. Pytania na testowym egzaminie dotyczyły ewentualnego wyniku na wyjściu programu podobnego do powyższego. Ciekawe, ale jednak to w wielu momentach jest po prostu loteria.

czerwiec 21, 2009

SCJP, podejście ósme

Zaszufladkowany do: Java, SCJP — chlebik @ 7:33 am

Wielkimi krokami zbliżam się do końca podręcznika. Jakoś nie czuję się o wiele mądrzejszy, ale to głupie odczucie i próbuję z nim walczyć. W końcu coś z tej wiedzy w głowie zostaje – im więcej w sumie tym lepiej – SCJP jednak tych kilka setek kosztuje. Lepiej nie podchodzić dwa razy.

Dziś na tapecie krótki rozdzialik o klasach wewnętrznych. Przyznam szczerze, że popełniłem w teście raptem 2 błędy zatem i pisać nie za bardzo mam o czym. Sam bowiem rozdział poświęcony temu zagadnieniu ma może 20 stron – naprawdę nie za wiele można na tylu stronach zmieścić. Jednakże oto lista:

  • dostęp – zasadniczo właśnie dostępności/widoczności poszczególnych klas czy metod dotyczy większość pytań. Tutaj napiszę tylko, że w przypadku method-local inner class ma ona nielimitowany dostęp do klasy otaczającej. Taki kod:

    public class Tester {
    final String lancuch = new String("Chlebik");
    public void pokazKlaseWewnetrzna()
    {
    class takaSobieKlasa {
    void hej()
    {
    System.out.println( lancuch );
    }
    }
    }
    }

    jest jak najbardziej w porządku i zadziała bez problemu.

  • pamiętaj o statykach – zarówno o tym, że metody niestatyczne nie mogą być wywoływane ze statycznych metod, a także, że statyczne zmienne trzymają się twardo i mają za nic tworzenie instancji klasy.
  • widoczność klas wewnętrznych – kod z pytania testowego:
    class A { void m() { System.out.println("outer"); } }
    public class TestInners {
    public static void main(String[] args) {
    new TestInners().go();
    }
    void go() {
    new A().m();
    class A { void m() { System.out.println("inner"); } }
    }
    class A { void m() { System.out.println("middle"); } }
    }

    Pytanie brzmi – co zostanie wyświetlone na wyjściu programu? Odpowiedź: middle. Dlaczego? Ano bo klasa wewnątrz metody jest zadeklarowana dopiero po wywołaniu (czyli po fragmencie new A().m();, w związku z czym jest niewidoczna. Klasa A, która jest w pierwszej linijce kodu jest “poziom wyżej” niż klasa dająca na wyjściu “middle” i dlatego też nie zostanie wykorzystana.

czerwiec 17, 2009

SCJP, podejście siódme

Zaszufladkowany do: Java, SCJP — chlebik @ 10:31 pm

Siódmy rozdział podręcznika do SCJP to kolekcje i generyki. Jest to zmora wszystkich programistów, zresztą nie dziwię się – sam rozdział ma koło 100 stron i do lekkich nie należy. Końcowy test wyszedł mi “dobrze-niedobrze” – zasadniczo większość odpowiedzi udzielałem “półpoprawnie”, czyli obejmowałem poprawną odpowiedź, ale też przy okazji wpisywałem czegoś za dużo lub za mało. Jednakże zawziąłem się i postanowiłem przerobić zagadnienie jeszcze raz dość gruntownie, posiłkując się innymi swoimi książkami – Blochem oraz Koffmanem przedstawię kilka rzeczy, które z pewnością się przydadzą. Ten wpis będzie inny i dłuższy niż poprzednie, ale jestem pewny, że jego lektura na pewno nikomu nie zaszkodzi.

Zacznijmy od podstawowej rzeczy, jaką jest wiedza dotycząca metod equals oraz hashCode. Equals służy do porównywania dwóch obiektów i sprawdzenia, czy tym samym są one sobie znaczeniowo równe.

String s1 = new String("chlebik");
String s2 = new String("chlebik");s1.equals(s2); // To daje TRUE

Oczywiście w przypadku klasy String o równości mówić jest łatwo. Podobnie jak w przypadku liczb – albo dwie zmienne reprezentują tę samą liczbę, albo i nie. Co natomiast zrobić w przypadku klas napisanych przez programistę? Ano trzeba by nadpisać tę metodę. Domyślnie w klasie Object metoda ta używa do porównania dwóch obiektów operatora ==, co nie jest raczej dobrym rozwiązaniem dla klas reprezentujących…hmmm…no nigdy nie jest dobre.

Przedstawię wpierw klasę, na których będziemy pracować:

class Bloger { }class WPBloger extends Bloger {
Integer age = 24;
String nick = new String("Chlebik");
}

Załóżmy, że chcemy sprawdzić równość dwóch obiektów klasy WPBloger. Co należy porównać by mieć pewność, że jedna i druga zmienna znaczeniowo reprezentuje ten sam obiekt? Ano jeśli zgadza się wiek, zgadza się nick, no to mamy identyczne obiekty. Super. Oto jak to zaimplementujemy:

public boolean equals( Object o ) {if( (o instanceof WPBloger) &&
( ((WPBloger)o).nick == this.nick ) &&
( ((WPBloger)o).age == this.age ) ) {
return true;
} else {
return false;
}
}

Oto o czym należy pamiętać:

  • parametr metody – musi być typu Object. I kropka.
  • sprawdzanie typu – owszem, parametr jest typu Object, ale jeśli chcemy porównywać nick czy wiek należy potraktować nasz parametr jako obiekt konkretnej klasy. Jednakże zanim to uczynimy, należy zabezpieczyć się przed ewentualnym błędem, jeśli do metody przekazano obiekt innej klasy. Stąd sprawdzenie w warunku instanceof.
  • rzutowanie parametru – po pomyślnym przejściu testu z operatorem instanceof możemy swobodnie (bez obawy o błędy) rzutować parametr na konkretną klasę, a następnie odwoływać się do jego pól/metod.
  • porównywanie wartości – dla zmiennych instancji będącymi obiektami, należy wywoływać ich metody equals. W przypadku typów prostych (prymitywów) rzecz jasna wystarcza operator ==. Wyjątek! W przypadku wartości float i double dobrze jest je wpierw przerobić na inne typy. Float na integer (metoda Float.floatToIntBits), zaś double na long (metoda Double.doubleToLongBits ). Jest to podyktowane tym, że w przypadku tych prymitywów mogą one przyjmować wartości takie jak Float.NaN i parę innych, co przy porównaniu może prowadzić do nieścisłości. Tablice porównujemy w całości, pole po polu (lub za pomocą Arrays.equals)

I to na razie tyle. Czasem warto też zapoznać się z kontraktem tej metody, jednakże jest to dość oczywiste i nie będę przepisywał manuala. Teraz przejdziemy do metody hashCode. Wpierw troche uśmiechu od autorów podręcznika:

For the exam you do not need to understand the deep details of how the collection classes that use hashing are implemented, but you do need to know which collections use them (but, um, they all have “hash” in the name so you should be good there)

Generalnie hashe są używane po to, aby polepszyć efektywność składowania i wyszukiwania elementów w kolekcjach. Im nasza klasa będzie generowała bardziej unikalne i różnorodne hashe tym lepiej (wyszukiwanie będzie szybsze). Wartość zwracana przez metodę hashCode to int! Autorzy podręcznika pokazali działanie haszowania na bardzo przystępnym przykładzie.

Załóżmy, że mamy klasę reprezentującą zawodników pewnej drużyny. Hashe są generowanie na takiej zasadzie, że bierzemy po kolei litery z imienia zawodnika i zamieniamy ich wartości na liczby w rosnącej kolejności:

Imię Haszowanie Wynik
Alex A(1)+L(12)+E(5)+X(24) 42
Bob B(2)+O(15)+B(2) 19
Dirk D(4)+I(9)+R(18)+K(11) 42

Jak widać dla imion Alex i Dirk zostaną wygenerowane te same wartości. I teraz na przykładzie koszyków z imionami – oba imiona zostaną włożone do tego samego koszyka. Gdybyśmy próbowali znaleźć imię w takim koszyku, wówczas musielibyśmy przetrząsnąć taki koszyk w poszukiwaniu odpowiedniego. To może zająć sporo czasu! W użyciu mamy wówczas metodę equals, która po kolei przejdze po imionach w koszyku w poszukiwaniu odpowiedniego. Hmmm, trochę to mało efektywne. Na przykładzie 2 imion to może tego nie widać, ale gdybyśmy w takim koszyku mieli owych imion 50, już zacząłby się narzut.

Co zatem zrobić? Najlepiej by było w ogóle zrezygnować z przeszukiwania koszyka. Jak to osiągnąć? Pisząc na tyle oryginalne metody hashCode, aby każde imię miało swój własny koszyk. Najlepiej tak przegrzebać w wartościach, aby były one dość oryginalne (haszowania). Dzięki temu wyszukiwanie naszych obiektów będzie z pewnością szybsze. Metoda hashCode również posiada swój kontrakt, ale podobnie jak przy equals nie zamierzam przepisywać manuala.

Dlaczego piszę o tych metodach? Ano dlatego, że kilka zadań jest w swej idei prosta – mamy dwie kolekcje, coś tam dodamy, coś odejmiemy i mamy linię, w którą możemy wsadzić jakiś kod. Jaki będzie wynik działania programu jeśli ta linia będzie bla bla bla bla. Generalnie bez znajomości działania powyższych metod oraz tego jak zachowają się dodawane elementy będzie dość trudno odpowiedzieć na tego typu pytania. Dla pamięci i na zakończenie tego wątku – wpierw lecimy po hashCode, dopiero potem wyszukiwanie obejmuje equals! (zatem należy uwzględnić sytuacje kiedy np. metoda hashCode w ogóle nie jest nadpisywana).

Jeśli chodzi o samo API kolekcji to raczej nie ma z nim większych problemów. Należy uważać na cztery rzeczy:

  • interfejsySet, Map i List są właśnie interfejsami. Można na nie rzutować, ale nie da się stworzyć ich instancji!.
  • różnice zawartości w zbiorachHashMap może posiadać jeden klucz będący wartością NULL, jak i również wiele wartości NULL jako zawartość, na którą klucze wskazują. Natomiast w przypadku Hashtable (patrz: różnica w wielkości liter) nie ma mowy o jakichkolwiek NULLAch.
  • TreeMap i TreeSet – to są ciekawe kolekcje, gdyż charakteryzują się posiadanym porządkiem i sortowaniem. Nie będę znowu przeklejał manuala( TreeMap i TreeSet ), ale z pewnością wypada zwrócić uwagę na metody, które obejmuje egzamin ( Key w znaczeniu nie parametru ale odmiany tej metody dla Map):
    • ceiling (Key)
    • higher (Key)
    • floor (Key)
    • lower( Key)

    najlepiej sobie wbić do głowy, co i kiedy zostanie zwrócone.

  • “backed collections” – czyli dziwne rozczłonkowanie kolekcji poprzez wywołanie metody subMap (wraz z jej przeciążonymi wersjami).

No i przyszedł czas na generyki. Wpierw trochę historii. Otóż obsługa generyków została dodana w Javie 1.5 i w związku z tym (albo przede wszystkim dlatego, że to była wersja 1.5) musiała w racjonalny sposób umożliwić działanie starego kodu, który był napisany bez istnienia czegoś takiego jak typy generyczne. Dlatego też całe to zagadnienie jest trochę “walnięte”, gdyż jest kompromisem pomiędzy nowoczesnością, a przenośnością kodu. No i dlatego mamy taki bajzel.

Po kilku słowach krytyki czas na konkrety. Standardowo w wersjach przed wprowadzeniem generyków istniała możliwość zrobienia czegoś takiego:

List lista = new ArrayList();lista.add(new Integer(23));
lista.add(new String("chlebik"));

I to nawet działało. Problemem było oczywiście porównywanie elementów, a także konieczność ciągłego rzutowania wyciąganych obiektów z listy (metody dostępowe zwracały Object). I głównie po to powstały generyki – by umożliwić kontrolę typów na poziomie samej struktury danych, bez konieczności dokonywania ciągłych rzutowań i obawy o ClassCastException. Gdyby zagadnienie kończyło się w tym miejscu, byłoby ono jednym z najłatwiejszych w certyfikacji SCJP. Oczywiście tak różowo nie jest.

Generalnie kod napisany w wersjach sprzed generyków daje spore szanse na działanie. Oto przykład:

public void zrobCos() {
List lista = new ArrayList();
lista.add(23); // zwroc uwage na autoboxing
lista.add(44);
List drugaLista = zrobCosZLista( lista );
}public List zrobCosZLista( List lista ) {
// dodajemy cos do listy i tak dalej
}

Jeżeli w metodzie zrobCosZLista grzecznie będzie istniał kod sprawdzający czy elementy listy są Integerami, wówczas problemu nie ma. Dodatkowe rzutowania nikomu nigdy nie zaszkodziły (poza pewnie narzutem na wydajności). Co jednakże się stanie jeśli postanowimy coś dodać do takiej listy? Ano mamy problem, gdyż oficjalnie dla kompilatora mamy doczynienia ze zwykłą listą, która może przyjmować dowolne wartości! Co z tego, że “piętro wyżej” nasz argument nie był zwykłą listą, ale listą z predefiniowanym typem?

public List zrobCosZLista( List lista ) {
lista.add( new Integer(29) );
}

Działa! Oj jak fajno. Zwracamy listę, ale z elegancko dodaną kolejną wartością Integer. Kompilator jest szczęśliwy, programista też. Co jednakże jeśli do listy dodamy np. łańcuch tekstowy?

public List zrobCosZLista( List lista ) {
lista.add( new String("chlebik") );
}

Dodać możemy, w końcu to zwykła lista. I co? Ano wszystko dobrze – taki kod skompiluje się i nawet uruchomi. Nie będzie błędów podczas działania programu!!!. Jedyną rzeczą będzie wygenerowanie ostrzeżenia przez kompilator (w poprzednim przypadku dodawania Integera takie ostrzeżenie również zostanie wygenerowane). Przy dodawaniu trefnego elementu do kolekcji nic się nie stanie! Problem najczęściej pojawia się wówczas, gdy próbujemy potraktować nasz element o typie String jako Integer. Co się dzieje w takiej sytuacji chyba nie muszę tłumaczyć.

Powyższe przykłady nie są tragiczne i da się je zrozumieć. Najgorszą rzeczą w przypadku generyków jest polimorfizm i różne jego odcienie. To na tym zagadnieniu polega większość zdających (no i oczywiście ja :). Problemu z polimorfizmem w przypadku “typu kolekcji” nie ma. Czyli możemy rzutować ArrayList na List i wszystko będzie działało. Co zaś z takim kodem (kalka z podręcznika)?

class Parent { }
class Child extends Parent { }
List<Parent> myList = new ArrayList<Child>();

Takie coś nie zadziała. Zadziała zaś z kolei zasada mówiąca o tym, że zadeklarowany typ generyczny jest jedynym typem, który dana kolekcja może przyjąć. Koniec kropka. Czyli w powyższym przypadku tylko Child, albo tylko Parent. Próby napisania innego kodu skończą się błędem kompilacji.
Podobna sytuacja ma również miejsce w momencie, kiedy np ArrayList jest przekazywana jako parametr do metody, która przyjmuje np: List. Nie, nie i jeszcze raz nie. Tak się nie da.Co zatem zrobić w takim przypadku? Zrezygnować z polimorfizmu się raczej nie da, pisanie przeciążonych metod dla każdej z podklas Parent też nie wchodzi w grę. Z pomocą w takich przypadkach przychodzi specjalny “operator” (tzw. wildcard) – <? extends Parent>. Znaczy on mniej więcej tyle – pozwalaj na przekazywanie obiektów, które rozszerzają podaną klasę (no i obiekty tej klasy też) i operowanie na takiej kolekcji. Obiecuję, że nie będę tam niczego wsadzał. Dzięki takiej konstrukcji znaczna część istniejącego już kodu mogła zadziałać – wystarczyło tylko nie wsadzać niczego do kolekcji w ten sposób przekazanej.

W tym miejscu wypada wskazać, że podobnie jak w kilku innych przypadkach w Javie – parametryzowanie w przypadku powyższym oznacza nie tylko klasę, ale również implementację interfejsu. Czyli taki kod jest jak najbardziej poprawny:

public zrobCos( List<? extends Runnable> ) {}

Istnieje też odwrotność tego zapisu. Jak już wspomniałem <? extends Class> dotyczy konkretnej klasy/interfejsu oraz jej dzieci/implementacji. A co jeśli chcemy rzutować w górę? Ależ proszę bardzo:

public zrobCos( List<? super Class>) {}

Taki zapis mówi kompilatorowi – przyjmij jako argument listę z obiektami typu Class i wszystkimi jego rodzicami. Dzięki temu mamy możliwość ograniczenia potencjalnych obiektów, które można przekazać jako parametr, ale z drugiej strony jesteśmy w stanie doprowadzić stary kod do stanu używalności z generykami.

Ostatnią rzeczą, na którą wypada zwrócić uwagę są deklaracje z użyciem generyków. Jak podkreślają sami autorzy podręcznika jest to dla generyków zastosowanie bardzo rzadkie, jednakże wypada się z nim zapoznać (bo w sumie tylko z takich zagadnień układają pytania :). Uogólnione bowiem typy możemy stosować w przypadku klas:

public class mojaKlasa<T extends jakasKlase> {
T[] tabela;
T innaZmienna;
// i reszta kodu klasy, gdzie pod T zostanie podłożona nazwa klasy
}

Albo też i metod:

public <T> int zwrocInt( T obiekt ) {
// owo T reprezentuje jeszcze nam nieznany typ obiektu
}

To tyle, co postanowiłem pokrótce przedstawić. Zagadnienie kolekcji jako takich jest olbrzymie – bo nie tylko rzecz w powyższej wiedzy – w ogóle bowiem nie dotknąłem zagadnienia efektywności kolekcji (jaką i do czego najlepiej wykorzystać), własnych implementacji, bardziej rozbudowanego API. Ale w tej serii postów skupiam się na zagadnieniach poruszanych na egzaminie – zainteresowani niech poszukają dla siebie odpowiedniej literatury :)

czerwiec 14, 2009

JavaBlackBelt – podejście pierwsze

Zaszufladkowany do: Java, Life, SCJP — chlebik @ 10:39 pm

Jestem po rozdziale z kolekcji w podręczniku SCJP i troszeczkę mnie on “przetrzepał”. Oj już wiem dlaczego ludzie tak klną na tę część egzaminu. Jednakże relacja z tego będzie miała formę dłuższego wpisu zatem na razie musicie uzbroić się w cierpliwość. Za to dla relaksu/odmiany/sprawdzenia/+15 do lansu zrobiłem dwa pierwsze egzaminy na JavaBlackBelt. No i oficjalnie mogę już używać żółtego pasa.

Chciałem od ręki zrobić pomarańczowy pas (jeden egzamin), ale popatrzyłem, że w wymaganiach wątki się pojawiają zatem poczekam chwilkę i podejdę po lekturze 9 rozdziału z podręcznika SCJP.

czerwiec 13, 2009

JSF i RichFaces – pierwsze starcie

Zaszufladkowany do: Java, JavaServer Faces, ProgramBash — chlebik @ 12:12 am

Za namową Darka Ludery postanowiłem troszeczkę jeszcze pogrzebać przy samym wyborze JSF. To, że jest to framework wręcz stworzony do rozszerzania o inne funkcjonalności już dowiedziałem się wcześniej. Zresztą w książce o JSF piszą o Apache Tiles, Strutsie i trochę o AJAXie. Darek w komentarzu do poprzedniego mojego wpisu zasugerował RichFaces. Zajrzałem, popatrzyłem i jest fajnie – JQuery UI dla Javy, ale tak na oko 6,5 raza lepszy.

No ale dość gadania. Wypadałoby zabrać się do jakieś roboty. Próbowałem stworzyć projekt na swoim starym dobrym NetBeansie w wersji 6.5, ale cosik mi się popieprzyło (nagle uruchomienie Tomcata wywalało błąd, że kompilator nie może odnaleźć klasy Object, wtf?). Przeinstalowałem zatem temat i na pokładzie mam ślicznego NetBeansa w wersji 6.5.1, GlassFisha w wersji jeszcze 2 (rozwojowa wersja 3 mnie póki co nie interesuje), no i przeinstalowałem Tomcata. Nie wiem czy zapamiętano wszystkie pluginy, które wcześniej miałem poinstalowane czy co, ale kwestia jest taka, że po upgradzie dalej sypało mi błędami. Jednakże może wpierw proces tworzenia projektu:

Tworzymy nowy projekt. File->New Project->Java Web->Web Application.

jsf_new_step1

Wybieramy nazwę dla projektu, lokalizację i takie tam:

jsf_new_step2

Środowisko uruchomieniowe:

jsf_new_step3

No i na końcu dorzucamy sobie biblioteki, które chcemy wykorzystać. Dla przypomnienia – Hibernate, JSF, RichFaces.

jsf_new_step4

Klikając na kolejne elementy listy na dole pojawiają się opcje konfiguracyjne. Ja tam ustawiłem wersję dla JSF (1.2), a także wybrałem połączenie MySQL. Zainstalowałem sobie lokalnie PostgreSQL. Jeśli na moim hostingu dałoby się toto również zainstalować, wówczas cała aplikacja będzie powstawała z tym systemem bazodanowym. Zawsze coś innego niż do tej pory (z PostgreSQL pracowałem ze dwa miesiące może w poprzedniej firmie). Pozostaje też pytanie o mój hosting. Podobnie jak z HowToJava byłoby dobrze móc pokazywać efekty swojej pracy szerokiej publice – jeśli da radę zainstalować na serwerze PostgreSQL to będzie w ogóle miło.

Na sam koniec wypada pokazać jak wygląda nasz nowo utworzony projekt:

jsf_new_step5

Wygląda niby ciekawie, ale jak już wspomniałem wcześniej – takie coś się nie uruchomi! (przynajmniej u mnie nie chciało). Przeorałem net i w końcu postanowiłem iść metodą drobnych kroczków.
Sypała błędami deklaracja taglibów w pliku JSP:

<%@taglib uri="http://richfaces.org/a4j" prefix="a4j"%>

Po usunięciu z kodu wszystko działa ślicznie pięknie, idzie deploy, Tomcat odpala się aż miło. Generalnie na necie znalazłem opis podobnego przypadku, ale kolesiowi wyrzucało problemy, gdyż dodatkowo miał wrzucony do WEB-INF/lib plik jar z biblioteką ajax4jsf. Ja nie miałem i w myśl tego tutoriala (oficjalnego) mieć nie powinienem. Jedyną zadeklarowaną biblioteką znaczników powinna być:

<%@taglib uri="http://richfaces.org/rich" prefix="rich"%>

Tylko to dotyczy tylko i wyłącznie RichFaces. No a gdzie AJAX mój kochany? Postanowiłem nie dać za wygraną i opdrążyć temat głębiej. Dlaczego bowiem domyślne odpalenie projektu skutkuje błędami? Aż taki bug? Rozumiem, że wsparcie dla RichFaces jest pluginem, no ale bez przesady. Wspomniana biblioteka ajax4jsf została zintegrowana z RichFaces już jakiś czas temu. Dziwne toto.

Zrobiłem tak. Usunąłem z libraries wpisy dotyczące RichFaces. Po czym ściągnąłem najnowszą wersję tego frameworka, rozpakowałem 3 jary do WEB-INF/lib i dodałem je do projektu. Run i… DZIAŁA! Jednakże w logu uruchomienia Tomcata dalej znajduję takie coś:

2009-06-13 01:53:06 org.apache.catalina.loader.WebappClassLoader loadClass
INFO: Illegal access: this web application instance has been stopped already.
Could not load java.lang.Object.
The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException

No ja rozumiem, debugging purposes, ale dlaczego wszystko poza tym działa? Wpisuję w okienko tekst, a on się automatycznie wyświetla poniżej (czyli a4j zaskoczył). Totalnie nie mam pojęcia o co w tym chodzi. Obiecywałem sobie, że w Javie będę grzecznie orientował się na wiedzę, a nie osiągnięte cele, a tutaj mam zagwozdkę – cel osiągnięty, wiedzy do końca to nie ma. No ale może mądrzejsi po przeczytaniu tego postu podsuną jakieś rozwiązanie. Na pożegnanie screen z “HelloWorld” konglomeratu JSF+RichFaces

jsf_new_step6

czerwiec 11, 2009

SCJP podejście szóste

Zaszufladkowany do: Java, SCJP — chlebik @ 7:16 pm

Korzystając z długiego urlopu intensywnie (na zmianę z JSF) edukuję się celem zdania SCJP. Dziś przetrawiłem rozdział szósty podręcznika, który dotyczył wielu istotnych zagadnień w życiu każdego programisty – parsowania łańcuchów tekstowych, serializacji, formatowania dat i liczb, IO, wyrażeń regularnych i cholera wie czego jeszcze. Przyznam się do tego, iż z tym rozdziałem miałem najwięcej problemów spośród wszystkich dotychczas przerobionych. No bo jak tu ogarnąć na raz 100 stron wycieczki po API Javy kiedy się w niej nigdy zawodowo nie kodowało? Dla ludzi, którzy zęby na Javie zjedli treść tego rozdziału pewnie była śmieszna, dla mnie z kolei przypomnieniem co to znaczy uczyć się czegoś na pamięć :)

Pomimo tego końcowy test nie poszedł mi wcale tak źle. Najwięcej problemów miałem z pytaniami z serializacji, a to z powodu nieuwagi przy czytaniu zależności między klasami/zmiennymi instancji/statykami przy odbywaniu się tego procesu. Oto lista rzeczy, na którą warto zwracać uwagę:

  • nieszczęsna serializacja – jest kilka zasad, których trzeba przestrzegać, aby ustrzec się błędów w pytaniach dotyczących serializowania obiektów. Oto używany kod:class A {}
    class B extends A implements Serializable {
    A zmienna = new A();
    static int zmienna2 = 9;
    int transient zmienna3 = 1;
    }

    • W przypadku deserializacji klasy B nie jest wywoływany jej konstruktor (to wynika z procesu deserializacji), ale za to konstruktor klasy A jest jak najbardziej wywoływany
    • Istniejący zapis uniemożliwia serializację klasy B, gdyż klasa A nie może być serializowana. Jednakże taki kod skompiluje się bez problemu!!!. Dopiero podczas procesów serializacji wyrzuci wyjątek.
    • przy deserializacji i serializacji statyczne składowe klasy w ogóle są wypisywane z tego procesu. Nasza składowa zmienna2 istnieje niezależnie od serializacji/deserializacji.
    • zmienne oznaczone jako transient nie zostaną zainicjalizowane swoimi domyślnymi wartościami (np. zmienna3 po deserializacji obiektu nie otrzyma wartości 1!!!). Dostanie za to śliczne 0, gdyż będzie to jej wartość domyślna (dla tego typu zmiennych).
  • tak pro forma plików i katalogów – istnieje metoda mkdir, ale dla plików stosujemy createNewFile(). Warto też przyjrzeć się konstruktorom File.
  • NumberFormat – ma ciekawą metodę setMaximumFractionDigits, która ustawia ilość cyfr “po kropce”. I byłoby świetnie gdyby autorzy podręcznika podkreślili przy jej omawianiu, że dotyczy ona tylko metody format(). Metody parse już obostrzenia związane z ustawioną wartością zupełnie nie dotyczą. Do tego należy przypomnieć, że metoda format przy dopasowywaniu wartości do wskazanej liczy cyfr “po kropce” nie obcina liczby, ale ją zaokrągla!

To tyle póki co z placu boju. Jutro czeka na mnie smakowity kąsek – rozdział siódmy dotyczący kolekcji. Będzie się działo.

czerwiec 7, 2009

SCJP, podejście piąte

Zaszufladkowany do: Java, SCJP — chlebik @ 10:04 pm

Kolejny rozdział podręcznika przerobiony. Dotyczył on sterowania przepływem, asercjami oraz wyjątkami. Ciekawa lektura, pełna olbrzymiej wręcz ilości potencjalnych kruczków przy układaniu pytań testowych. O dziwo końcowe zadania poszły mi powiedziałbym bardzo dobrze, chyba zaczynam ‘czuć’ pytania, a także potencjalne odpowiedzi. Rzecz, która może tylko cieszyć.

Oczwyście nie ustrzegłem się kilku potknięć, oto lista rzeczy wartych zapamiętania:

  • kolejna nieostrożność związana z kodem – o czym warto pamiętać. Poległem na kodzie, w którym zadeklarowano wyrzucenie wyjątku IOException. I o ile na pytanie odpowiedziałem poprawnie, to nie zauważyłem myku, na który we wstępie do podręcznika autorzy zwracają uwagę. Otóż jeżeli listing programu zaczyna się od linii numer 1, wówczas oznacza to fakt, iż widzimy całość przedstawionego kodu. Natomiast jeśli pierwsza linia w listingu jest większa niż 1, wówczas istnieje szansa, że coś z tym kodem jest nie tak. No i było nie tak, gdyż IOException należy zaimportować przed użyciem, bez tego sypie błędem, że nie znalazł klasy.
  • deklarowanie wyjątków przez metodę main – tak, tak. Metoda ta może zadeklarować wyrzucenie wyjątku!
  • rozszerzona pętla for – jest fajna, nietrudna, ale różni się jednyn małym szczegółem od swojej starszej siostry. Rozważmy taki kod:
    int x = 5;
    for( x = 2; x < 10; x++) { }

    Skompiluje się on i będzie super pięknie i ładnie. Jednakże taki kod:
    int[] x = {2,3,4};
    int y = 0;

    for( y : x) {}

    nie skompiluje się. Pierwszy operand w tej wersji pętli musi być zadeklarowany w jej ciele.

  • nieskończone pętle nie muszą wysypywać stosu – rzecz teoretycznie kontrowersyjna. Kod:

    for(int i = 0; i < 10; i++) {
    if( i == 9 ) i = 1;
    }

    to oczywiście pętla nieskończona (zawiesiła mi NetBeansa). Jednakże nie doprowadzi do wygenerowania StackOverflowError!!. Jednakże takie coś:
    public void zrobCos() {
    zrobCos();
    }

    wywołane przez instancję obiektu doprowadzi do przepełnienia stosu, gdyż każde wywołanie metody rezerwuje trochę pamięci. W ten sposób moje nowe 2GB RAMu poleciały bardzo szybko i sypnęło błędem.

  • drugi operand asercji – służy temu, aby dorzucić do błędu asercji informację o tym, gdzie i co się wysypało. Jakoś ubzdurało mi się, że podane tam rzeczy muszą ostatecznie skutkować w obiekcie String. Okazało się, że to nie tak – wystarczy, że zwracane jest cokolwiek.
  • overriding metod – ta informacja była podana w rozdziale bodajże drugim, ale kto by się wtedy tym przejmował :) Otóż jeśli metoda nadpisuje metodę ze swej klasy rodzicielskiej, nie może deklarować wyrzucenia szerszego wyjątku! Czyli jeśli metoda zrobCos() w klasie A deklaruje, że wyrzuci wyjątek IOException, wówczas dziedzicząca po klasie A klasa B nie może nadpisać metody zrobCos() deklarujac, ze wyrzuci ona po prostu zwykły Exception.

To tyle na dziś. Poinformuję tylko o bardzo ciekawej inicjatywie, której autorem jest Mariusz Lipiński, a którą można znaleźć pod wielce mówiącym adresem. Inicjatywa warta wspierania.

Starsze wpisy »

Blog na WordPress.com.