Podchwytliwe pytania rekrutacyjne z Javy (razem z odpowiedziami)

Przygotowaliśmy zbiór ciekawych pytań rekrutacyjnych z Javy, które możesz usłyszeć podczas rozmowy rekrutacyjnej na stanowiska związane z automatyzacją testów oprogramowania.

Poniższy wpis jest zbiorem zadań sprawdzających wiedzę z Javy, które zdarzyło mi się otrzymać na kilku rekrutacjach w ciągu ostatnich kilku lat. Pomyślałem, że warto zapisać i podzielić się tymi z nich, które okazały się mniej, lub bardziej podchwytliwe.

Pod każdym pytaniem wypisałem poprawną odpowiedź. Zachęcam do samodzielnego przetestowania podanych rozwiązań, tak by je dobrze zrozumieć i zapamiętać.

Lista pytań

  • Czy konstruktor może być prywatny?
  • Jakie zmiany wprowadzono w Java 8?
  • Która kolekcja w Java przechowuje unikalne wartości?
  • Czym różni się klasa abstrakcyjna od interfejsu?
  • Jaka jest różnica między ==, a .equals()?
  • Czy można nadpisać prywatną metodę?
  • Czym w Java różni się nadpisywanie (overriding) od przeciążania (overloading)?
  • Wyjaśnij, co to jest String Pool
  • Wyjątki checked, unchecked – czym się różnią?
  • Czy po każdym bloku try musi następować blok catch?
  • Czym się różni parametr od argumentu?

Lista odpowiedzi

Czy konstruktor może być prywatny?

Poprawna odpowiedź brzmi: Tak, konstruktor może być prywatny, czyli oznaczony modyfikatorem private. Takie podejście pozwala na kontrolę tworzenia nowych obiektów danej klasy. Konstruktory prywatne są używane w połączeniu np. ze wzorcem Factory Method lub wzorcem (w sumie to antywzorcem) Singleton.

Poniżej znajduje się przykład:

Java
public class Car {
    private Car() {} // prywatny konstruktor

    public static Car getCarInstance() {
        return new Car();
    } // metoda statyczna zwracająca instancję klasy

    public void showMessage() {
        System.out.println("Just some message to print anything");
    } // prosta metoda printująca łańcuch znaków
}
Java
public class Main {
    public static void main(String[] args) {
        Car.getCarInstance().showMessage(); // "Just some message to print anything"
    }
}

Jakie zmiany wprowadzono w Java 8?

Odpowiedź – są to między innymi:

  • wyrażenia lambda
  • stream API (strumienie)
  • domyślne metody w interfejsach
  • wartości opcjonalne

Która kolekcja w Java przechowuje unikalne wartości?

Odpowiedź – jest to Set, czyli kolekcja która zawiera jedynie unikalne elementy. Zbiory są implementacją interfejsu Set i gwarantują, że każdy element jest przechowywany tylko raz. Oznacza to, że próba dodania duplikatu do zestawu nie powiedzie się.

Java
        Set<String> fruits = new HashSet<>();

        // Dodaj elementy do zbioru
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");
        fruits.add("Apple"); // Duplikat - zostanie zignorowany

        // Sprawdź rozmiar zbioru
        System.out.println("Liczba owoców: " + fruits.size()); // 3

Czym różni się klasa abstrakcyjna od interfejsu?

Najważniejsze różnice to:

  • Klasa abstrakcyjna to klasa, która zawierać zarówno metody abstrakcyjne, jak i zwykłe metody z implementacją. Interfejs może posiadać jedynie metody abstrakcyjne (ale od Javy 8 można tworzyć metody domyślne i statyczne w interfejsie, które zawierają implementacje metod).
  • Klasa może dziedziczyć tylko po jednej klasie abstrakcyjnej, ale może implementować wiele interfejsów.
  • Klasa abstrakcyjna może zawierać zmienne oznaczone jako final, non-final, static i non-static.
  • Metody klas abstrakcyjnych mogą mieć modyfikatory dostępu takie jak public, private, protected, static, natomiast metody interfejsów są domyślnie publiczne, nie można tam używać żadnych innych modyfikatorów dostępu.
  • Klasa abstrakcyjna może implementować interfejs, ale interfejs nie może implementować klasy abstrakcyjnej.

Jaka jest różnica między ==, a .equals()?

W uproszczeniu, == to porównanie referencji, czyli sprawdzenie czy oba obiekty wskazują na to samo miejsce w pamięci. Z kolei .equals() porównuje wartości obiektów. Przykład znajduje się poniżej:

Java
String s1 = "HELLO";
        String s2 = "HELLO";
        String s3 =  new String("HELLO");
 
        System.out.println(s1 == s2); // true
        System.out.println(s1 == s3); // false
        System.out.println(s1.equals(s2)); // true
        System.out.println(s1.equals(s3)); // true

Czy można nadpisać prywatną metodę?

Odpowiedź brzmi: Nie, nie można nadpisać metody oznaczonej modyfikatorem private, ponieważ nie jest ona widziana przez żadną inną klasę.

Czym w Java różni się nadpisywanie (overriding) od przeciążania (overloading)?

Poprawna odpowiedź:

Kiedy sygnatura metody (nazwa i parametry) jest taka sama w klasie nadrzędnej i klasie potomnej, nazywa się to nadpisywaniem (overriding). Kiedy co najmniej dwie metody w tej samej klasie mają tę samą nazwę, ale różne parametry, nazywa się to przeciążaniem (overloading). Poniżej napisałem przykłady obu tych pojęć:

Overloading:

Java
class MathOperations {
    // Przeciążona metoda add, która dodaje dwa inty
    int add(int a, int b) {
        return a + b;
    }

    // Przeciążona metoda add, która dodaje trzy inty
    int add(int a, int b, int c) {
        return a + c + b;
    }
}

public class Main {
    public static void main(String[] args) {
        MathOperations math = new MathOperations();

        System.out.println(math.add(2, 3)); // 5
        System.out.println(math.add(2, 3, 4)); // 9
    }
}

Overriding:

Java
// Klasa nadrzędna
class Animal {
    // Metoda, która zostanie nadpisana w klasie potomnej
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

// Klasa potomna
class Dog extends Animal {
    // Nadpisanie metody makeSound z klasy nadrzędnej
    @Override
    void makeSound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        myDog.makeSound(); // Dog barks
    }
}

Wyjaśnij, co to jest String Pool

String Pool, znany również jako String Intern Pool, to specjalny obszar w pamięci zarządzany przez JVM, który jest używany do przechowywania unikalnych obiektów typu String. Głównym celem String Poola jest optymalizacja pamięci poprzez przechowywanie tylko jednej kopii każdego niezmiennego łańcuchu znaków.

Wyjątki checked, unchecked – czym się różnią?

To akurat jedno z częstszych pytań na rozmowach rekrutacyjnych, ale warto je przypomnieć.

Wyjątki kontrolowane (checked exceptions) są sprawdzane przez kompilator na etapie kompilacji. Oznacza to, że programista musi je obsłużyć, inaczej program się nie skompiluje. Przykład:

Widzimy tutaj, że kompilator wymaga od nas zabezpieczenia się przed brakiem pliku w podanej lokalizacji. Gdy opakujemy całość w try - catch (lub kiedy dodamy wyjątek do sygnatury metody), kompilator nie zwraca już błędu:

Inaczej wygląda sprawa z wyjątkami niekontrolowanymi (unchecked exceptions). Są to wyjątki, które najczęściej wynikają z błędu programisty – np. dzielenie przez zero. Nie są one wyłapywane na etapie kompilacji, a kompilator nie wymusza na nas obsłużenia takiego wyjątku. Przykład:

Powyżej widać, jak próba wywołania metody length() na niezainicjalizowanym obiekcie powoduje wyrzucenie NullPointerException. Opakowanie tego fragmentu w try – catch pozwala na obsłużenie wyjątku:

Czy po każdym bloku try musi następować blok catch?

Odpowiedź brzmi – nie, nie musi. Mimo że konstrukcja try-catch jest często używana w parze do obsługi wyjątków, istnieje kilka scenariuszy, w których blok catch nie jest wymagany. Przykładem może być użycie bloku finally po bloku try, który zawsze zostanie wykonany, niezależnie od tego, czy wystąpił wyjątek, czy nie:

Java
int[] numbers = {1, 2, 3, 4, 5};

try {
  System.out.println(numbers[5]); // Wywoła ArrayIndexOutOfBoundsException
} finally {
  System.out.println("Blok finally zawsze się wykona.");
}

Czym się różni parametr od argumentu?

Poprawna odpowiedź: Parametr i argument w kontekście metod w Java to dwa pojęcia, które często są ze sobą mylone. Parametr to zmienna, która jest deklarowana w definicji metody i służy do przyjmowania wartości podczas wywołania tej metody. Można go postrzegać jako miejsce na wartość, którą przekażemy później.

Java
public int addSomeNumbers(int a, int b) {
    return a + b;
}

W powyższym przypadku, a i b to parametry metody addSomeNumbers.

Jeśli wywołamy tę metodę:

Java
addSomeNumbers(3, 4);

To 3 i 4 staną się argumentami tej metody. Proste, prawda? 🙂

Podobał Ci się ten artykuł?

Jeśli chciałbyś przeczytać takich więcej, zachęcamy do polubienia naszych profili w mediach społecznościowych. Zero spamu, sam konkret!

Dodaj komentarz