Java Pytania Rekrutacyjne, Java Zadania

Pytania rekrutacyjne developer

Szykujesz się do rozmowy kwalifikacyjnej na Java Developera i chcesz wcześniej poznać pytania rekrutacyjne?
Ba! Kto by nie chciał ich znać zawczasu. 🙂

Na moje i Twoje szczęście większość pytań i zadań podczas rozmowy kwalifikacyjnej dla początkujących programistów zwyczajnie się powtarza.
Poniżej masz listę pytań z odpowiedziami, która pomoże Ci się przygotować do Twojej rozmowy. Powodzenia!

Spis treści

Java pytania rekrutacyjne

Jest to tekst z serii pytań i odpowiedzi na rozmowę kwalifikacyjną Developera.

Pozostałe artykuły z tej serii to SQL oraz JavaScript.

Zapraszam do dzielenia się swoimi pytaniami z rozmów kwalifikacyjnych tutaj w komentarzach lub na grupie.

Co to jest leniwe (ang. lazy loading) i zachłanne ładowanie danych (ang. eager loading)?

Lazy loading to wzorzec projektowy mający na celu odroczenie inicjalizacji obiektów najpóźniej jak to tylko możliwe, czyli dopiero aż będą potrzebne. Właściwie wykorzystany może przyczynić się do poprawy wydajności aplikacji. Jego przeciwieństwem jest zachłanne pobieranie danych (ang. eager loading).

Dzięki leniwemu ładowaniu unikamy sytuacji w których przygotujemy złożonych obiekt np. pobierając informacje z bazy danych i później ten obiekt nie zostanie nigdzie wykorzystany.

Na czym polega problem n+1 zapytań?

Jest to problem bezpośrednio związany z leniwym pobieraniem danych. Najczęściej występuje w momencie generowania różnego rodzaju list rekordów. Pierwsze zapytanie pobiera główną listę rekordów, a następnie podczas iterowania po niej, dla każdego rekordu pobierane są dane w sposób leniwy, co generuje kolejne n zapytań.

Ostatecznie do wyświetlenia listy danych potrzebujemy n+1 zapytań:

  • 1 zapytanie – do pobrania samej listy obiektów,
  • następnie dla każdego z tych obiektów jeszcze jedno zapytanie, czyli n kolejnych zapytań.

Możliwym rozwiązaniem tego problemu jest zachłanne pobieranie danych lub specjalne przygotowanie encji zawierających wszystkie potrzebne dane, by już nie było potrzeby ich dociągania.

Redukujemy wtedy ilość potrzebnych zapytań do bazy danych do jednego, ale bardziej złożonego zapytania.

Co to są typy proste i referencyjne?

Typy proste (prymitywne) przechowują tylko „surowe” dane, takie jak: liczby, czy znaki, mówiąc w uproszczeniu, są to konkretne dane np. 1,100 lub ‚A’.

Natomiast typy referencyjne przechowują swego rodzaju wskaźniki na obiekty lub wartość null, np. obiekt typu Person, który dopiero w sobie będzie zawierał typy proste jak np. int age lub ewentualnie kolejne typy referencyjne jak np. Address address.

Co to jest Autoboxing and Unboxing?

Autoboxing to automatyczna konwersja, dokonywana przez kompilator Javy między typami prymitywnymi a odpowiadającymi im klasami osłonowymi, np. zamiana typu int na Integer lub typu double na Double.

Unboxing to zamiana odwrotna, czyli z klas osłonowych do typów prostych.

Co to są obiekty immutable?

Obiekty immutable, czyli niemodyfikowalne, to obiekty, które po utworzeniu nie mogą już zmienić swojego stanu. W efekcie tego każda ich modyfikacja wiąże się z utworzeniem nowego obiektu. Najbardziej znaną klasą niemodyfikowalną jest standardowy String.

➡ ZOBACZ 👉Immutable – niezmienne obiekty

Co to jest serializacja?

Serializacja to zamiana obiektów, np. w strumień bajtów z zachowaniem ich aktualnego stanu. Serializowany obiekt może zostać zapisany np. na dysku twardym lub w bazie danych, a następnie w procesie deserializacji odtworzony do swojej pierwotnej postaci.

Co to jest refleksja?

Mechanizm refleksji polega na modyfikowaniu kodu aplikacji podczas jej działania. Dzięki refleksji można zarządzać kodem programu prawie tak łatwo, jakby to były zwykłe dane.

Można np. pobrać wszystkie pole zadeklarowane w danej klasie i je wyświetlić lub nawet zmienić ich modyfikatory dostępu! Wszystko podczas działania aplikacji, bez modyfikacji jej kodu źródłowego.

Czym różnią się wyjątki oznaczone (ang checked) od nieoznaczonych (ang. unchecked exceptions)?

Wyjątki checked są oznaczane już w momencie kompilacji kodu. Jeżeli w jakiejś metodzie może zostać rzucony wyjątek oznaczony, taka metoda musi go przechwycić i obsłużyć lub musi jawnie zadeklarować go przy pomocy słowa kluczowego throws.

Wyjątki nieoznaczone nie są weryfikowane podczas kompilacji, dlatego nie ma konieczności ich deklarowania w definicji metody.

W Javie wszystkie wyjątki dziedziczące po klasie Error i RuntimeException są nieoznaczone, a wszystkie pozostałe dziedziczące po Throwable są oznaczone.

public void method2() {
    try {
        method1();
    } catch (Exception ex) {
        // handle exception
    }
    throw new RuntimeException();
}

public void method1() throws Exception {
    throw new Exception();
}

Co to jest enum?

Enum to typ wyliczeniowy, czyli pewna struktura danych zawierająca listę wszystkich wartości, jakie może przyjąć ten typ. Typy wyliczeniowe wykorzystywane są bardzo często jako swego rodzaju stałe.

enum EnumType {
    T1, T2, T3
}

EnumType enumType = EnumType.T1;

W powyższym przykładzie zmienna enumType może przyjąć tylko 3 wartości: EnumType.T1, EnumType.T2 lub EnumType.T3.

➡ ZOBACZ 👉: Struktury danych – podstawy algorytmów

Czy klasa może dziedziczyć po kilku interfejsach?

Java z zasady nie wspiera wielodziedziczenia (jako wyjątek można uznać interfejsy z default methods). Jedna klasa może dziedziczyć tylko po jednej klasie rodzica.

Klasy nie dziedziczą po interfejsach, a je implementują. Jedna klasa może implementować wiele interfejsów.

Jeden interfejs może dziedziczyć po kilku innych.

interface Interface1 {}

interface Interface2 {}

interface Interface3 extends Interface1, Interface2 {}

class Class1 {}

class Class2 extends Class1 implements Interface1, Interface2, Interface3 {}

Jak pobrać pojedynczy znak z obiektu String?

Do pobrania jednego znaku ze stringa służy metoda charAt.

String str = "Java";
char actualValue = str.charAt(1);
char expectedValue = 'a';

Jak wywołać metodę w klasie podrzędnej z klasy nadrzędnej?

Do wywoływania metod z klasy rodzice służy słowo kluczowe: super.

Podobnie jak metodę można wywołać również konstruktor. W tym wypadku jednak nie podajemy nazwy konstruktora, a samo słowo: super.

class Class1 {
    void method1() {
    }
}

class Class2 extends Class1 {

    public Class2() {
        super();
    }

    void method1() {
        super.method1();
    }
}

Na czym polega kontrakt między metodami hashCode i equals?

Dla metod hashCode oraz equals zdefiniowano założenia, których przestrzeganie gwarantuje poprawne działanie obiektów np. z wykorzystaniem kolekcji (java.util.List, java.util.Map itp).

  1. Kolejne wywołania hashCode muszą zwracać ten sam wynik.
  2. Jeżeli obiekty są równe, wg metody equals, ich hashCode również musi być równy.
  3. Jeżeli obiekty są różne, to ich hashCode może być równy.
  4. Relacja wyznaczona metodą equals musi być zwrotna.
  5. Relacja wyznaczona metodą equals musi być symetryczna.
  6. Relacja wyznaczona metodą equals musi być przechodnia.
  7. Relacja wyznaczona metodą equals musi być spójna.
  8. Każdy obiekt jest różny od null, czyli wywołanie x.equals(null) dla obiektu x różnego od null, zawsze musi zwrócić false.

Więcej na temat tych metod oraz szczegółowe wyjaśnienie zawartego między nimi kontraktu można przeczytać w artykule o hashCode i equals.

Czym się różni lista (List) od zbioru (Set)?

Lista (java.util.List) reprezentuje uporządkowaną listę elementów i może zawierać duplikaty. Dodatkowo przechowuje kolejność dodawania danych.

Zbiór (java.util.Set) reprezentuje zbiór unikatowych elementów, natomiast przechowywana kolejność jest zależna od konkretnej implementacji zbioru.

Na czym polega mechanizm konkatenacji stringa?

Konkatenacja stringów to po prostu łączenie ich ze sobą.

Więcej na temat samej konkatenacji stringów oraz klasy StringBuilder, poprawiającej wydajność konkatenacji można przeczytać w podlinkowanych artykułach.

Co to jest rekurencja?

Rekurencja (rekursja) polega na odwoływaniu się funkcji do siebie samej.

@Test
public void run() {
    r(5);
}

void r(int i) {
    if (i > 0) {
        System.out.println(i);
        r(--i);
    }
}

W powyższym przykładzie metoda rekurencyjna zostanie wywołana 5 razy. Najpierw metoda sprawdza, czy argument wejściowy jest większy od zera, a następnie go wyświetla i wywołuje samą siebie z argumentem mniejszym o 1.

Korzystając z rekurencji należy uważać, by warunek kończący jej wykonanie był dobrze zdefiniowany, ponieważ możemy doprowadzić do zapętlenia się wywołań.

O czym mówią zasady SOLID?

SOLID jest to mnemonik opisujący pięć podstawowych założeń programowania obiektowego.

Single responsibility principle (Zasada jednej odpowiedzialności)

Dana klasa powinna mieć tylko jedną odpowiedzialność.

Open/closed principle (Zasada otwarte-zamknięte)

Zmiana wymagań powinna skutkować dodaniem nowego kodu rozszerzającego poprzedni, a nie modyfikacją działającego już kodu.

Liskov substitution principle (Zasada podstawienia Liskov)

Funkcje przyjmujące jako argument klasy bazowe powinny być w stanie obsłużyć również obiekty klas pochodnych.

Interface segregation principle (Zasada segregacji interfejsów)

Lepiej przygotować kilka specyficznych, dedykowanych interfejsów, niż jeden zbiorczy.

Dependency inversion principle (Zasada odwrócenia zależności)

Moduły wyższego poziomu nie powinny być zależne od tych z niższego poziomu.

Co to jest ciągła integracja (ang. Continuous Integration)?

Ciągła integracja polega na rozwijaniu oprogramowania z częstym i regularnym dołączaniem bieżących zmian do głównego kodu aplikacji. Dzięki wykorzystaniu ciągłej integracji zmniejsza się koszty i potencjalne ryzyko łączenia prac wykonywanych przez różne osoby. Przekłada się to również na wcześniejsze wykrycie potencjalnych błędów.

Co to jest mock?

Mock  to swego rodzaju atrapa obiektu, wykorzystywana do symulowania zachowania rzeczywistego obiektu.

Mocki wykorzystywane są zazwyczaj podczas testów. Przy ich pomocy można zasymulować konkretny przypadek testowy.

Co to jest TDD?

TDD, czyli Test-driven development to technika tworzenia oprogramowania oparta o testy. Metodyka ta polega na wielokrotnym powtarzaniu poniższych kroków:

  1. Przygotowanie automatycznego testu sprawdzającego daną funkcjonalność. Ponieważ funkcjonalność jeszcze nie istnieje, test nie powinien się powieść.
  2. Podstawowa implementacja funkcjonalności, tak by spełnić warunki założone w teście.
  3. Refaktoryzacja kodu, żeby spełniał oczekiwane standardy z jednoczesną weryfikacją, czy warunki testowe są dalej spełnione.

Powyższe trzy kroki często nazywane są również: red, green, refactor.

Jakie znasz poziomy testów?

Testy jednostkowe

Weryfikacja poprawności działania pojedynczego elementu aplikacji. Do testów jednostkowych można wykorzystać np. bibliotekę JUnit.

Testy integracyjne

Weryfikacja działania połączonych elementów aplikacji.

Testy akceptacyjne

Celem testów akceptacyjnych nie jest już wykrycie błędów, a jedynie potwierdzenie jakości oprogramowania. Tego rodzaju testy są bardzo często wykonywane z udziałem klienta odbierającego produkt.

➡ ZOBACZ 👉: Testowanie oprogramowania

Jakie znasz wzorce projektowe?

Pytanie o wzorce projektowe pada bardzo często, dlatego warto nauczyć się przynajmniej kilku podstawowych wzorców:

Wzorce kreacyjne

  • budowniczy
  • fabryka
  • singleton

Wzorce strukturalne

  • adapter
  • dekorator
  • fasada

Wzorce czynnościowe

  • łańcuch odpowiedzialności
  • iterator
  • strategia
  • obserwator

Co to jest wątek (thread)?

Wątek pozwala uruchomić fragmenty kodu aplikacji współbieżnie. Dzięki wątkom można np. część operacji wykonywać asynchronicznie, nie blokując głównego przepływu aplikacji lub skończyć pracę szybciej, wykorzystując kilka wątków jednocześnie.

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getId());
    }
}).start();

System.out.println(Thread.currentThread().getId());

Powyższy fragment kodu demonstruje proste wykorzystanie wątków w Javie. Metoda run zawiera fragment logiki wykonywany w osobnym wątków, natomiast metoda start rozpoczyna działanie tego wątku.

Java Zadania praktyczne

  • Napisz kod, który wypisze 10 pierwszych liczb ciągu fibonacciego.
  • Napisz program, który wypisze liczby od 1 do 100. Dodatkowo dla wielokrotności trójki wyświetli  ‚A’ zamiast liczby, dla wielokrotności piątki wyświetli ‚B’, a dla liczb będących wielokrotnością trójki i piątki wyświetli ‚AB’.
  • Zadania w stylu „czy program się skompiluje”.
  • Zadania w stylu „co wyświetli dany program”.

Jak zostać programistą

8 rzeczy, które musisz wiedzieć, żeby dostać pracę jako programista.

Jak zostać programistą
6 komentarzy
Share:

6 Comments

  1. kszych says:

    Set gwarantuje lub nie gwarantuje zachowania kolejności w zależności od implementacji, więc jest to półprawda. Na przykład HashSet vs LinkedHashSet.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Przeczytaj poprzedni wpis:
Pytania rekrutacyjne JavaScript
Pytania rekrutacyjne JavaScript

Jest to kolejna część z serii pytań i odpowiedzi na rozmowę kwalifikacyjną Java Developera - zakres JavaScript. Ich pełne zrozumienie powinno...

Zamknij