Miesięczne archiwum: Listopad 2011

Zdaj OCPJP – pytanie 16

Jaki będzie efekt działania poniższego programu?

abstract class A
{
   protected void test() { System.out.println("A"); }
}
public class B extends A
{
   void test(){ System.out.println("B"); }
   public static void main(String[] args) {
      A a = new B();
      a.test();
   }
}

 

  1. Zostanie wyświetlone A
  2. Zostanie wyświetlone B
  3. Program się nie skompiluje
  4. Zostanie rzucony wyjątek RuntimeException

 

Pokaż odpowiedź »

Poprawna odpowiedź: 3
Kompilacja programu zakończy się rzuceniem wyjątku, ponieważ próbujemy zmniejszyć poziom dostępności nadpisywanej metody. W Javie istnieją 4 poziomy dostępu (posortowane od najmniej restrykcyjnego):

  1. Public
  2. Protected
  3. Package
  4. Private

Kompilator nigdy nie pozwoli nam na zmniejszenie poziomu dostępności, poprzez zmianę poziomów z wyższego na niższy w przedstawionej hierarchii.

Zdaj OCPJP – pytanie 15

Jaki będzie efekt kompilacji i uruchomienia poniższego kodu?

public class Sample
{
    public static String getText()
    {
        return null;
    }

    public static void main(String[] args)
    {
        System.out.println(null);
        System.out.println(getText());
    }
}

 

  1. Kod nie skompiluje się przez błąd w linii 10.
  2. Kod nie skompiluje się przez błędy w liniach 10 i 11.
  3. W trakcie uruchomienia rzucony zostanie NullPointerException.
  4. Wydrukowane zostanie odpowiednio null null.
  5. Wydrukowane zostanie null.

 

Pokaż odpowiedź »

Poprawna jest odpowiedź 1.

Linia 10 powoduje błąd kompilacji, ponieważ nie wiadomo, którą z przeciążonych metod println klasy PrintStream wywołać. W linii 11 nie ma tego problemu, ponieważ metoda getText deklaruje zwracany typ jako String. Co ciekawe, po zmianie linii 10 na poniższą, program kompiluje się i uruchamia bez problemu.


System.out.println((Object)null);

Zdaj OCPJP – pytanie 14

Jaki będzie efekt działania poniższego programu?

public static void main(String[] args) {
    char \u0105 = '\u0061';
    char \u007e = 'ż';
    System.out.println(\u0105 + " " + \u007e);
 }

Informacje dodatkowe:

  • \u0105 odpowiada literze ą
  • \u0061 odpowiada literze a
  • \u007e odpowiada znakowi ~

 

  1. Błąd kompilacji w linii 2
  2. Błąd kompilacji w linii 3
  3. Błąd kompilacji w linii 4
  4. Zostanie rzucony wyjątek RuntimeException w linii 4
  5. Program wykona się poprawnie i wyświetli „ą ż”
  6. Program wykona się poprawnie i wyświetli „a ż”

 

Pokaż odpowiedź »

Poprawna odpowiedź: 2
Przede wszystkim należy pamiętać, że w kodzie źródłowym możemy wykorzystywać znaki Unicode w nazwach identyfikatorów. W tym celu wykorzystujemy zapis \u{kod_znaku}. Możemy także zrezygnować z zapisu znaku w postaci kodu i użyć go bezpośrednio (należy wtedy ustawić odpowiednie kodowanie źródeł programu za pomocą opcji ‚-encoding’ kompilatora):

public static void main(String[] args) {
    char ąąą = 'u0061';
    System.out.println(ąąą);
}

Dlaczego więc przykładowy program zwraca błąd kompilacji? Wynika to z zasady, która mówi, że każdy identyfikator musi zaczynać się od litery, podkreślenia (‚_’) lub znaku dolara (‚$’). Możemy uogólnić tą zasadę – identyfikator musi zaczynać się od znaku, dla którego funkcja isJavaIdentifierStart zwraca true. Oznacza to, że znaki Unicode także są respektowane (tłumaczy to brak błędu dla identyfikatora \u0105). Znak reprezentowany przez kod \u007e (‚~’) nie należy więc do grupy akceptowalnych wartości.

Zdaj OCPJP – pytanie 13

Jaki będzie efekt kompilacji i uruchomienia poniższego kodu?

public class Sample
{
    public static void main(String... args)
    {
        String a = "a";
        String aa = "a";
        String ab = "ab";

        System.out.println(a == aa);
        System.out.println(ab == ("a" + "b"));
        System.out.println(ab == new String("ab"));
        System.out.println(ab == new String("ab").intern());
    }
}

 

  1. Kod nie skompiluje się.
  2. Wydrukowane zostanie odpowiednio true true false true.
  3. Wydrukowane zostanie odpowiednio false false false false.
  4. Wydrukowane zostanie odpowiednio true true false false.
  5. Wydrukowane zostanie odpowiednio true true true true.

 

Pokaż odpowiedź »

Poprawna jest odpowiedź nr 2.

Pierwsze porównanie zwraca true, ponieważ obydwie referencje wskazują na ten sam obiekt String znajdujący się w puli (literały typu String są tam umieszczane celem optymalizacji wykorzystania pamięci). W drugim przypadku należy dodatkowo pamiętać o tym, że działania na literałach typu String są wykonywane już w trakcie kompilacji, skutkiem czego w trakcie działania programu mamy do czynienia z wynikiem konkatenacji. W trzecim teście tworzony jest bezpośrednio nowy obiekt, który jest zupełnie nową instancją – stąd negatywny wynik porównania. W ostatnim przypadku również używamy operatora new, jednak na otrzymanym obiekcie dodatkowo wywoływana jest metoda intern(). Pozwala ona na pobranie obiektu String o takiej samej wartości z puli. Jeśli takowy nie istnieje, zostanie on w niej umieszczony.

Zdaj OCPJP – pytanie 12

Jaki będzie efekt uruchomienia poniższego kodu?

public static void main(String[] args) {
	        int a = Integer.MIN_VALUE;
	        int b = -a;
	        System.out.println(a + " " + b);
}

 

  1. Zostaną wyświetlone dwie różne ujemne liczby
  2. Zostanie wyświetlona liczba przeciwna do Integer.MIN_VALUE
  3. Zostaną wyświetlone dwie te same liczby
  4. Zostanie rzucony wyjątek RuntimeException w linii 3
  5. Program się nie skompiluje

 

Pokaż odpowiedź »

Poprawna odpowiedź: 3
Jest to jedno z pytań, których raczej nie znajdziemy na prawdziwym egzaminie, ale jest na tyle ciekawe, że postanowiliśmy je opisać (może komuś trafi się na rozmowie kwalifikacyjnej do wymarzonej pracy…). W celu lepszego zrozumienia odpowiedzi przeanalizujmy wynik działania prostego programu pomocniczego:

public static void main(String[] args) {
        int a = Integer.MIN_VALUE;
        int b = -a;
        int c = Integer.MAX_VALUE;
        System.out.println(a + " " + b + " " + c);
        System.out.println(Integer.toBinaryString(a) + " " + Integer.toBinaryString(b) + " " + Integer.toBinaryString(c));
    }
-2147483648 -2147483648 2147483647
10000000000000000000000000000000 10000000000000000000000000000000 1111111111111111111111111111111

Liczba Integer.MIN_VALUE przyjmuje wartość -2147483648, której odpowiada postać binarna 10000000000000000000000000000000. Liczba do niej przeciwna powstaje przez zamianę zer z jedynkami oraz dodanie wartości jeden:

1 krok: 10000000000000000000000000000000
2 krok (zamiana): 01111111111111111111111111111111
3 krok (+1): 10000000000000000000000000000000

Warto zauważyć, że wartość odwrotna do Integer.MIN_VALUE wykracza poza zakres dopuszczalny dla typu Integer.

Zdaj OCPJP – pytanie 11

Jaki będzie efekt kompilacji i uruchomienia kodu zaprezentowanego poniżej?

public class Sample
{
    public static void main(String... args)
    {
        Integer aa = 27;
        Integer ab = 27;

        Integer ba = 127;
        Integer bb = 127;

        Integer ca = 1127;
        Integer cb = 1127;

        Integer da = -128;
        Integer db = -128;

        System.out.println(aa == ab);
        System.out.println(ba == bb);
        System.out.println(ca == cb);
        System.out.println(da == db);
    }
}

 

  1. Wydrukowane zostanie odpowiednio false false false false.
  2. Wydrukowane zostanie odpowiednio true true false false.
  3. Wydrukowane zostanie odpowiednio true true true true.
  4. Wydrukowane zostanie odpowiednio true true false true.

 

Pokaż odpowiedź »

Odpowiedź numer 4 jest prawidłowa.

Powodem, dla którego porównania w liniach 17, 18 i 20 zwracają true, jest fakt pobierania tych wartości z puli tworzonej w celu optymalizacji wykorzystania pamięci. W puli tej umieszczane są:

  • Wszystkie wartości typu Boolean.
  • Wszystkie wartości typu Byte.
  • Wartości typu Character z zakresu od 0 do 127 włącznie.
  • Wartości typu ShortInteger i Long z zakresu od -128 do 127 włącznie.

Zdaj OCPJP – pytanie 10

Co bedzie wynikiem skompilowania i uruchomienia poniższego kodu?

public class Sample
{
    public static void main(String... args)
    {
        Integer[] aa = new Integer[]{1, 2, 3};
        Integer[] ab = new Integer[]{0, 2, 4};

        System.out.println(aa[(aa = ab)[0]]);
        System.out.println(aa[0]);
    }
}

 

  1. Dwukrotnie wydrukowana zostanie cyfra 0.
  2. Wydrukowane zostaną odpowiednio cyfry 2 i 0.
  3. Wydrukowane zostaną odpowiednio cyfry 1 i 0.
  4. Powyższy kod nie skompiluje się.
  5. Powyższy kod spowoduje rzucenie wyjątku.

 

Pokaż odpowiedź »

Poprawna jest odpowiedź nr 3.

Kod jest jak najbardziej poprawny – jako indeks tablicy możemy podawać dowolne wyrażenia, które dają wynik odpowiedniego typu. Trudność pytania polega na konieczności przewidzenia kolejności rozpatrywania poszczególnych wyrażeń. Jak się okazuje, przy dostępie do tablic wyrażenie po lewej stronie nawiasów kwadratowych (w tym przypadku aa) jest pobierane z wyprzedzeniem. Przypisanie wewnątrz nawiasów kwadratowych rzeczywiście zmienia obiekt, na który wskazuje referencja aa, ale odpowiedni element zostanie pobrany jeszcze z pierwotnej tablicy.

Zdaj OCPJP – pytanie 9

Jaki będzie efekt kompilacji i uruchomienia zaprezentowanego poniżej kodu?

public class Sample
{
    public static void main(String[] args)
    {
        try
        {
            System.out.println("TRY");
            return;
        }
        finally
        {
            System.out.print(" FINALLY");
        }
    }
}

 

  1. Kod nie skompiluje się, ponieważ brakuje bloku catch.
  2. Kod nie skompiluje się, ponieważ w bloku try nie można umieścić return.
  3. Kod skompiluje się, a po uruchomieniu wypisane zostanie TRY.
  4. Kod skompiluje się, a po uruchomieniu wypisane zostanie TRY FINALLY.

 

Pokaż odpowiedź »

Poprawna odpowiedź to 4.

Pierwsze dwie odpowiedzi są błędne, ponieważ blok catch nie jest wymagany, jeśli istnieje blok finally, a umieszczenie instrukcji return w sekcji try jest zupełnie poprawne. Instrukcje zawarte w bloku finally zostaną wykonane nawet wtedy, kiedy w bloku try nastąpi powrót z metody. Jedyną sytuacją, w której blok finally nie wykonałby się, jest wywołanie System.exit() w sekcji chronionej, lub zakończenie pracy JVM w inny sposób.