RU | EN | DE

1. Task on Understanding Inheritance

The following code is given:

public class First {    
	protected int count;    
	
	public First() {        
		System.out.println("First");        
		calculate();    
	}    
	
	public void calculate() {        
		System.out.println(count);    
	}    
	@Override    
	public int hashCode() {        
		return 0;    
	}
}
 
class Second extends First {    
	public Second() {        
		this.count = 5;        
		System.out.println("Second");        
		calculate();    
	}    
	
	public void calculate() {        
		this.count++;        
		System.out.println(count);    
	}    
	
	@Override    
	public int hashCode() {        
		return 0;    
	}
}
 
class Main {    
	public static void main(String[] args) {        
		Second s = new Second();    
	}
}

Answer:

First 1
Second
6

Note:

  1. When creating Second s = new Second();, the parent class First constructor is called first
  2. In the First() constructor
    • System.out.println("First") - prints “First”
    • calculate() → calls the overridden method from class “Second”
  3. Then the “Second()” constructor executes
    • this.count = 5 → count becomes 5
    • System.out.println("Second") → prints “Second”
    • calculate() → calls “Second.calculate()” again where “count++” occurs

2. Knowledge of the Contract Between Equals and HashCode

There’s code (classes same as in the above task), what does it output:

public static void main(String[] args) {  
	HashSet<Object> set = new HashSet<>();  
	set.add(new First());  
	set.add(new Second());  
	set.add(new Second());  
	System.out.println("Size:" + set.size());
}

Answer:

Size:3

Note:

  1. new First() - added (hashCode = 0)
  2. new Second() - checked:
    • HashCode = 0 (matches)
    • equals() by default compares references → different objects → added
  3. new Second() - another new object:
    • HashCode = 0 (matches)
    • equals() compares references → this is a third unique object → added

The equals() method by default (from Object class) compares references to objects, not their content. Therefore each new Second() creates a new object with a new reference, and all are considered different.

It’s important to note that even if hashCode didn’t match, we would still get Size:3

But if we override equals and hashCode:

@EqualsAndHashCode // using Lombok annotation as example
public class First {
 
}
@EqualsAndHashCode
class Second extends First {
 
}

Then the result would be Size:2

3. HashMap Structure

I understand that HashMap has been talked about almost everywhere. I’ll tell very briefly (if you want to read in detail and dive deeper, you can look here)

Answer:

HashMap — is an array of buckets. We choose the index by hashCode, and resolve collisions (collisions) by comparisons through equals.

Insertion algorithm:

  1. Calculate hashCode() and determine bucket.
  2. If bucket is empty - insert.
  3. If bucket has elements:
    1. search for same key through equals
    2. if found → replace value
    3. if not → add new element (list → tree when >8 elements)
  4. When fullness >loadFactor, resize occurs.

4. Why Binary Trees are Needed and Their Complexity

Binary trees have also been talked about everywhere, detailed info here

Answer:

Why binary trees are needed: To store data in sorted form and quickly perform search, insertion, and deletion.

Search complexity in BST:

  • Best case (balanced): O(log n)
  • Worst case (degenerated to list): O(n)

Insertion complexity:

  • Best case: O(log n)
  • Worst case: O(n)

5. What GC Types Exist and How They Differ

I talked about this here

Answer:

Serial GC

  • Uses one thread for all GC phases.
  • Suitable for single-threaded applications and small heaps.
  • Algorithm: “copying” (in Young Gen) and “mark-sweep-compact” (in Old Gen).
  • Parameter: -XX:+UseSerialGC

Parallel GC (Throughput Collector)

  • Uses multiple threads for work in Young and Old Gen.
  • Goal — maximum throughput, not pause minimization.
  • Suitable for server applications without strict latency requirements.
  • Parameter: -XX:+UseParallelGC

CMS (Concurrent Mark Sweep) [deprecated]

  • Works in parallel with the application (concurrent), reducing stop-the-world pauses.
  • Phases: initial mark, concurrent mark, remark, sweep.
  • Doesn’t compact memory (may lead to fragmentation).
  • Deprecated starting from Java 9 and removed in Java 14
  • Parameter: -XX:+UseConcMarkSweepGC

G1 GC (Garbage First)

  • Divides heap into many regions.
  • Each region can be part of Young or Old Generation.
  • GC phases include: Initial Mark, Concurrent Mark, Remark, Cleanup, Copy.
  • Works on the principle of “collect the most garbage-filled regions first” (Garbage First).
  • Uses predictable pauses and tries not to exceed MaxGCPauseMillis
  • Supports incremental, concurrent, and compacting collection of Old Gen.
  • G1 maintains statistics on region “usefulness” and selects the most efficient ones for collection.
  • Parameter: -XX:+UseG1GC
  • Default from Java 9+

ZGC (Z Garbage Collector)

  • Supports heaps up to terabytes.
  • Works with pauses less than 10 ms, regardless of heap size.
  • Fully concurrent (almost all phases execute in parallel with the application).
  • Suitable for latency-sensitive systems.
  • Parameter: -XX:+UseZGC

Shenandoah

  • Similar to ZGC, with emphasis on short pauses.
  • Uses concurrent compacting.
  • Supported by OpenJDK.
  • Parameter: -XX:+UseShenandoahGC

6. Reference Types in Java

Answer:

Strong Reference
  • These are ordinary references we use every day.
  • As long as there is at least one strong reference to an object — it is not subject to garbage collection.
  • For an object to be collected, all strong references to it must be nulled.
Object obj = new Object();
Soft Reference
  • Object is deleted only when memory is low.
  • Used in caches to not load memory but keep the object if it’s still useful.
  • Can get the object via ref.get(), but if GC already deleted it — will return null.
SoftReference<Object> ref = new SoftReference<>(new Object());
Weak Reference
  • Object can be collected immediately, even if only weak references to it remain.
  • Used for implementing structures with auto-deletion (e.g., WeakHashMap).
  • Often applied when the object should be available “as long as someone needs it”.
WeakReference<Object> ref = new WeakReference<>(new Object());
Phantom Reference
  • Object is already marked for deletion but not yet collected by GC.
  • The get() method always returns **null**.
  • Used for controlling finalization and releasing resources outside heap (e.g., off-heap, native).
  • Requires ReferenceQueue, through which you can know that the object is about to be deleted.
PhantomReference<Object> ref = new PhantomReference<>(new Object(), referenceQueue);

7. Name 5 Classes from the Concurrent Package and What They’re For

Answer

  1. CompletableFuture - allows launching async tasks, combining them, chaining callbacks, and working without blocking. Simplifies parallelism.
  2. ConcurrentHashMap - thread-safe HashMap. Allows many threads to simultaneously read and update data without a big common lock.
  3. Phaser - advanced synchronizer, allows synchronizing threads by phases (stages). More flexible than CyclicBarrier/CountDownLatch.
  4. AtomicInteger - primitive for atomic operations over int without using locks (CAS). Needed for counters, flags, and increments between threads.
  5. ReentrantLock - explicit lock with extended capabilities: tryLock, fairness, condition variables. More flexible alternative to synchronized.

8. Tell About DeadLock and LiveLock

Answer

Deadlock (mutual blocking)

Threads block each other forever, each waiting for a resource held by the other. Result: system stands still, no progress.

Example: Thread A holds resource 1 and waits for resource 2. Thread B holds resource 2 and waits for resource 1.

How to avoid:

  1. Always lock resources in the same order.
  2. Use timeouts when capturing locks (tryLock(timeout)).
  3. Minimize the number of simultaneously captured locks.
Livelock (animated blocking)

Threads are not blocked but move uselessly, constantly trying to avoid conflict and interfering with each other. Result: system works, but also no progress.

Example: Two threads yield resource to each other, refuse, and try again, but synchronously and endlessly.

How to avoid:

  1. Add random delays or exponential backoff on repeated attempts.
  2. Use explicit timeouts and stop attempts after certain time.
  3. Rethink cooperation algorithm to not continuously block each other.

9. SQL Task

There’s this DB structure: Need to write a query that returns client name and total product sum. Client must be active and product sum greater than 0

Answer

SELECT c.name AS company_name, SUM(p.price) AS product_sum
FROM client c
JOIN product p ON c.id = p.client_id
WHERE c.is_active
GROUP BY c.id, c.name
HAVING SUM(p.price) > 0
ORDER BY product_sum DESC;

10. What’s the Difference Between having and where

Answer

Time of application

  • WHERE - filtering before grouping (at the level of individual rows)
  • HAVING - filtering after grouping (at the group level) What they work with
  • WHERE - works with individual records and normal fields
  • HAVING - works with results of aggregate functions (SUM, COUNT, AVG, etc.) Use with GROUP BY
  • WHERE - can be used without GROUP BY
  • HAVING - used together with GROUP BY

11. What is explain plan and What It’s For

Answer

EXPLAIN PLAN — is an execution plan for an SQL query, showing what operations the DBMS will perform (table scan, index usage, join types, etc.). It’s needed to understand where bottlenecks are and what can be optimized — for example, add an index, change join type, or rewrite the query.

12. What Transaction Isolation Levels Exist and What Problems Are Present in Them?

Answer

PostgreSQL: Documentation: 18: 13.2. Transaction Isolation

13. Understanding Proxy in Spring

There are base tasks with transactions, but in this interview they asked me about @Cacheable. There’s a code example where cache doesn’t work, how to make it work:

@Service@EnableCaching
public class CacheClass {        
 
	@SneakyThrows    
	@PostConstruct    
	public void init() {        
		System.out.println(test(1));        
		Thread.sleep(1000);        
		System.out.println(test(2));        
		Thread.sleep(1000);        
		System.out.println(test(1));    
	}    
	
	@Cacheable(cacheNames = "test", key = "#integer")    
	public String test(int integer) {        
		return LocalDateTime.now().toString();    
	}
}

Answer

Here you can immediately suggest several options analogous to transactions:

  1. Do self-inject and call method through it
  2. Use applicationContext.getBean(CacheClass.class) For @Transaction this would 100% work, but in Cacheable there’s a pitfall. None of the above options will work. Think more about how to solve this?
Answer for Cacheable

For @Cacheable the situation is even worse: cache aspect initializes later, so calling @Cacheable from @PostConstruct usually doesn’t work — there’s an explicit issue about this in Spring

Here you can apply ApplicationRunner or CommandLineRunner, which can help, just implement it:

@Service
@EnableCaching
public class CacheClass implements ApplicationRunner {
 
    @Autowired
    @Lazy
    private CacheClass self;
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(self.test(1));
        Thread.sleep(1000);
        System.out.println(self.test(2));
        Thread.sleep(1000);
        System.out.println(self.test(1));
    }
 
    @Cacheable(cacheNames = "test", key = "#integer")
    public String test(int integer) {
        return LocalDateTime.now().toString();
    }
}

14. Give an Example of Each Pattern Type (Behavioral, Creational, Structural) and Name Your Favorite Pattern and How You Applied It.

Answer

My favorite pattern is Template Method. Template Method is used when there’s a general algorithm that repeats for different handlers, but some steps in it must differ. The general logic is fixed in the base class, and the changing parts are extracted into abstract methods, and specific implementations determine what happens in these stages. This avoids code duplication and clearly separates general and differing algorithm steps. This pattern has saved me more than once)

15. When to Use Relational Databases and When Not. What’s the Difference Between SQL and NoSQL Database

Answer

SQL DB:

  1. Complex queries and JOIN — when aggregations and relationships between tables are needed
  2. ACID transactions — banking operations, financial systems
  3. Structured data — clear schema, predictable structure
  4. Data integrity — foreign keys, constraints
  5. Reporting and analytics — complex SQL queries Suitable for: banking systems (transactions), medical records (data integrity), online stores (orders, inventory)

NoSQL DB:

  1. Large data volumes — Big Data, logging
  2. Horizontal scaling — distributed systems
  3. Flexible schema — frequently changing data structure
  4. High write performance — IoT, sensors, clickstream
  5. Unstructured data — JSON, documents, graphs Suitable for: projects working with documents, where need to process huge data volumes (e.g., I know VK Video uses Cassandra as DB), data structure changes frequently

16. System Design Task

We work in an insurance company and provide clients with policy services. When a request comes from a client, we need to query an external SOAP system via HTTP to get necessary data for calculations. According to requirements, we must give the client a response within two minutes. If the calculation isn’t completed in this time — we must return an error.

The problem is that this external SOAP system on average responds in about 40 seconds, but works unstably: sometimes very slow, sometimes doesn’t respond at all. At the same time, expected load — up to 15,000 requests per second, meaning the system must withstand high request flow and not depend on its instability.

I’ll say right away, in this task there’s no clear single answer. I’ll tell my answer:

Answer

I’ll go in order:

  1. I’d make a gateway for user authorization and authentication (OAuth, Keycloak)
  2. Asynchronous model (event architecture) - SOAP system is unstable, gives response +-40 sec, SLA ≤ 2 minutes - async approach is maximally justified. Client doesn’t need to wait - you give taskId and they check status
  3. Microservice architecture, I highlighted several services:
    1. Main-service - only business logic: task creation, status storage, orchestration.
    2. Adapter-service - work with external system, retries, circuit breaker.
    3. Timeout-service - only timers and SLA. (I’d use delay queue for timer tracking)
    4. Gateway-service - authorization, authentication, user redirection
  4. In Adapter-service need retries, circuit breaker, fallback
  5. Main-service processes all logic, also listens to response from adapter-service and timeout-service + main stores info about messages (outbox table)
  6. Timeout-service can be implemented using delay queue for timer tracking
  7. We’ll use Kafka + outbox + de-dup table to guarantee delivery and processing once.
    1. Outbox guarantees event delivery.
    2. Message IDs — protection from duplicates in consumer.
    3. Kafka — will handle your TPS with huge reserve.
  8. For state tracking can use distributed tracing
  9. Archive topic (fan-out topic) - we discard messages to separate topic that nobody is listening to at the moment, so there’s possibility to connect to it and listen to all messages, even if Kafka already deleted them from another topic. (This can be done if future service addition processing is visible and there are additional resources for it)
  10. If future client growth is visible, can use Kafka Streams for working with big data

Disadvantages of this approach:

  • more complex logging
  • more complex tracing
  • load on infrastructure
  • more complex testing

Advantages of this approach:

  • scalability
  • flexibility / extensibility
  • fault tolerance
  • high throughput

Approximate service interaction scheme:

Conclusion

Today we went through an example of a complete interview for a Senior Java Developer position. Of course, this is not a universal scenario - each interview has its nuances. But this is real experience that I had, and I tried to show what questions and situations can occur and what to pay special attention to. I hope this will help you prepare and approach the interview more confidently.