JAVA exPress logo Published on JAVA exPress (http://www.javaexpress.pl)

Express killers, cz. I

Damian Szczepanik

Issue 2 (2008-12-06)

Z przyjemnością witam w nowym cyklu Express killers. Dział będzie prezentował ciekawe (nie koniecznie zalecane!) przypadki użycia języka Java. Jestem zwolennikiem korzystania z IDE i wszystkich jego możliwości, które oferuje, dlatego problemy tutaj poruszane nie będą pytaniem, dlaczego poniższy kod się nie kompiluje?

            public class Object {
              Object o = new Object() {
                public String toString() {
                  return null;
                }
              }
            }
        

Nie będę także czytelników zanudzał pytaniami o wynik takiego wyrażenia:

            int i = 0x12<<3+04^5>>06>>>7|8&9;
        

bo przecież po to są nawiasy, żeby sobie życie ułatwiać oraz internet, żeby nie trzeba było pamiętać, który operand będzie pierwszy brany do wyliczenia.

Co zatem będzie tematem? Najlepsze są przykłady z życia wzięte. Ot np. taki:

            import java.util.Vector;
            import java.util.logging.Level;
            import java.util.logging.Logger;

            public class Pool {

              private static Logger logger = Logger
                  .getLogger(Pool.class.getName());
              private Vector messages;
              private Integer status;

              public boolean add(String message) {
                logger.log(Level.ALL, "addding:" + this);
                return messages.add(message);
              }

              public String get(int index) {
                logger.log(Level.ALL, "getting:" + this);

                return messages.get(index);
              }

              public String toString() {
                StringBuffer sb = new StringBuffer();
                int i = 1;
                do {
                  sb.append(get(i)).append(",");
                } while (++i < messages.size());
                sb.append(status);
                return sb.toString();
              }

              public static void main(String[] args) {
                Pool p = new Pool();
                p.add("hello");
                System.out.println(p.get(0));
              }
            }
        

Biorąc pod uwagę, że powyższy kod się kompiluje, które z poniższych odpowiedzi dotyczą sytuacji po jego uruchomieniu?

- Uruchomienie zakończy się błędem java.lang.ArrayIndexOutOfBoundsException, gdyż w metodzie toString() nieprawidłowo inicjalizowana jest pętla.

- Gdyby zmienna status była prymitywem, program nie zakończyłby się wyjątkiem.

- Program zakończy się poprawnie, a na wyjście zostanie wypisany napis hello.

- Wyjątek java.lang.NullPointerException zostanie rzucony w pętli metody toString().

- Żadna z powyższych odpowiedzi nie jest prawidłowa.

Odpowiedź na to pytanie pozostawiamy czytelnikowi, który może uruchomić powyższy kod w swoim ulubionym środowisku programistycznym. Może także poszukać jej na kolejnych stronach Java exPress.

A oto i drugie zadanie. Jego specyfika nie przeszkadza, by wkleić następującą metodę do swojego IDE i sprawdzić, jak się zachowuje po uruchomieniu. Oto kawałek kodu pewnej klasy:

            public static void main(String[] args)
                throws Exception {

              CatchNull test = new CatchNull();
              if (test == null) {
                throw new NullPointerException("null object");
              }

              if (!(test instanceof CatchNull)) {
                throw new UnknownError(
                    "not an instanceof CatchNull");
              }
            }
        

W jakim przypadku uruchomienie powyższego kodu spowoduje rzucenie jednego z dwóch wyjątków? Odpowiedź, podobnie, jak do poprzedniego pytania, na dalszych stronach. Gorąco także zachęcam do poszukania jej samemu!

Serdecznie zapraszam wszystkich, by pochwalili się z czytelnikami swoimi interesującymi kawałkami kodu. Najlepsze będą publikowane i nagradzane na łamach czasopisma. Mogą to być także kawałki kodu z cyklu WTF. Praca codziennie dostarcza interesujące przykłady, po przeczytaniu których programista zaczyna sprawdzać elementarne podstawy swojej wiedzy. Niech zaczną ją też sprawdzać inni za pośrednictwem Java exPress.

Odpowiedzi:

W pierwszym przykładzie w metodzie main(String[]) jest wywołanie metody get(int) co spowoduje zapisanie tej informacji w logach. Użycie słowa kluczowego this zakończy się wywołaniem metody toString(). W metodzie toString() następuje iteracja po wszystkich elementach kolekcji. Chociaż pętla nie jest napisana poprawnie, to przy pierwszej iteracji nie zostanie zgłoszony żaden wyjątek. Zostanie natomiast wywołana metoda get(int). W konsekwencji wygenerowany zostanie wyjątek o przepełnieniu stosu java.lang.StackOverflowError.

W drugim przypadku żaden wyjątek nie zostanie rzucony. Pierwszy z nich byłby rzucony, gdyby zmienna test nie została poprawnie stworzona. Miałoby to miejsce, gdyby konstruktor rzucił wyjątek lub gdyby maszyna wirtualna nie posiadała wystarczającej ilości pamięci do stworzenia nowego obiektu. Gdyby tak się stało, to rzucony wyjątek nie zostałby chwycony, a zatem pierwsza instrukcja warunkowa nie zostanie wykonana. W takim przypadku także druga instrukcja warunkowa nie zostanie sprawdzona.

Druga instrukcja warunkowa przy deklaracji zmiennej test jako CatchNull nie ma sensu. Kompilator (lub maszyna wirtualna, jeśli zastosować rzutowanie) nie dopuści, aby do zmiennej typu CatchNull został przypisany obiekt, który nie jest typem CatchNull lub pochodnym. Jedynym przypadkiem, by warunek drugi był prawdziwy jest, by zmienna test była równa null, co według wcześniejszego wywodu nie jest możliwe.

Source: http://www.javaexpress.pl/article/show/Express_killers_cz_I