Interview questions
4 subscribers
6 files
3 links
Answers to common Java Backend questions

My Linked in: www.linkedin.com/in/axmadullo-ubaydullayev-0738a1210

Tg: @HardWorker3334
Download Telegram
What is the difference between ConcurrentHashMap and Hashtable?

HashTable is the legacy class, it blocks the entire map till finish its job, it comes to us from the first version of java.
And the next one ConcurrentHashMap works simultaneously when it comes to write/read data to the bucket, it comes from Java8 and works, also it doesn't block anything

#concurrent_hash_map #has_table
What is an immutable class in Java?


Gotcha! Classes which one creates new element when we try change the value of current element, like String. Every new String element, even when modifying element will be store here.

When it comes to creating our own class, first thing is first, put the keyword final in class level and for all fields as well. And the value to them will given by the constructor.


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public final class ImmutableClass {
private final int value;
private final List<String> examples;

public ImmutableClass(int value, List<String> examples) {
this.value = value;
this.examples = new ArrayList<>(examples);
}

public int get() {
return value;
}

public List<String> getExamples() {
return Collections.unmodifiableList(examples);
}

}



import java.util.List;

public class Interview {
public static void main(String[] args) throws InterruptedException {
ImmutableClass immutableClass = new ImmutableClass(1, List.of("a", "b", "c"));
int i = immutableClass.get();
System.out.println(i);
System.out.println("immutableClass.getExamples() = " + immutableClass.getExamples());
}
}
What is a Thread Pool and why do we need it?

Thread pool is the place where lives our worker threads, creating new threads every time when we need can be too expensive for us, so we should use thread pools

Oh! newSingleThreadExecutor works with just one thread worker, and tasks will wait till it finishes job.

newFixedThreadPool works with fixedThread pools, it uses all the worker threads to handle with tasks, all the fixedThreads will be used and it will wait till threads will be free.

newScheduledThreadPool works with scheduling tasks, we have 2 options for working with it, scheduleAtFixedRate (if the task couldn't be handle in this fixed time next task starts automatically) and scheduleWithFixedDelay (waits fixed time before starting the next task).

#thread_pool #newSingleThreadExecutor #newFixedThreadPool #newScheduledThreadPool
What is a Work Stealing Pool?

It's a fork join pool, it works according to the logic divide and conquer. The tasks will be divided to small-enough parts. We have 2 classes RecursiveAction (it just calculates, do actions and nothing returns) or RecursiveTask<GenericType> (it returns the result)

The key concept: each worker thread has its own deque (double-ended queue) of tasks. When a thread finishes its own tasks and becomes idle, it steals tasks from the tail of another thread's queue instead of sitting idle. This keeps all threads busy.

Thread 1 queue: [task1, task2, task3, task4]
Thread 2 queue: [empty]

Thread 2 steals task4 from Thread 1's tail
→ Both threads stay busy


Absolutely, but when it comes to large tasks where accuracy is more important than code readability, we should use ForkJoinPool.commonPool() or create a dedicated ForkJoinPool instance.

When you need to configure pool size, handle exceptions properly, or isolate tasks from other parallel streams, creating your own ForkJoinPool instance is the better choice:

ForkJoinPool customPool = new ForkJoinPool(8);
customPool.submit(() -> list.parallelStream().forEach(...));



#fork_join_pool #work_steal #divide_and_conquer
What is ThreadLocal?

ThreadLocal is already thread safe and faster than synchronised blocks/methods. Every thread uses its own copies in the threads.

ThreadLocal<String> userContext = new ThreadLocal<>();

Thread t1 = new Thread(() -> {
try {
userContext.set("Axmadullo");
System.out.println(userContext.get());
} finally {
userContext.remove();
}
});

Thread t2 = new Thread(() -> {
try {
userContext.set("Sevara");
System.out.println(userContext.get());
} finally {
userContext.remove();
}
});

t1.start();
t2.start();
t1.join();
t2.join();


We should make sure that we removed the data after using it in our current thread to avoid the memory leak, stale data as well

Like in this example, Axmadullo used in first example, and Sevara in the last


#thread_local #thread_safe
How do we handle Callable tasks?

In order to achieve our desired results we need Future<GenericType> class. It works with the type returning by the Callable<GenericType>. But it blocks everytime when we get the element with .get();

ExecutorService pool = Executors.newFixedThreadPool(8);
Callable<String> callable = () -> "Callable example";

Future<String> futureResult = pool.submit(callable);

String s = futureResult.get();// blocked here
System.out.println(s);

pool.shutdown();



#callable #executorservice
How do we achieve fully asynchronous programming in Java?


Oh! Java supports CompletableFuture for this reason, it has multiple methods for working with asyncronous tasks like supplyAsync(), runAsync(), thenAccept(), thenApply, thenCombining().
It has several steps without interrupting it can show the result

The difference between thenApply() and thenCompose() in CompletableFuture is similar to the difference between map and flatMap in Java Streams.

thenApply() is used when you want to transform the result of a CompletableFuture into another value. It takes the result and applies a function to it, returning a new CompletableFuture with the transformed value.

thenCompose() is used when the next step itself returns another CompletableFuture. It allows you to chain asynchronous operations without creating a nested CompletableFuture<CompletableFuture<T>>.

In other words, thenApply() performs a simple transformation, while thenCompose() is used for asynchronous composition.

Errors in CompletableFuture can be handled using methods such as exceptionally(), handle(), or whenComplete(). For example, exceptionally() allows you to provide a fallback value if an exception occurs.


#completablefuture #asynchronous_calls #thenCompose #thenApply
Advantages of Spring

What problem does it solve?

In plain Java you manually create and manage all objects and their dependencies — lots of boilerplate code.
Spring does that for you automatically through IoC and DI.

Spring ecosystem modules:

Spring Boot — rapid app setup, no manual configuration
Spring MVC / WebFlux — web applications
Spring Security — authentication & authorization
Spring Cloud — microservices
Spring AI — AI integration


Core benefits:
Eliminates boilerplate object management
Built-in testing support
Easy to build, deploy and scale
Enterprise-ready out of the box


#spring
IoC — Inversion of Control

What is it?
The core idea of Spring — giving control of object creation to the Spring container instead of doing it manually.

Before Spring:

UserService userService = new UserService(); // you control it


With IoC:

@Autowired
UserService userService; // Spring controls it


Objects managed by the Spring container are called Beans.

The control of object creation is inverted — from the developer to the framework. That's where the name comes from.

#IoC #inversion #bean
DI — Dependency Injection

What is it?

The way Spring creates and injects beans into each other.

3 types:

1. Constructor Injection most recommended

public UserService(UserRepository repository) {
this.repository = repository;
}


Errors visible immediately at startup (Fail fast). Dependencies can be final — immutable (make sure they won't be modified).

2. Setter Injection

public void setRepository(UserRepository repository) {
this.repository = repository;
}


Used for optional dependencies.
3. Field Injection @Autowired

@Autowired
UserRepository repository;


Least recommended — hides dependencies, makes testing harder. Not just for tests, works everywhere but avoid in production code.


#DI #constructor_injection #setter_injection #field_injection
What is a Bean?

Objects which created automotically by the spring container and inversioned by it

We can use Annotations like Component, or other specific annotations (Controller, Service, Repository)
Or create beans in configuration class, using @Bean over the methods, and point @Configuration in class level.
Or Use XML way to create beans

#bean #annotation #configuration
What is the difference between ApplicationContext and BeanFactory?


BeanFactory vs ApplicationContext
BeanFactory — legacy

XML based configuration
Creates beans lazily — only when requested
Basic bean management only

ApplicationContext — modern

Extends BeanFactory — has everything it has + more
Creates beans eagerly — all at startup
Supports AOP
Supports events
Recommended for all modern Spring apps

#bean #spring #application_context #bean_factory
@Qualifier What is it?

When multiple implementations of the same interface exist, @Qualifier tells Spring exactly which one to inject.

@Autowired
@Qualifier("emailNotificationService")
private NotificationService notificationService;


Without it — Spring throws:
NoUniqueBeanDefinitionException — found 2 candidates, doesn't know which to pick.

Two ways to fix:

@Qualifier("beanName") // explicitly pick one
@Primary // mark one implementation as the default choice



#qualifier
How many ways exist to create Beans?

@Repository — adds exception translation, converts raw DB exceptions into Spring's DataAccessException. The only one with real functional difference.
@Service — purely semantic, no extra functionality. Just indicates business logic layer.
@Component — generic base annotation. Use when the class doesn't fit @Service or @Repository.

#bean #repository #service #component
What does @ComponentScan do?

It shows the location of beans where compiler should search for beans to inject

#componentScan
How many Bean scopes exist?

singleton (object creates once and uses everywhere) , prototype (new object every time) , session (used rarely)

#singleton #prototype
What is @PostConstruct and @PreDestroy?

Method with @PostContruct annotation runs after injecting the bean, and @PreDestroy method runs after closing the context

@PreDestroy — closing connections, releasing resources, cleanup before the bean dies.


@PostConstruct
public void init() {
dbConnection.open(); // runs after bean created
}

@PreDestroy
public void cleanup() {
dbConnection.close(); // runs before context closes
}


#post_construct #pre_destroy
Bean Circular Dependency in Spring
What is it?

When two beans depend on each other — neither can be created first.

Bean A needs B → Bean B needs A → 💥

Spring throws: BeanCurrentlyInCreationException:

Requested bean is currently in creation:

Is there an unresolvable circular reference?

3 ways to fix:

1. @Lazy recommended

@Component
public class A {
public A(@Lazy B b) { }
}


Delays bean creation until actually needed — breaks the cycle.

2. Setter Injection

@Autowired
public void setB(B b) { this.b = b; }


Spring creates both beans first, injects after.

3. Redesign — best long term

Extract shared logic into a third bean. Circular dependency usually signals an architecture problem.


#Java #Spring #Beans #CircularDependency
1
@Value in Spring

What is it?

Injects values from properties files or expressions directly into fields at runtime.

1. From properties file ${}

application.yml
app.servers=server1,server2,server3
app.timeout=5000



@Value("${app.servers}")
private String[] servers;

@Value("${app.timeout:5000}") // default if not found
private int timeout;


2. SpEL expressions #{}

@Value("#{2 * 3}")                         // math → 6
private int result;

@Value("#{userService.getDefaultName()}") // method call on a bean
private String name;

@Value("#{T(Math).PI}") // static field
private double pi;


Key difference:
${} reads from properties files
#{} evaluates Java expressions at runtime

#Java #Spring #Value #SpEL #Properties