Priprema za Java backend intervju

Java pitanja i odgovori za intervju 2026

Fokusirana kolekcija Java pitanja za backend intervjue. Koristite svaki odgovor kao strukturu za usmenu vežbu: koncept, primer i trade-offs.

Započni Java mock intervjuKreditna kartica nije potrebna. Dostupna je 1 besplatna sesija.
Vežba tehničkog intervjua na engleskomRežim u kome kandidati kojima engleski nije maternji mogu da vežbaju tehničke intervjue.

Java and JVM

1Šta je Java i zašto se smatra nezavisnom od platforme?

Java je statički tipiziran programski jezik opšte namene. Izvorni kod se kompajlira u bajtkod (bytecode), a bajtkod se izvršava na JVM-u (Java Virtual Machine). Isti .class ili .jar fajl se obično može izvršavati na različitim operativnim sistemima bez ponovnog kompajliranja, pod uslovom da je dostupna kompatibilna JVM. Ova nezavisnost nije apsolutna: nativne biblioteke, putanje specifične za platformu, sistemske komande i ponašanje operativnog sistema i dalje mogu učiniti aplikaciju zavisnom od platforme.

Java source -> javac -> bytecode -> JVM -> machine code
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

2Koja je razlika između JVM (Java Virtual Machine), JRE (Java Runtime Environment) i JDK (Java Development Kit)?

JVM učitava i izvršava bajtkod i upravlja memorijom, nitima i sakupljanjem smeća. JRE je istorijski predstavljao JVM zajedno sa bibliotekama neophodnim za pokretanje aplikacija. JDK obuhvata izvršno okruženje i razvojne alate kao što su javac, java, debager, javadoc, jar i dijagnostički alati. U modernim distribucijama zaseban JRE se ređe koristi jer se aplikacije često isporučuju sa prilagođenom slikom izvršnog okruženja.

JDK = development tools + runtime
Runtime = JVM + standard libraries
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

3Kako JVM (Java Virtual Machine) izvršava Java kod i zašto je potreban JIT (Just-In-Time)?

`javac` kompajlira izvorni kod u bajtkod. JVM učitava klase i počinje interpretiranje ili izvršavanje bajtkod instrukcija. Delovi koda kritični za performanse koji se često izvršavaju detektuju se tokom izvršavanja i JIT kompajler ih prevodi u optimizovani mašinski kod. JIT može da ugradi metode na mestu poziva (inlining), optimizuje petlje, primeni analizu bekstva (escape analysis) i ukloni pojedine alokacije. Zbog toga Java aplikacije mogu postati brže nakon faze zagrevanja (warm-up), i zato su mikro-testovi performansi bez JMH alata često obmanjujući.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

4Kako funkcioniše ClassLoader?

ClassLoader učitava definicije klasa u JVM (Java Virtual Machine). Uobičajeni učitavači su Bootstrap, Platform, Application i prilagođeni učitavači. Klasa se identifikuje ne samo po svom punom kvalifikovanom nazivu već i po ClassLoader-u koji ju je učitao, tako da dve klase sa istim nazivom iz različitih učitavača mogu biti različiti tipovi. Java obično koristi model delegiranja roditelju: učitavač prvo pita svog roditelja kako bi se smanjio rizik od zamene klasa platforme.

Loading
Linking: verification, preparation, resolution
Initialization
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

5Kako je organizovana memorija u JVM-u (Java Virtual Machine)?

Ključne memorijske oblasti JVM-a uključuju hip (heap) za objekte i nizove, Java stekove sa okvirima poziva za svaku nit, Metaspace za metapodatke klasa, keš koda za JIT kompajliran kod, PC registar i nativne stekove. Treba izbegavati previše pojednostavljeno shvatanje da primitivni tipovi uvek žive na steku, a objekti na hipu: polje primitivnog tipa skladišti se unutar svog objekta, a JIT analiza bekstva (escape analysis) može ukloniti alokacije ili promeniti reprezentaciju objekta.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

6Kako funkcioniše GC (Garbage Collector)?

GC oslobađa memoriju koju zauzimaju objekti koji više nisu dostižni iz GC korena (GC Roots). Koreni obuhvataju lokalne promenljive aktivnih metoda, statička polja, aktivne niti i JNI reference. Mnogi sakupljači se oslanjaju na zapažanje da većina novih objekata brzo prestaje da se koristi, pa je hip često podeljen na mladu i staru generaciju. GC ne zatvara deterministički datoteke ili mrežne veze; eksterni resursi treba da se zatvore pomoću try-with-resources konstrukcije.

try (var stream = Files.newInputStream(path)) {
    // use stream
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

OOP and types

7Koja četiri principa OOP-a (Object-Oriented Programming) Java podržava?

Java podržava enkapsulaciju, apstrakciju, nasleđivanje i polimorfizam. Enkapsulacija skriva unutrašnje stanje iza javnog API-ja. Apstrakcija se fokusira na suštinsko ponašanje. Nasleđivanje kreira novi tip na osnovu postojećeg tipa. Polimorfizam omogućava kodu da radi sa različitim implementacijama kroz zajednički ugovor. U praksi, kompoziciji se često daje prednost u odnosu na nasleđivanje jer stvara manju spregnutost.

interface PaymentService {
    void pay(BigDecimal amount);
}

class CardPaymentService implements PaymentService {
    @Override
    public void pay(BigDecimal amount) {
        // payment implementation
    }
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

8Koja je razlika između preopterećivanja metoda (method overloading) i nadjačavanja metoda (method overriding)?

Preopterećivanje (overloading) znači da više metoda ima isto ime, ali različite parametre; kompilator bira metodu na osnovu statičkih tipova argumenata. Nadjačavanje (overriding) znači da potklasa zamenjuje implementaciju instance metode iz roditeljske klase; dinamičko dispečiranje u vreme izvršavanja (runtime dispatch) bira metodu na osnovu stvarnog tipa objekta. Statičke metode se sakrivaju, a ne nadjačavaju, dok se privatne metode ne nadjačavaju jer se ne nasleđuju kao dostupni članovi.

void send(String message) {}
void send(String message, int priority) {}

class Animal {
    void speak() { System.out.println("Animal"); }
}

class Dog extends Animal {
    @Override
    void speak() { System.out.println("Dog"); }
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

9Koja je razlika između interfejsa i apstraktne klase?

Interfejs definiše ugovor i može sadržati apstraktne metode, podrazumevane (default) metode, statičke metode, konstante i privatne pomoćne metode. Apstraktna klasa dodatno može imati stanje instance, konstruktore, zaštićena (`protected`) polja, deljenu implementaciju i apstraktne ili konkretne metode. Klasa može naslediti samo jednu klasu, ali može implementirati više interfejsa. Koristite interfejs za nezavisan ugovor, a apstraktnu klasu za usko povezane tipove sa deljenom implementacijom i stanjem.

interface Repository<T> {
    Optional<T> findById(long id);
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

10Šta označava ključna reč final?

Promenljivoj označenoj sa `final` vrednost se može dodeliti samo jednom, ali `final` referenca ne čini referencirani objekat nepromenljivim. Metoda označena sa `final` ne može se redefinisati. Klasa označena sa `final` ne može se nasleđivati. Ključna reč `final` iskazuje nameru i ograničava izmenu referenci ili nasleđivanje, ali sama po sebi ne pruža potpun model nepromenljivosti niti bezbednost u višenitnom radu.

final int maxAttempts = 3;

final List<String> names = new ArrayList<>();
names.add("Alice"); // allowed

public final void validate() {}

public final class Token {}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

11Šta su record tipovi u programskom jeziku Java?

`Record` je sažeta sintaksa za nepromenljive nosioce podataka. Kompajler generiše privatna `final` polja, konstruktor, pristupne metode, `equals`, `hashCode` i `toString`. `Record` tipovi su korisni za DTO (Data Transfer Object) objekte, objekte vrednosti i rezultate metoda. Oni nisu zamena za svaki domenski entitet: validacija se može dodati u kompaktnom konstruktoru, ali složeno promenljivo ponašanje, lenjo učitavanje i modeli zasnovani na nasleđivanju obično ne odgovaraju `record` tipovima.

public record User(long id, String name) {}

public record Order(List<String> items) {
    public Order {
        items = List.copyOf(items);
    }
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

12Kako se kreira nepromenljivi objekat u programskom jeziku Java?

Označite klasu kao `final` ili pažljivo ograničite nasleđivanje, definišite polja kao `private final`, izbegavajte setere i izvršite validaciju unutar konstruktora. Za promenljive ulazne podatke kreirajte defanzivne kopije i vraćajte kopije ili nepromenljive prikaze. Duboka nepromenljivost zahteva da i celokupno ugnježdeno stanje bude nepromenljivo. Polja deklarisana kao `final` pomažu pri bezbednom objavljivanju, ali objekat i dalje može biti logički promenljiv ako izlaže promenljivu kolekciju ili promenljivi ugnježdeni objekat.

public final class User {
    private final String name;
    private final List<String> roles;

    public User(String name, List<String> roles) {
        this.name = name;
        this.roles = List.copyOf(roles);
    }

    public List<String> getRoles() {
        return roles;
    }
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

References, strings and comparison

13Da li Java prenosi argumente po vrednosti ili po referenci?

Java uvek prenosi argumente po vrednosti. Za primitivne tipove kopira se sama vrednost. Za objekte se kopira vrednost reference. Metod može da modifikuje objekat preko kopirane reference, ali ne može da zameni referencu pozivaoca drugim objektom. Ova razlika objašnjava zašto je promena polja objekta vidljiva pozivaocu, dok dodeljivanje novog objekta parametru unutar metoda nije.

void change(int value) {
    value = 100;
}

void rename(User user) {
    user.setName("Kate");
}

void replace(User user) {
    user = new User("New");
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

14Koja je razlika između == i equals()?

Za primitivne tipove, == poredi vrednosti. Za reference na objekte, == proverava da li obe reference pokazuju na isti objekat u memoriji. equals() je metod koji treba da poredi logičku jednakost ako ga klasa ispravno implementira. String, Integer, LocalDate i mnoge druge klase vrednosti redefinišu (override) metod equals. Za prilagođene domenske objekte, equals mora da odražava željenu semantiku identiteta ili vrednosti.

int a = 10;
int b = 10;
System.out.println(a == b); // true

String first = new String("java");
String second = new String("java");

System.out.println(first == second);      // false
System.out.println(first.equals(second)); // true
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

15Šta predstavlja equals/hashCode ugovor?

Ako su dva objekta jednaka prema metodi equals, moraju da vrate isti hashCode. Obrnuto nije obavezno: različiti objekti mogu imati isti hashCode. Metoda equals mora biti refleksivna, simetrična, tranzitivna, konzistentna i da vraća false za null. Kršenje ovog ugovora narušava ponašanje u HashMap, HashSet i drugim kolekcijama zasnovanim na heširanju: objekat može postati nemoguće pronaći iako se nalazi u kolekciji.

@Override
public boolean equals(Object object) {
    if (this == object) return true;
    if (!(object instanceof User other)) return false;
    return id == other.id;
}

@Override
public int hashCode() {
    return Long.hashCode(id);
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

16Zašto je String nepromenljiv?

String je nepromenljiv radi bezbednosti, predvidljivosti i performansi. Nepromenljivost omogućava interniranje niski (string interning), bezbedno deljenje među nitima, pouzdano keširanje hashCode vrednosti i bezbednu upotrebu u putanjama, URL adresama, nazivima klasa i bezbednosno osetljivim podacima. Operacije kao što su concat, replace i substring kreiraju nove niske. Od verzije Java 9, String interno može koristiti byte[] sa kompaktnom reprezentacijom, ali javni ugovor o nepromenljivosti ostaje nepromenjen.

String value = "Java";
value.toUpperCase();

System.out.println(value); // Java

String a = "java";
String b = "java";

System.out.println(a == b); // often true due to interning
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

17Koja je razlika između klasa String, StringBuilder i StringBuffer?

String je nepromenljiv, pa uzastopno spajanje u petljama može stvoriti mnogo međusobnih objekata, iako kompajler može optimizovati jednostavne slučajeve. StringBuilder je promenljiv i preporučuje se za sastavljanje niski u okviru jedne niti. StringBuffer je sličan, ali sinhronizovan, pa je obično sporiji i uglavnom relevantan za nasleđene niti bezbedne API-je. Za formatiranje, String.format ili šablonski API-ji mogu biti pregledniji, ali nisu uvek brži.

StringBuilder builder = new StringBuilder();

for (String item : items) {
    builder.append(item).append(',');
}

String result = builder.toString();

String message = "Hello, " + name;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Generics and collections

18Šta su generički tipovi (generics) i šta je brisanje tipova (type erasure)?

Generički tipovi omogućavaju pisanje tipski bezbednih kontejnera i API-ja, kao što je List<String>. Prilikom kompajliranja, kompajler proverava tipove i ubacuje eksplicitne konverzije tamo gde je potrebno. Tokom izvršavanja, većina informacija o generičkim tipovima se briše, pa List<String> i List<Integer> dele istu klasu u izvršnom okruženju. Zbog toga ne možete direktno instancirati new T(), koristiti primitivne tipove kao parametre ili pouzdano proveriti instanceof List<String>.

List<String> names = new ArrayList<>();
names.add("Alice");

List<? extends Number> producer;
List<? super Integer> consumer;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

19Šta je Java Collections Framework?

Collections Framework je skup interfejsa i implementacija za skladištenje i obradu grupa objekata. Osnovni interfejsi uključuju `List`, `Set`, `Queue`, `Deque` i `Map`. Implementacije uključuju `ArrayList`, `LinkedList`, `HashSet`, `TreeSet`, `HashMap`, `TreeMap` i `PriorityQueue`. Kolekcija se bira na osnovu obrasca pristupa, zahteva za redosledom, jedinstvenosti elemenata, složenosti pretrage, memorijskog zauzeća i potreba za konkurentnošću.

List<String> list = new ArrayList<>();
Set<Long> ids = new HashSet<>();
Map<Long, User> usersById = new HashMap<>();
Deque<String> queue = new ArrayDeque<>();
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

20Kako funkcioniše HashMap?

HashMap čuva unose u pretincima koji se određuju na osnovu heš vrednosti ključa. Prilikom pretrage, računa heš, pronalazi odgovarajući pretinac i upoređuje ključeve pomoću metode `equals`. Kolizije se rešavaju unutar pretinca; u modernoj Javi, dugački lanci mogu biti transformisani u balansirano stablo kada se ispune određeni uslovi. HashMap dozvoljava jedan `null` ključ i `null` vrednosti, nije bezbedan za rad sa više niti i automatski povećava kapacitet kada se prekorači prag faktora opterećenja (load factor).

Map<Long, User> users = new HashMap<>();
users.put(user.id(), user);

User found = users.get(user.id());
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

21Koja je razlika između HashMap, LinkedHashMap, TreeMap i ConcurrentHashMap?

HashMap pruža brz neuređeni pristup i nije bezbedna za rad sa nitima. LinkedHashMap čuva redosled umetanja ili redosled pristupa i korisna je za keševe poput LRU (Least Recently Used). TreeMap održava ključeve sortiranim prema prirodnom redosledu ili pomoću Comparator-a i ima operacije složenosti O(log n). ConcurrentHashMap je dizajnirana za konkurentan pristup uz segmentiranu/internu sinhronizaciju, ne dozvoljava null ključeve niti vrednosti i ima prednost u odnosu na synchronizedMap pri velikom opterećenju.

map.computeIfAbsent(key, ignored -> loadValue());
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

22Koja je razlika između ArrayList i LinkedList?

ArrayList je zasnovana na nizu. Pruža brz indeksirani pristup, kompaktan raspored u memoriji i efikasnu iteraciju, ali umetanje u sredinu zahteva pomeranje elemenata. LinkedList je dvostruko povezana lista. Ima jeftinije umetanje ako je čvor već poznat, ali je indeksirani pristup složenosti O(n), memorijsko zauzeće je veće, a lokalnost keša lošija. U mnogim realnim aplikacijama ArrayList je brža čak i kada su uključena određena umetanja.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

23Koja je razlika između Comparable i Comparator?

Comparable definiše prirodni redosled unutar same klase putem metode compareTo. Comparator je zaseban objekat ili lambda izraz koji definiše spoljašnje sortiranje. Comparable je koristan kada tip ima jedan očigledan podrazumevani redosled. Comparator je bolji izbor kada postoji više redosleda, na primer po imenu, datumu ili prioritetu. compare i compareTo moraju biti konzistentni i po mogućstvu kompatibilni sa metodom equals kada se koriste u sortiranim skupovima ili mapama.

class User implements Comparable<User> {
    private int age;

    @Override
    public int compareTo(User other) {
        return Integer.compare(age, other.age);
    }
}

Comparator<User> byName =
    Comparator.comparing(User::getName);

Comparator<User> byAgeThenName =
    Comparator.comparingInt(User::getAge)
              .thenComparing(User::getName);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Exceptions and resources

24Koja je razlika između proveravanih (checked) i neproveravanih (unchecked) izuzetaka?

Proveravani izuzeci nasleđuju klasu Exception, ali ne i RuntimeException, i kompajler zahteva od pozivaoca da ih obradi ili deklariše. Neproveravani izuzeci nasleđuju RuntimeException i nisu obavezni u potpisima metoda. Proveravani izuzeci odgovaraju situacijama koje se mogu oporaviti na granicama API-ja (Application Programming Interface). Neproveravani izuzeci odgovaraju programerskim greškama, neispravnom stanju i kršenju ugovora koda. U savremenom servisnom kodu timovi često izbegavaju preteranu upotrebu proveravanih izuzetaka jer se bučno šire kroz slojeve.

void load() throws IOException {
    // ...
}

throw new IllegalArgumentException("Invalid id");
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

25Koja je razlika između throw, throws i try-with-resources?

throw odmah ispaljuje izuzetak. throws deklariše mogući izuzetak u potpisu metode. try-with-resources automatski zatvara AutoCloseable resurse u obrnutom redosledu. Ako i glavni blok i close bace izuzetke, sekundarni izuzetak se čuva kao potisnuti izuzetak (suppressed exception). Ovo je bezbednije i jasnije od ručnih finally blokova kada je u pitanju više resursa.

throw new IllegalArgumentException("Invalid id");

void readFile() throws IOException {}

try (var reader = Files.newBufferedReader(path)) {
    return reader.readLine();
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Stream API and functional style

26Šta je Stream API (Application Programming Interface)?

Stream API omogućava deklarativnu obradu sekvenci podataka. Međufazne operacije kao što su filter, map i sorted su lenje; terminalne operacije kao što su toList, collect, reduce i forEach pokreću izvršavanje. Tok (stream) nije kolekcija, obično se koristi samo jednom i najbolje funkcioniše sa čistim funkcijama bez bočnih efekata. parallelStream ne garantuje brže izvršavanje i može negativno uticati na performanse ili ispravnost.

List<String> names = users.stream()
    .filter(User::isActive)
    .sorted(Comparator.comparing(User::getName))
    .map(User::getName)
    .toList();
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

27Šta je Optional i kada ga treba koristiti?

Optional<T> eksplicitno predstavlja odsustvo rezultata i najbolje se koristi kao povratni tip metode. Korisne metode uključuju map, flatMap, filter, orElse, orElseGet i orElseThrow. orElse odmah evaluira svoj argument, dok se kod u orElseGet izvršava samo kada vrednost nije prisutna. Optional se uglavnom ne preporučuje za polja entiteta, parametre metoda ili serijalizovane DTO (Data Transfer Object) objekte, osim ako za to ne postoji jak razlog.

Optional<User> user = repository.findById(id);

String name = user
    .map(User::getName)
    .orElse("Unknown");

optional.orElse(expensiveOperation());
optional.orElseGet(() -> expensiveOperation());
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Concurrency

28Šta garantuju synchronized, volatile i atomske klase?

synchronized obezbeđuje međusobno isključivanje i relaciju dešavanja pre (happens-before). volatile garantuje vidljivost najnovijih upisa i ograničava promenu redosleda instrukcija, ali ne čini složene operacije atomskim. Atomske klase obezbeđuju pojedinačne atomske operacije kao što je incrementAndGet. Koristite zaključavanja ili synchronized za više povezanih izmena, volatile za jednostavne zastavice stanja, a AtomicInteger ili LongAdder za brojače pod konkurentnim pristupom.

synchronized (lock) {
    counter++;
}

private volatile boolean running = true;

AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

29Šta su uslov trke (race condition), mrtva petlja (deadlock) i bezbednost niti (thread safety)?

Uslov trke nastaje kada rezultat zavisi od nepredvidivog redosleda izvršavanja niti. Mrtva petlja nastaje kada niti beskonačno čekaju jedna na drugu da oslobode resurse. Da bi se postigla bezbednost niti, potrebno je smanjiti deljeno promenljivo stanje, održavati dosledan redosled zaključavanja, skratiti kritične sekcije, izbegavati pozivanje spoljnog koda pod zaključavanjem, i koristiti nepromenljive objekte, konkurentne kolekcije, vremenska ograničenja ili razmenu poruka. Bezbednost niti je svojstvo celokupnog protokola za pristup stanju.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

30Koja je razlika između ExecutorService, CompletableFuture i virtuelnih niti?

ExecutorService upravlja skupom niti i izvršavanjem zadataka. CompletableFuture pomaže u komponovanju asinhronih operacija. Virtuelne niti su lake JVM (Java Virtual Machine) niti koje su pogodne za veliki broj blokirajućih I/O (Input/Output) zadataka. One pojednostavljuju model jedne niti po zahtevu, ali ne ubrzavaju zadatke koji intenzivno koriste procesor, ne uklanjaju ograničenja baze podataka ili spoljnih resursa, niti automatski čine promenljivo stanje bezbednim za niti.

try (var executor = Executors.newFixedThreadPool(4)) {
    Future<Integer> future = executor.submit(() -> calculate());
    int result = future.get();
}

CompletableFuture<User> userFuture =
    CompletableFuture.supplyAsync(this::loadUser)
        .thenApply(this::enrichUser)
        .exceptionally(error -> fallbackUser());

try (var executor =
         Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> handleRequest());
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom