My Introduction for Sonar Interview

February 16, 2026 - Geneva Position

1. About Me

Hello, my name is Haythem Rehouma. I hold a Ph.D. in Applied Engineering from ETS Montreal, and I have a broad background spanning programming, engineering, and education. I'm passionate about developing robust software solutions and sharing knowledge to empower the next generation. It's great to introduce myself to you today.

I am a Canadian citizen based in Montreal, with 18 years of professional experience in the technology field. My academic journey includes a Master's degree in Artificial Intelligence from UQAM and another Master's in Communication Systems. This diverse education has given me a strong foundation in both theory and practice.

Throughout my career, I've worked as a software engineer, a researcher, and an educator. I believe that the best engineers are those who can not only write code, but also communicate clearly, collaborate effectively, and continuously learn. These are values I try to embody every day.

2. About Sonar

SonarSource is known for tools that help ensure code quality and security. Their most popular product is SonarQube, which analyzes source code to detect bugs, vulnerabilities, and code smells. It supports a wide range of programming languages and integrates seamlessly into CI/CD pipelines, helping teams continuously maintain high-quality code.

In short, Sonar is all about clean, maintainable, and secure code. The company was founded with a simple but powerful mission: to help developers write better code. Today, their products are used by over 7 million developers worldwide, including teams at Microsoft, NASA, and MasterCard.

What I find particularly inspiring is Sonar's commitment to the open source community. SonarQube started as an open source project, and this spirit of transparency and collaboration remains at the heart of the company's culture.

3. Why I Want to Join Sonar

I want to join Sonar because I genuinely believe in the mission. I have been using SonarQube in my own projects for years, and I've seen firsthand how it improves code quality and helps teams catch issues early. The idea of contributing to a product that I already use and trust is incredibly motivating.

I'm also drawn to Sonar's CODE culture, which stands for Smarter Together, Excellence, Innovation, and Delivery. These values resonate deeply with me. I believe that no one is as smart as all of us working together, and I strive for excellence in everything I do.

Finally, Sonar wants to be a human enterprise, a place where people come first. That's the kind of company I want to work for. I'm looking for a place where I can grow professionally while also contributing to something meaningful.

4. My Java Experience

On the backend, I have extensive experience with Java and Spring Boot. I've built REST APIs, microservices architectures, and integrated various AWS services. I follow clean code principles and design patterns like Repository, Service, and Controller to keep my code maintainable and testable.

I write unit tests with JUnit and Mockito, and I write integration tests for my APIs. I use Maven and Gradle for build management. I integrate SonarQube into my Java projects to track code coverage and catch issues early.

For databases, I have experience with PostgreSQL, MySQL, and NoSQL databases like DynamoDB. I understand how to design efficient schemas, write optimized queries, and ensure data integrity. I also have experience with database migrations and versioning.

5. My React Experience

I've been teaching React since before 2019, so I experienced the era of class components and lifecycle methods. After 2019, the shift to functional components and hooks, like useState and useEffect, brought a more streamlined, declarative approach. I've guided students through that transition—from class-based patterns to the modern, hook-based paradigm we rely on today.

I work with TypeScript for type safety. I build responsive and accessible user interfaces. I follow component-based architecture and understand state management patterns. I write unit tests for my React components.

I work closely with UX designers to implement features. I understand the importance of a good user experience and focus on delivering polished products. I've taught React to hundreds of students, which has deepened my understanding of the framework and its best practices.

6. My AWS Expertise

I have solid experience with AWS, where I've designed and deployed robust cloud architectures. I've worked with services like EC2, S3, and Lambda, and I've automated infrastructure through IaC tools like CloudFormation and Terraform. This hands-on experience has given me a deep understanding of cloud-native development.

I hold four AWS certifications, including Solutions Architect Professional and DevOps Professional. But more than certifications, I have real-world experience building and deploying production applications on AWS.

I am also an AWS-Authorized Instructor. This is a restricted certification from Amazon—only qualified professionals who meet strict requirements can obtain it. I teach official AWS courses, which has deepened my understanding of the platform and best practices.

In my projects, I've used services like VPC, RDS, Elastic Beanstalk, SQS, SNS, and API Gateway. I set up CI/CD pipelines with CodePipeline and implement security best practices with IAM and KMS. I understand how to build secure, scalable, and cost-effective cloud architectures.

This experience with AWS aligns well with Sonar's cloud offerings. I understand how to deploy, monitor, and scale applications in the cloud, and I can contribute to building robust cloud-native solutions.

7. Teamwork

I am a strong team player. I believe that the best results come from collaboration, not isolation. When I work on a project, I make sure to communicate clearly with my teammates, share knowledge, and support others when they need help.

I understand that in a team, prioritization is essential. Even though I am a perfectionist by nature, I have learned that we cannot do everything at once. We need to focus on what brings the most value first. I always ask myself: what is the most important task right now? What will have the biggest impact?

I also believe in transparency. If I am stuck or if something is taking longer than expected, I communicate early. I don't wait until the last minute. This way, the team can adjust and we avoid surprises.

For me, being a good team player means putting the team's success above personal achievements. When the team wins, we all win.

8. My Weaknesses

One area I'm working on is my tendency to be a perfectionist. Sometimes I spend too much time trying to make something perfect when "good enough" would be acceptable. I've learned to balance quality with delivery. I now set clear deadlines for myself and focus on what truly matters.

Another thing I'm improving is delegation. In the past, I sometimes took on too much because I wanted to make sure everything was done correctly. I've learned that trusting my teammates and letting them take ownership is better for everyone. It helps the team grow and prevents burnout.

I can also be too direct in my communication. I value honesty, but I've learned to balance directness with diplomacy. I now take more time to consider how my words might be received, especially in written communication.

I see these as areas for continuous improvement. I'm aware of them, and I actively work on getting better every day.

9. Conflict Handling

Let me share how I would handle a difficult situation. Imagine a colleague goes on vacation and leaves some work unfinished. The deadline is approaching, and we need to deliver.

First, I would not call them during their vacation. Everyone deserves to disconnect and rest. Interrupting their time off would damage trust and morale.

Instead, I would assess the situation. What exactly needs to be done? Is there documentation? Can I understand the work from the code or notes they left behind?

Then, I would take ownership of the task. If it's within my capabilities, I would complete it myself or ask a teammate for help. The goal is to meet the deadline without creating stress for the person on vacation.

When my colleague returns, I would have a calm conversation. I would explain what happened, not to blame them, but to understand how we can prevent this in the future. Maybe we need better handoff processes. Maybe we need to plan better before vacations.

The key is to focus on solutions, not blame. We're all human, and things happen. What matters is how we handle them as a team.

10. Why You Should Hire Me

You should hire me because I bring a rare combination of deep academic expertise and practical experience. I've built robust solutions, led teams, and taught future professionals, so I know how to both deliver results and empower others. I'll contribute not just with technical depth, but also with a commitment to quality and innovation.

I have the technical expertise you're looking for—over four years of Java experience, over three years of React and TypeScript, and deep AWS knowledge with certifications and teaching experience. I understand the full stack, from database design to user interface.

Beyond technical skills, I bring communication abilities that come from years of teaching. I can explain complex concepts clearly, mentor junior engineers, and collaborate effectively with cross-functional teams. I understand the importance of documentation and knowledge sharing.

I also bring a genuine passion for code quality. I don't just write code that works—I write code that is clean, maintainable, and well-tested. I've used SonarQube extensively, and I understand its value. I would be proud to contribute to a product that helps millions of developers write better code.

Finally, I am committed and reliable. When I take on a project, I see it through to completion. I take ownership of my work and hold myself to high standards. I'm ready to make a strong commitment to Sonar and contribute to the team's success.

11. Why Sonar Impresses Me

I am genuinely impressed by SonarSource. In the era of artificial intelligence, where tools like Copilot and ChatGPT can generate code in seconds, the need for clean and secure code has never been more important.

AI can write code quickly, but it cannot fully replace human expertise when it comes to verifying quality and security. That's where Sonar comes in. Your tools help developers catch bugs, vulnerabilities, and code smells that AI might introduce. This is critical in today's fast-paced development environment.

I believe Sonar is positioned at the intersection of AI and human expertise. As more code is generated by AI, the demand for quality analysis will only grow. Sonar's mission to help developers write better code is more relevant than ever.

What also impresses me is your commitment to the developer community. Over 7 million developers trust your tools. Companies like Microsoft, NASA, and MasterCard use SonarQube. That's a remarkable achievement.

I want to be part of a company that is shaping the future of software development. Sonar is doing exactly that, and I would be honored to contribute.

12. Closing

Thank you for taking the time to learn about me. I'm genuinely excited about this opportunity to join Sonar and contribute to the Storefront team in Geneva.

I believe my skills, experience, and values align perfectly with what you're looking for. I bring technical depth in Java, React, and AWS. I bring teaching experience that translates into mentorship and clear communication. And I bring a genuine passion for code quality.

What excites me most is the opportunity to work on products that help millions of developers write better code. In a world where AI is changing how we develop software, Sonar's mission is more important than ever. I want to be part of that mission.

I am ready to make a strong commitment to Sonar. I am ready to learn, to contribute, and to grow with the company. I look forward to the next steps in the process.

Thank you again for this opportunity. I hope to speak with you soon.

Bonus 1 — Handling a Disagreement

Once, my colleagues and I disagreed about a project email that was sent out. They felt it wasn't clear, while I thought it was fine.

We sat down and discussed how it was interpreted. I listened carefully to their perspective. They explained that some phrases could be misunderstood by the client, and I realized they had a valid point.

I acknowledged their perspective, and we adjusted the wording together. Instead of insisting I was right, I focused on what would produce the best outcome for the team and the client.

The final version had a much better impact. The client responded positively, and the communication was clearer for everyone. It was a good reminder that collaboration always leads to better results than working in isolation.

What I learned from this experience is that being open to feedback is not a weakness — it's a strength. When we combine different perspectives, the result is always stronger.

Bonus 2 — A Colleague Raised a Concern About My Work

Early in my career, around 2010, I was working on the backend for an HR module. A colleague on the frontend needed my API to finish his part. We had a demo the next morning.

I ran into an unexpected issue and delivered my part about 30 minutes later than planned. It was tight. My colleague had less time than expected to integrate, and he raised a concern about the timing.

The demo went well. But afterward, we had a good conversation. He explained that even a small delay can create pressure when people depend on each other. I appreciated his honesty.

That conversation stuck with me. Since then, I always build a buffer before deadlines and communicate early if anything takes longer than expected. Today, this kind of situation doesn't happen anymore. If I see any risk, I warn the team right away.

What I learned: good communication and planning prevent most problems. When people depend on your work, giving them a heads-up early makes all the difference.

Bonus 3 — A Time I Introduced a Bug

I was working on a platform built with Java and Spring Boot. Some pages were only for paying users. If you didn't pay, the system should block you and say "access denied".

During testing, I noticed something wrong. I was able to access a paid page without paying. The door was supposed to be locked, but it let me in. That was a security problem.

I looked into it and found that the protection was only on the client side. It was easy to bypass — anyone with basic knowledge could skip it and access the paid content. The real security check on the server side was missing for that page.

I fixed it right away. I added the security check, made sure unpaid users are now blocked, and verified all other paid pages to make sure nothing else was open.

I also added automatic checks — so every time someone adds a new paid page, the system will verify it is properly protected. This way, the same mistake cannot happen again.

No user was impacted because I caught it before it went live.

What I learned: bugs happen, but what defines a senior engineer is how quickly you respond, how honestly you communicate, and what systems you put in place so it doesn't happen again.

Bonus 4 — A Challenging Situation I Managed

As a supervisor, I was responsible for the database program in my department. Two professors who were supposed to work together on the same course had a disagreement. They had different views on how to teach the material, and they stopped collaborating. The program was stuck.

I decided to step in. I talked to each of them separately first. I listened. I wanted to understand what each person really cared about — not just the method, but the reason behind it.

One wanted to teach everything with PostgreSQL — he said it was modern and open source. The other preferred Oracle — he said it was the industry standard and students needed it for jobs. They couldn't agree on the course plan or the tools.

I brought them together and said: "You actually want the same thing — students who are strong in databases." I suggested we teach the core concepts first, then use PostgreSQL for one part and Oracle for another. Students would learn both. Each professor would lead the tool he knew best.

They both agreed. The course went well, students got the best of both approaches, and the two professors started trusting each other. They even collaborated on the next semester's material together.

What I took away from this experience: when facing a challenging situation, the key is to stay calm, listen to both sides, and find common ground. Most conflicts are not about who is right — they are about people caring about different things. A good mediator helps the team see that.

Bonus 5 — People & Culture (The Hidden Key)

One thing I learned while preparing is that at Sonar, the People and Culture interview is extremely important. A candidate shared that he passed all the technical stages, finished the coding task, covered it with tests, and got positive feedback. But he was rejected after the People and Culture stage.

That tells me something clear: at Sonar, who you are matters as much as what you can code.

I respect that. I believe a strong team is not just about skills — it's about trust, communication, and shared values.

Here is how I see culture: I am someone who listens before speaking. I don't need to be the smartest person in the room. I want to be the person who makes the room work better together.

I value honesty. If I make a mistake, I say it. If I disagree, I say it respectfully. If a teammate needs help, I help — even if it's not my task.

I also believe in continuous learning. I teach, but I also learn from my students, from my colleagues, from every code review. Nobody knows everything, and that's okay.

Sonar's values — Smarter Together, Excellence, Innovation, and Delivery — are not just words on a wall for me. They describe how I already work every day.

I am ready to bring my energy, my humility, and my commitment to the Sonar team.

Bonus 6 — Why Do You Want to Join Us?

I want to join Sonar because I believe in what you do. I have used SonarQube in my own projects. I have seen how it catches problems early and helps teams write better code. I want to work on a product I already trust.

I also love the mission. In a world where AI generates more and more code, the need for quality and security analysis is growing fast. Sonar is at the center of that.

Beyond the product, I am drawn to the culture. Sonar values collaboration, excellence, and putting people first. That's how I like to work. I want to be somewhere I can grow, contribute, and be proud of what I build every day.

And honestly — I want to be part of a team that cares about clean code as much as I do.

Bonus 7 — Tell About a Disagreement

Once, my colleagues and I disagreed about a project email that was sent out. They felt it wasn't clear. I thought it was fine.

We sat down and discussed how it was interpreted. I listened. They showed me that some sentences could confuse the client. I realized they had a good point.

I didn't insist. We adjusted the wording together. The final version was much clearer and the client responded positively.

What I learned: being open to feedback is not a weakness — it's a strength. When you combine different perspectives, the result is always better.

Bonus 8 — How Would People From Your Last Companies Describe You?

I think they would say three things about me.

First, that I am reliable. When I say I will do something, I do it. If there is a problem, I communicate early. People know they can count on me.

Second, that I am helpful. I don't just focus on my own tasks. If a colleague is stuck, I help — even if it's not my responsibility. I believe the team's success is more important than individual performance.

Third, that I am calm under pressure. When things go wrong — a bug in production, a tight deadline, a last-minute change — I stay focused. I don't panic. I find the solution first and discuss the process after.

And if I'm being honest, they would probably also say I can be a bit of a perfectionist. But I've learned to balance that — deliver first, improve after.

My Questions for Sonar

— What does career growth look like for engineers at Sonar?

— What do people enjoy the most about working at Sonar?

— Does the team do any team building activities or events to connect outside of work?

Interview 2 — OOP Concepts

Core Object-Oriented Programming principles explained simply

Encapsulation

Encapsulation means hiding an object's internal state and allowing access only through controlled methods.

It helps protect data from direct or invalid modification.

In practice, this is often done with private attributes and public getters or setters.

Abstraction

Abstraction means showing what an object can do without exposing how it does it internally.

It simplifies complexity by focusing only on the essential features.

This is commonly achieved with interfaces or abstract classes.

Inheritance

Inheritance means a class can receive properties and behaviors from another class.

It allows code reuse and helps create relationships between parent and child classes.

A child class can also add new features or redefine inherited behavior.

Polymorphism

Polymorphism means the same interface or method name can produce different behaviors depending on the object.

It allows flexibility and makes code easier to extend.

This is useful when different classes share a common structure but act in their own way.

Java Senior Interview — Cheat Sheet

20 must-know concepts • One sentence each • Memorize these

1. Encapsulation

Hides internal state and controls access through methods.

2. Abstraction

Exposes what an object does, not how it does it.

3. Inheritance

Allows a class to reuse and extend another class.

4. Polymorphism

Allows one interface to have multiple implementations.

5. Interface

Defines a contract that classes must implement.

6. Abstract class

Provides shared structure and partial implementation for subclasses.

7. Lambda

A short anonymous function used to pass behavior as data.

8. Stream

A pipeline for processing data declaratively step by step.

9. Optional

A container that may or may not hold a value.

10. Overriding

Redefining inherited behavior in a subclass.

11. Overloading

Using the same method name with different parameters.

12. Static

A member that belongs to the class, not the instance.

13. Final

Something that cannot be changed, overridden, or extended.

14. Immutable Object

An object that cannot be modified after creation.

15. equals()

Checks logical equality between objects.

16. hashCode()

Returns an integer for efficient lookup in hash-based collections.

17. Exception

An event that interrupts normal execution because of an error.

18. Dependency Injection

Provides dependencies from outside the class.

19. Singleton

Ensures that only one instance of a class exists.

20. Spring Bean

An object managed by the Spring container.

Full Java Glossary

50+ concepts • One sentence each • Complete reference

OOP & Core Concepts

Encapsulation — Hides internal state and controls access through well-defined methods.

Abstraction — Exposes essential behavior while hiding internal implementation details.

Inheritance — Allows a class to reuse and extend the behavior of another class.

Polymorphism — Allows the same interface or method call to behave differently depending on the object type.

Object — An instance of a class containing state and behavior.

Class — A blueprint used to create objects.

Interface — Defines a contract that implementing classes must follow.

Abstract class — Provides shared structure and partial implementation for subclasses.

Constructor — Initializes an object when it is created.

Method Overloading — Defining methods with the same name but different parameter lists.

Method Overriding — Redefining inherited behavior in a subclass.

Association — A relationship where one object uses or knows another object.

Aggregation — A weak whole-part relationship where parts can exist independently.

Composition — A strong whole-part relationship where parts depend on the whole.

Java 8+ / Functional

Lambda — A concise anonymous function used to pass behavior as data.

Stream — A pipeline used to process collections declaratively step by step.

Optional — A container object used to represent the presence or absence of a value.

Method Reference — A shorter way to refer to an existing method in lambda-style code.

Functional Interface — An interface with exactly one abstract method.

Object Behavior / Memory / Keywords

Static — A member that belongs to the class rather than to a specific instance.

Final — A variable cannot be reassigned, a method cannot be overridden, or a class cannot be extended.

Immutable Object — An object that cannot be modified after creation.

Enum — A fixed set of predefined constant values.

this — Refers to the current object instance.

super — Refers to the parent class and is used to access inherited members.

Collections / Equality / Utility

equals() — Checks logical equality between two objects.

hashCode() — Returns an integer used for efficient lookup in hash-based collections.

Comparable — Defines a natural ordering for objects using compareTo().

Comparator — Defines custom sorting logic separate from the class itself.

Generic — Lets you write reusable and type-safe code.

Collection — A structure used to group and manage multiple objects.

List — An ordered collection that allows duplicates.

Set — A collection that does not allow duplicate elements.

Map — Stores key-value pairs where each key is unique.

Exceptions / Threads / Architecture

Exception — An event that disrupts normal program execution due to an error.

Checked Exception — Must be handled or declared at compile time.

Unchecked Exception — Occurs at runtime and is not required to be declared.

Thread — An independent path of execution within a program.

Concurrency — The ability to manage multiple tasks that may run at overlapping times.

Synchronization — Controls concurrent access to shared resources.

Dependency Injection — Provides required dependencies from outside the class.

Singleton — Ensures that only one instance of a class exists during the application lifecycle.

Spring / Backend Classics

Spring Bean — An object created, managed, and wired by the Spring container.

IOC — The framework manages object creation and dependency flow instead of the programmer.

REST API — Exposes resources over HTTP using standard methods like GET, POST, PUT, DELETE.

DTO — An object used to transfer data between layers without exposing domain logic.

Entity — A persistent domain object usually mapped to a database table.

Repository — Abstracts data access logic from business logic.

Service — Contains business logic and coordinates operations between components.

Controller — Handles incoming requests and returns responses.

Senior Java Concepts

20 advanced concepts every senior engineer must know • One sentence each

1. Resilience

The ability of an application to keep working reliably even when components fail or external services are down.

2. Circuit Breaker

A pattern that stops calling a failing service after repeated failures, waits for a cooldown, then tests again to prevent cascading failures.

3. Exponential Backoff

A retry strategy where each attempt waits progressively longer (1s, 2s, 4s, 8s) to give a failing system time to recover.

4. Fallback

A backup plan that provides an alternative response (cached data, simplified result) when a component fails.

5. Idempotency

An operation that produces the same result no matter how many times it is executed (GET, PUT, DELETE are idempotent; POST is not).

6. CQRS

Command Query Responsibility Segregation — separate read and write models for better scalability and performance.

7. Event Sourcing

Instead of storing current state, store a sequence of events that led to that state — enables full audit trail and replay.

8. Saga Pattern

A pattern for managing distributed transactions across microservices using a sequence of local transactions with compensating actions on failure.

9. Caching

Storing frequently accessed data in a fast-access layer (Redis, in-memory) to avoid expensive database or API calls.

10. Rate Limiting

Controlling the number of requests a client can make in a time window to protect the system from overload or abuse.

11. Pagination

Returning data in small chunks (pages) instead of everything at once to reduce memory and network usage.

12. Connection Pooling

Reusing a pool of pre-created database connections instead of creating a new one for each request (HikariCP in Spring Boot).

13. Microservices

An architecture where the application is split into small, independent services that communicate over APIs and can be deployed separately.

14. API Gateway

A single entry point for all client requests that routes, authenticates, rate-limits, and load-balances across microservices.

15. Service Discovery

A mechanism that allows services to find each other dynamically without hardcoded URLs (Eureka, Consul, Kubernetes DNS).

16. Distributed Tracing

Tracking a request across multiple services to understand latency and debug issues (Zipkin, Jaeger, OpenTelemetry).

17. SOLID Principles

Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion — five principles for clean, maintainable code.

18. DDD (Domain-Driven Design)

Structuring code around business domains using entities, value objects, aggregates, and bounded contexts.

19. CAP Theorem

A distributed system can only guarantee two out of three: Consistency, Availability, and Partition Tolerance.

20. 12-Factor App

A set of best practices for building cloud-native apps: config in env vars, stateless processes, port binding, logs as streams, disposability, etc.

Interview 3 — Design Patterns

23 Gang of Four design patterns explained simply • Creational • Structural • Behavioral

1. Singleton (Creational)

Singleton ensures that a class has only one instance during the execution of a program.

It provides a single global access point to that instance.

This pattern is useful when exactly one shared object is needed, such as for configuration or logging.

2. Factory Method (Creational)

Factory Method defines a way to create objects without specifying their exact concrete class directly.

It delegates the creation process to a method, making the code more flexible.

This pattern is useful when the type of object to create may vary depending on the situation.

3. Abstract Factory (Creational)

Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes.

It helps ensure that compatible objects are used together.

This pattern is useful when a system must support multiple product families consistently.

4. Builder (Creational)

Builder separates the construction of a complex object from its final representation.

It allows an object to be created step by step.

This pattern is useful when an object requires many configuration options or construction stages.

5. Prototype (Creational)

Prototype creates new objects by copying an existing object instead of building one from scratch.

It is useful when object creation is expensive or complex.

This pattern makes cloning possible while preserving the structure of the original object.

6. Adapter (Structural)

Adapter allows two incompatible interfaces to work together.

It acts as a bridge between an existing class and the interface expected by the client.

This pattern is useful when reusing old code inside a new system.

7. Bridge (Structural)

Bridge separates an abstraction from its implementation so both can evolve independently.

It reduces tight coupling between high-level logic and low-level details.

This pattern is useful when both sides may change over time.

8. Composite (Structural)

Composite organizes objects into tree structures so that individual objects and groups can be treated the same way.

It simplifies the handling of hierarchical data.

This pattern is useful for menus, folders, and nested structures.

9. Decorator (Structural)

Decorator adds new behavior to an object dynamically without changing its original class.

It wraps the object and extends its functionality from the outside.

This pattern is useful when features must be combined flexibly.

10. Facade (Structural)

Facade provides a simplified interface to a complex subsystem.

It hides internal complexity and makes the system easier to use.

This pattern is useful when users only need a clear and simple entry point.

11. Flyweight (Structural)

Flyweight reduces memory usage by sharing common data between many similar objects.

It avoids storing duplicated information repeatedly.

This pattern is useful when a system creates a very large number of lightweight objects.

12. Proxy (Structural)

Proxy provides a substitute object that controls access to another object.

It can be used for security, lazy loading, or performance optimization.

This pattern is useful when direct access to an object should be managed carefully.

13. Chain of Responsibility (Behavioral)

Chain of Responsibility passes a request through a sequence of possible handlers until one processes it.

It reduces coupling between the sender and the receiver.

This pattern is useful when multiple objects may handle the same request.

14. Command (Behavioral)

Command turns a request into an object.

It separates the sender of the request from the object that executes it.

This pattern is useful for undo systems, task queues, and action history.

15. Interpreter (Behavioral)

Interpreter defines a way to evaluate sentences or expressions in a language.

It represents grammar rules as classes or objects.

This pattern is useful for simple language processing or rule-based systems.

16. Iterator (Behavioral)

Iterator provides a way to access elements one by one without exposing the collection's internal structure.

It separates traversal logic from the collection itself.

This pattern is useful for uniform access to different data structures.

17. Mediator (Behavioral)

Mediator centralizes communication between multiple objects.

Instead of objects talking directly, they communicate through a mediator.

This pattern is useful for reducing complex dependencies between components.

18. Memento (Behavioral)

Memento stores the internal state of an object so it can be restored later.

It is commonly used to implement rollback or undo features.

This pattern helps preserve encapsulation while saving history.

19. Observer (Behavioral)

Observer defines a one-to-many relationship so that when one object changes, the others are notified.

It supports event-driven communication between components.

This pattern is useful for user interfaces, notifications, and reactive systems.

20. State (Behavioral)

State allows an object to change its behavior when its internal state changes.

The object appears to change its class depending on the current situation.

This pattern is useful when behavior depends heavily on state transitions.

21. Strategy (Behavioral)

Strategy defines a family of algorithms and makes them interchangeable.

It allows a behavior to be selected at runtime without changing the main class.

This pattern is useful when several ways of performing the same task are needed.

22. Template Method (Behavioral)

Template Method defines the skeleton of an algorithm in a parent class and lets subclasses redefine some steps.

It promotes code reuse while allowing controlled customization.

This pattern is useful when several classes share the same overall process.

23. Visitor (Behavioral)

Visitor lets you define new operations on a group of objects without changing their classes.

It separates the algorithm from the objects on which it operates.

This pattern is useful when many unrelated operations must be applied to the same object structure.

Interview 4 — Java Versions

Key features from Java 8 to Java 21 • explained simply

1. Lambda Expressions (Java 8)

Lambda expressions provide a shorter and more readable way to write anonymous functions.

They are mainly used to simplify code when passing behavior as an argument.

This feature is especially useful with collections and functional interfaces.

2. Functional Interface (Java 8)

A functional interface contains only one abstract method.

It is designed to work with lambda expressions and method references.

This makes code more concise and supports functional programming in Java.

3. Method Reference (Java 8)

A method reference is a shorter way to refer to an existing method without calling it immediately.

It improves readability when a lambda only calls one method.

This feature is often used with streams and functional interfaces.

4. Stream API (Java 8)

The Stream API allows data to be processed in a declarative and efficient way.

It supports operations such as filtering, mapping, sorting, and collecting results.

This feature makes collection processing cleaner and easier to understand.

5. Default Methods in Interfaces (Java 8)

Default methods allow interfaces to contain methods with a body.

They were introduced to add new functionality without breaking existing classes.

This feature improves interface evolution and backward compatibility.

6. Static Methods in Interfaces (Java 8)

Java 8 allows interfaces to contain static methods.

These methods belong to the interface itself and not to implementing classes.

This helps group utility methods in a logical place.

7. Optional (Java 8)

Optional is a container object used to represent a value that may or may not be present.

It helps reduce the risk of NullPointerException.

This feature encourages safer and clearer handling of missing values.

8. forEach Method (Java 8)

The forEach method provides a simple way to iterate through collections.

It is often combined with lambda expressions for cleaner code.

This feature makes traversal more expressive and compact.

9. New Date and Time API (Java 8)

Java 8 introduced a modern date and time API in the java.time package.

It is more powerful, clearer, and less error-prone than the old date classes.

This feature improves the manipulation of dates, times, durations, and time zones.

10. Nashorn JavaScript Engine (Java 8)

Nashorn is a JavaScript engine introduced in Java 8.

It allows JavaScript code to run inside the Java Virtual Machine.

This feature was useful for integrating scripting capabilities into Java applications.

11. Collectors (Java 8)

Collectors are used with streams to gather processed data into a final form.

They can collect results into lists, sets, maps, or perform aggregations.

This feature makes stream results easier to organize and reuse.

12. Parallel Streams (Java 8)

Parallel streams allow stream operations to run on multiple threads automatically.

They can improve performance when processing large amounts of data.

This feature makes parallel data processing easier without managing threads manually.

13. Type Annotations (Java 8)

Java 8 expanded annotations so they can be applied in more places, including types.

This improves code analysis and supports more advanced validation tools.

This feature is useful for frameworks and static analysis.

14. Repeating Annotations (Java 8)

Repeating annotations allow the same annotation to be applied more than once to the same declaration.

This removes the need for wrapper annotations in many cases.

This feature makes annotations more flexible and expressive.

15. Module System (Java 9)

The module system introduces a new way to organize Java applications into named modules.

It improves encapsulation by allowing developers to control which packages are exposed.

This feature makes large applications more maintainable and secure.

16. JShell (Java 9)

JShell is an interactive tool that allows Java code to be executed line by line.

It is useful for testing small code fragments quickly without creating a full program.

This feature helps learning, experimentation, and fast prototyping.

17. Private Methods in Interfaces (Java 9)

Java 9 allows interfaces to include private methods.

These methods are used internally to avoid code duplication between default and static methods.

This feature improves code organization inside interfaces.

18. Factory Methods for Immutable Collections (Java 9)

Java 9 introduced convenient methods such as List.of, Set.of, and Map.of.

These methods make it easier to create immutable collections in a compact way.

This feature improves readability and reduces boilerplate code.

19. Improved Stream API (Java 9)

Java 9 added new stream operations such as takeWhile, dropWhile, and ofNullable.

These additions make stream processing more expressive and flexible.

This feature simplifies certain filtering and iteration tasks.

20. HTTP Client API (Java 11)

Java 11 introduced a modern HTTP client for sending requests and receiving responses.

It supports both synchronous and asynchronous communication.

This feature replaces older, less convenient ways of handling HTTP in Java.

21. var in Lambda Parameters (Java 11)

Java 11 allows the use of var in lambda parameters.

This makes lambda declarations more consistent with local variable syntax.

This feature is useful when annotations or clearer typing are needed in lambdas.

22. String Utility Methods (Java 11)

Java 11 added useful methods such as isBlank, lines, strip, and repeat.

These methods simplify common text-processing tasks.

This feature makes string manipulation cleaner and more expressive.

23. Files Utility Methods (Java 11)

Java 11 introduced new methods to read and write strings directly from files.

This reduces the amount of code needed for simple file operations.

This feature improves developer productivity and readability.

24. Long-Term Support Release (Java 11)

Java 11 is one of the major long-term support versions of Java.

It is widely used in enterprise systems because of its stability.

This makes it a common choice for production environments.

25. Sealed Classes (Java 17)

Sealed classes allow a developer to restrict which classes can extend or implement a type.

This provides better control over inheritance.

This feature is useful when the class hierarchy must remain limited and predictable.

26. Pattern Matching for switch (Java 17)

Java 17 improves the switch statement with pattern matching support.

It allows conditions to be written in a more readable and structured way.

This feature simplifies code that depends on different object types.

27. Records (Java 17)

Records provide a compact way to define classes that mainly store data.

They automatically generate constructors, accessors, equals, hashCode, and toString.

This feature reduces boilerplate code for data-focused classes.

28. Enhanced Random Number Generators (Java 17)

Java 17 introduced improved support for random number generation.

It offers more flexible and modern algorithms for simulation and statistics.

This feature improves control and quality in random-based operations.

29. Long-Term Support Release (Java 17)

Java 17 is another major LTS release.

It combines modern language improvements with production stability.

This makes it very attractive for enterprise adoption.

30. Virtual Threads (Java 21)

Virtual threads provide a lightweight way to handle concurrency in Java.

They make it possible to manage a very large number of tasks without the cost of traditional threads.

This feature simplifies scalable concurrent programming.

31. Record Patterns (Java 21)

Record patterns make it easier to extract values from record objects directly in expressions.

They improve readability when working with structured data.

This feature makes code more concise and more natural to write.

32. Pattern Matching for switch (Java 21)

Java 21 continues improving switch with more complete pattern matching.

It allows more expressive branching based on object shape and type.

This feature makes conditional logic clearer and more powerful.

33. Sequenced Collections (Java 21)

Sequenced collections provide a more consistent way to work with ordered collections.

They make it easier to access the first and last elements and to reverse the order.

This feature improves collection handling in a clean and uniform way.

34. String Templates (Java 21)

String templates introduce a more flexible way to build strings with embedded expressions.

They improve readability and reduce the complexity of string formatting.

This feature helps create dynamic text in a safer and more structured way.

35. Scoped Values (Java 21)

Scoped values provide a safer alternative to thread-local variables in some cases.

They allow data to be shared within a controlled execution scope.

This feature is useful in concurrent and structured applications.

36. Structured Concurrency (Java 21)

Structured concurrency treats a group of related tasks as a single unit of work.

It improves cancellation, error handling, and readability in concurrent programs.

This feature makes parallel programming easier to manage.

Interview 5 — Modern Java (8→21), Spring Boot & AI

Real senior interview questions • Sonar 2025–2026 • With code examples

1. Lambdas & Functional Interfaces (Java 8)

"What is a lambda? What is a functional interface?"

What is a lambda?

A lambda is an anonymous function — a block of code passed as a value.

// Avant Java 8
Comparator<String> old = new Comparator<String>() {
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
};

// Java 8 : lambda
Comparator<String> newWay = (a, b) -> a.compareTo(b);

// Encore plus court : method reference
Comparator<String> shortest = String::compareTo;
The 4 essential functional interfaces
// Predicate<T> — prend T, retourne boolean
Predicate<String> isEmail = s -> s.contains("@");

// Function<T, R> — prend T, retourne R
Function<String, Integer> len = String::length;

// Consumer<T> — prend T, retourne void
Consumer<String> print = System.out::println;

// Supplier<T> — ne prend rien, retourne T
Supplier<List<String>> factory = ArrayList::new;

2. API Streams (Java 8)

"What is the Streams API? map vs flatMap? Lazy evaluation?"

Pipeline Stream — 3 étapes
List<String> result = employees.stream()  // 1. SOURCE
    .filter(e -> e.isActive())             // 2. INTERMEDIATE (lazy)
    .map(Employee::getName)                // 2. INTERMEDIATE (lazy)
    .sorted()                              // 2. INTERMEDIATE (lazy)
    .toList();                             // 3. TERMINAL (triggers all)

Lazy evaluation: nothing executes until the terminal operation.

map() vs flatMap()
List<String> words = List.of("hello world", "java streams");

// map() : 1 input → 1 output (preserves structure)
// → [["hello","world"], ["java","streams"]]

// flatMap() : 1 input → many outputs (flattens)
List<String> flat = words.stream()
    .flatMap(s -> Arrays.stream(s.split(" ")))
    .toList();
// → ["hello", "world", "java", "streams"]
Collectors — most useful
// Group by department
Map<String, List<Employee>> byDept =
    employees.stream()
        .collect(Collectors.groupingBy(Employee::getDept));

// Count by department
Map<String, Long> countByDept =
    employees.stream()
        .collect(Collectors.groupingBy(
            Employee::getDept, Collectors.counting()));

// Join names
String names = employees.stream()
    .map(Employee::getName)
    .collect(Collectors.joining(", ", "[", "]"));
When NOT to use Streams
// ✗ Don't use streams for simple mutation
list.stream().forEach(item -> {
    item.setProcessed(true);
    database.save(item);
});

// ✓ Better: classic for loop for side effects
for (Item item : list) {
    item.setProcessed(true);
    database.save(item);
}

3. Optional — Null Safety (Java 8)

The purpose of Optional
// Bad: returning null causes NullPointerException later
public User findById(Long id) {
    return userMap.get(id); // may be null
}

// Good: Optional forces caller to handle absence
public Optional<User> findById(Long id) {
    return Optional.ofNullable(userMap.get(id));
}
Clean usage patterns
Optional<User> user = service.findById(42L);

// orElse — default value
String name = user.map(User::getName).orElse("Unknown");

// orElseThrow — throw if absent
User u = user.orElseThrow(() -> new NotFoundException(42L));

// ifPresentOrElse — branch on presence
user.ifPresentOrElse(
    u2 -> log.info("Found: {}", u2.getName()),
    ()  -> log.warn("Not found")
);
Anti-patterns to avoid

isPresent() + get() — defeats the purpose

✗ Optional as a field type — bad practice

✗ Optional as a method parameter — bad practice

4. HashMap Internals (Senior)

"How does HashMap work internally? Treeification?"

Internal structure
Array of buckets (default size: 16)
┌─────&boxdt;─────&boxdt;─────┐
│  0  │  1  │ ... │
└──&boxut;──&boxut;─────┘
   │     │
   ↓     ↓
 Entry  Entry → Entry  (linked list on collision)
                       (red-black tree when > 8 entries)
Key parameters
ParameterDefaultEffect
Initial capacity16Bucket array size
Load factor0.75Resize when 75% full
Treeify threshold8Chain → red-black tree
ResizeDoubleRehash all entries — expensive!
equals() + hashCode() contract
// THE CONTRACT: if a.equals(b), then a.hashCode() == b.hashCode()
// Breaking this BREAKS HashMap!

// Always override both, or use Records (auto-generated)

5. ConcurrentHashMap vs HashMap (Senior)

Comparison table
HashMapHashtableConcurrentHashMap
Thread-safe?✗ No✓ Yes (global lock)✓ Yes (segment lock)
Null keys?✓ One null key✗ No null✗ No null
Performance✓ Best✗ Slowest✓ Best thread-safe
Atomic operations
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// WRONG — not thread-safe "check then act"
if (!map.containsKey("key")) {
    map.put("key", computeValue()); // race condition!
}

// CORRECT — atomic
map.computeIfAbsent("key", k -> computeValue());

// Atomic increment
map.merge("counter", 1, Integer::sum);

6. Records & Sealed Classes (Java 16–17)

Records — Immutable data classes
// Before Java 16: ~30 lines of boilerplate
// Java 16+: one line
public record UserDTO(Long id, String name, String email) {}

// With compact constructor for validation
public record OrderSummary(Long orderId, List<Item> items, double total) {
    public OrderSummary {
        Objects.requireNonNull(orderId);
        if (total < 0) throw new IllegalArgumentException("total >= 0");
        items = List.copyOf(items); // defensive copy
    }
}
Sealed Classes — Restrict hierarchy
public sealed class Shape permits Circle, Rectangle, Triangle {}
public final class Circle extends Shape { ... }

// Switch knows all types → exhaustive!
public double area(Shape shape) {
    return switch (shape) {
        case Circle c    -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Triangle t  -> 0.5 * t.base() * t.height();
        // no default needed
    };
}

7. Pattern Matching & Switch (Java 14–21)

Pattern matching for instanceof
// Before Java 16: explicit cast
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

// Java 16+: pattern variable
if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}
Switch expressions (Java 14+)
String result = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
    case SATURDAY, SUNDAY -> "Weekend";
};

// With yield for blocks
int letters = switch (day) {
    case MONDAY -> 6;
    default -> {
        String s = day.name();
        yield s.length();
    }
};

8. Virtual Threads — Project Loom (Java 21)

Platform vs Virtual threads
Platform ThreadVirtual Thread
Managed byOSJVM
Cost~1MB stack~few KB (heap)
Max countThousandsMillions
Blocking I/OBlocks OS threadJVM "parks" it, OS thread free
CPU tasksGoodNo benefit
Creation and usage
// Java 21: virtual thread
Thread.ofVirtual().start(() -> System.out.println("Virtual"));

// Virtual thread executor
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1_000_000; i++) {
        exec.submit(() -> { Thread.sleep(100); return "done"; });
    }
}

// Spring Boot 3.2+:
// spring.threads.virtual.enabled: true
Pinning problem
// ✗ synchronized "pins" the virtual thread
synchronized (lock) {
    Thread.sleep(1000); // PINNED — OS thread blocked!
}

// ✓ Use ReentrantLock instead
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    Thread.sleep(1000); // virtual thread can be parked
} finally {
    lock.unlock();
}

9. Spring Boot — Key Internals

Auto-configuration — how it works
Startup sequence:
  1. @SpringBootApplication
     = @Configuration + @ComponentScan + @EnableAutoConfiguration

  2. @EnableAutoConfiguration reads:
     META-INF/spring/...AutoConfiguration.imports

  3. Spring evaluates @Conditional* annotations:
     @ConditionalOnClass(DataSource.class)
     @ConditionalOnMissingBean(DataSource.class)
     @ConditionalOnProperty("spring.datasource.url")
@Transactional — the 3 traps
@Service
public class OrderService {

    @Transactional
    public Order create(OrderRequest req) {
        Order order = repo.save(new Order(req));
        inventory.reserve(order.getItems());
        return order;
    }

    // TRAP 1: self-invocation bypasses proxy!
    public void processAll(List<OrderRequest> reqs) {
        reqs.forEach(this::create); // @Transactional NOT applied!
    }

    // TRAP 2: rollback only on RuntimeException by default
    @Transactional(rollbackFor = Exception.class)
    public void risky() throws IOException { ... }

    // TRAP 3: @Transactional on private method — ignored!
    @Transactional
    private void internalSave() { }
}

10. Spring AI & LLM Integration

ChatClient — calling an LLM
@Service
public class CodeReviewService {

    private final ChatClient chatClient;

    public CodeReviewService(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultSystem("You are a Java code review expert. Be concise.")
            .build();
    }

    public String review(String javaCode) {
        return chatClient.prompt()
            .user(u -> u.text("Review this Java code:\n```java\n{code}\n```")
                        .param("code", javaCode))
            .call()
            .content();
    }
}
RAG — Retrieval Augmented Generation
@Service
public class RagService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public String answer(String question) {
        List<Document> relevant = vectorStore.similaritySearch(
            SearchRequest.query(question).withTopK(3));

        String context = relevant.stream()
            .map(Document::getContent)
            .collect(Collectors.joining("\n\n"));

        return chatClient.prompt()
            .system("Answer only based on provided context.")
            .user(u -> u.text("Context:\n{ctx}\n\nQuestion: {q}")
                        .param("ctx", context)
                        .param("q", question))
            .call().content();
    }
}

11. Vibe Coding & AI-Assisted Development

What is vibe coding?

Le vibe coding (Andrej Karpathy, 2025) : you describe what you want in natural language and let AI generate the code.

Tools 2025–2026: GitHub Copilot, Cursor, Claude, Amazon Q

Risks — what interviewers expect
Risk 1: SECURITY VULNERABILITIES
  AI code often has: SQL injection, missing validation, hardcoded secrets
  ✓ Mitigation: run SonarQube on all AI-generated code

Risk 2: TECHNICAL DEBT
  AI generates working but poorly structured code at high speed
  ✓ Mitigation: code review, ArchUnit tests, Sonar Quality Gates

Risk 3: API HALLUCINATION
  AI confidently uses methods/libraries that don't exist
  ✓ Mitigation: always compile and test. Never trust blindly.

Risk 4: LOSS OF UNDERSTANDING
  Developer accepts code they don't understand
  ✓ Mitigation: be able to explain every line you commit
What to say — the Sonar angle

"Vibe coding is the trend of using AI to generate most code from natural language descriptions.

My approach: I use AI for scaffolding, test generation, and explaining unfamiliar code. I write business logic and security-critical code myself.

The biggest risk is security: AI-generated code often has SQL injections, missing validation, or hardcoded secrets. That's exactly why Sonar is more relevant than ever — their products are positioned as the 'trust layer' for AI-generated code.

Sonar analyzes all code, regardless of who writes it — your team, AI, or third parties."

Quick Checklist — Java Moderne & Spring

□ Lambda = anonymous function, Functional Interface = 1 abstract method
□ Streams: Lazy (nothing runs until terminal), map vs flatMap
□ HashMap: buckets + chaining → tree at 8, load factor 0.75
□ ConcurrentHashMap: segment-level lock, use computeIfAbsent
□ Records: immutable, auto equals/hashCode/toString
□ Sealed: closed hierarchy, exhaustive switch, no default needed
□ Virtual threads: I/O only, pinning with synchronized
□ @Transactional: proxy-based, 3 traps (self-invoke, private, rollback)
□ Spring AI: ChatClient, RAG, Tool Calling
□ Vibe coding: risks (security, hallucination, debt) + Sonar as trust layer

Interview 6 — Java, Data Structures & Algorithms

20 interview questions • Mid to senior level • With code examples

Q1 — == vs .equals()

See answer

== compares references (memory addresses). .equals() compares the logical content.

String a = new String("hello");
String b = new String("hello");
a == b       // false — different objects
a.equals(b)  // true  — same content

// String pool exception:
String x = "hello";
String y = "hello";
x == y       // true — same pool entry

Rule: If you override .equals(), you MUST override .hashCode().

Q2 — equals() & hashCode() Contract

See answer

If a.equals(b)a.hashCode() == b.hashCode() (mandatory).

If a.hashCode() == b.hashCode()a.equals(b) is NOT guaranteed (collision).

// BROKEN: equals() without hashCode()
class BadKey {
    String id;
    public boolean equals(Object o) { ... } // overridden
    // hashCode() NOT overridden!
}
Map<BadKey, String> map = new HashMap<>();
map.put(new BadKey("abc"), "value");
map.get(new BadKey("abc")); // null! — different hash buckets

Fix: Always override both, or use Records (auto-generated).

Q3 — Abstract class vs Interface

See answer
FeatureAbstract ClassInterface
MethodsConcrete + abstractAbstract; default + static (Java 8+)
FieldsInstance variablesOnly public static final
ConstructorsYesNo
InheritanceSingle (extends)Multiple (implements)
Use caseShare state + behaviorDefine a contract/capability

Abstract class → when classes share state (is-a). Interface → when unrelated classes share a capability (can-do).

Q4 — String vs StringBuilder vs StringBuffer

See answer
ClassMutableThread-safePerformance
StringNo (immutable)YesSlow in loops
StringBuilderYesNoFast — use for single-thread
StringBufferYesYes (synchronized)Slower due to locks
// Bad — O(n²) memory
String result = "";
for (int i = 0; i < 1000; i++) result += i;

// Good — O(n)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append(i);
String result = sb.toString();

Q5 — ArrayList vs LinkedList

See answer
OperationArrayListLinkedList
get(i)O(1)O(n)
Add/remove endO(1) amortizedO(1)
Add/remove middleO(n) — shift neededO(1) if you have the node
MemoryLess (contiguous)More (node + 2 pointers)

Reality: ArrayList is the default choice in 90%+ cases thanks to better cache locality and O(1) random access.

Q6 — HashMap Internals

See answer

Steps when calling map.put(key, value):

  1. Compute key.hashCode()
  2. Apply secondary hash to determine bucket index
  3. If bucket empty → insert directly
  4. If collision → walk the chain, check .equals()
  5. If chain length > 8 and capacity > 64 → convert to Red-Black Tree (O(log n))

Default capacity: 16, load factor: 0.75, resize doubles capacity (expensive rehash).

Q7 — Stack vs Queue

See answer
StructureOrderJava ClassUse case
StackLIFOArrayDequeUndo history, DFS
QueueFIFOLinkedList / ArrayDequeTask queues, BFS
// Stack (prefer ArrayDeque over legacy Stack)
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); stack.push(2);
stack.pop(); // 2 — LIFO

// Queue
Queue<Integer> queue = new LinkedList<>();
queue.offer(1); queue.offer(2);
queue.poll(); // 1 — FIFO

// PriorityQueue (min-heap)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(5); pq.offer(1); pq.offer(3);
pq.poll(); // 1 — smallest first

Q8 — Binary Tree vs BST vs Balanced BST

See answer
TypePropertySearchInsert
Binary treeEach node ≤ 2 childrenO(n)O(n)
BSTLeft < root < rightO(h)O(h)
Balanced BSTHeight = O(log n)O(log n)O(log n)

Java's TreeMap uses Red-Black Tree → O(log n) guaranteed, keys always sorted.

Q9 — Graphs — BFS vs DFS

See answer
// Adjacency list — O(V + E) space
Map<Integer, List<Integer>> graph = new HashMap<>();

// BFS — uses Queue — level by level
void bfs(int start) {
    Set<Integer> visited = new HashSet<>();
    Queue<Integer> queue = new LinkedList<>();
    queue.offer(start); visited.add(start);
    while (!queue.isEmpty()) {
        int node = queue.poll();
        for (int n : graph.getOrDefault(node, List.of()))
            if (visited.add(n)) queue.offer(n);
    }
}

// DFS — uses Stack/Recursion — depth first
void dfs(int node, Set<Integer> visited) {
    visited.add(node);
    for (int n : graph.getOrDefault(node, List.of()))
        if (!visited.contains(n)) dfs(n, visited);
}
AlgorithmUse caseComplexity
BFSShortest path (unweighted)O(V + E)
DFSTopological sort, cycle detectionO(V + E)

Q10 — Big O Notation

See answer
ComplexityNameExample
O(1)ConstantArray index, HashMap get
O(log n)LogarithmicBinary search, balanced BST
O(n)LinearLinear search, traversal
O(n log n)LinearithmicMerge sort, heap sort
O(n²)QuadraticBubble sort, nested loops
O(2¹)ExponentialBrute-force subsets

Q11 — Recursion vs Iteration

See answer
AspectRecursionIteration
ReadabilityClearer for tree/graphMore verbose for nested
PerformanceStack frame overheadGenerally faster
RiskStackOverflowErrorExplicit stack needed
Tail recursionJava does NOT optimizeN/A
// Fibonacci — naive recursion: O(2^n)
int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}

// Fibonacci — iterative DP: O(n) time, O(1) space
int fibIter(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1;
    for (int i = 2; i <= n; i++) { int c = a+b; a = b; b = c; }
    return b;
}

Q12 — Singleton, Factory, Builder

Singleton — one instance
public class DbConnection {
    private static volatile DbConnection instance;
    private DbConnection() {}
    public static DbConnection getInstance() {
        if (instance == null) {
            synchronized (DbConnection.class) {
                if (instance == null) instance = new DbConnection();
            }
        }
        return instance;
    }
}
Factory Method — delegate creation
interface Notification { void send(String msg); }
class EmailNotif implements Notification { ... }
class SmsNotif implements Notification { ... }

class NotifFactory {
    public static Notification create(String type) {
        return switch (type) {
            case "EMAIL" -> new EmailNotif();
            case "SMS" -> new SmsNotif();
            default -> throw new IllegalArgumentException(type);
        };
    }
}
Builder — step by step
public class HttpRequest {
    private final String url, method, body;
    private HttpRequest(Builder b) { this.url=b.url; this.method=b.method; this.body=b.body; }

    public static class Builder {
        private String url, method="GET", body;
        public Builder url(String u) { this.url=u; return this; }
        public Builder method(String m) { this.method=m; return this; }
        public Builder body(String b) { this.body=b; return this; }
        public HttpRequest build() { return new HttpRequest(this); }
    }
}
// Usage
HttpRequest req = new HttpRequest.Builder()
    .url("https://api.example.com").method("POST").body("{}").build();

Q13 — Adapter, Decorator, Proxy

Adapter — bridge incompatible interfaces
interface ModernPayment { void pay(double amount); }
class LegacyPayment { void makePayment(int cents) { ... } }

class PaymentAdapter implements ModernPayment {
    private final LegacyPayment legacy;
    PaymentAdapter(LegacyPayment l) { this.legacy = l; }
    public void pay(double amount) { legacy.makePayment((int)(amount*100)); }
}
Decorator — add behavior dynamically
interface Coffee { double cost(); String desc(); }
class SimpleCoffee implements Coffee {
    public double cost() { return 1.0; }
    public String desc() { return "Coffee"; }
}
class MilkDecorator implements Coffee {
    private final Coffee c;
    MilkDecorator(Coffee c) { this.c = c; }
    public double cost() { return c.cost() + 0.25; }
    public String desc() { return c.desc() + ", Milk"; }
}
// Coffee, Milk, Sugar = 1.35
Coffee order = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));

This is how Java I/O works: BufferedReader wraps FileReader.

Proxy — control access (lazy loading, cache, security)
interface Image { void display(); }
class RealImage implements Image {
    RealImage(String path) { System.out.println("Loading: " + path); } // expensive
    public void display() { ... }
}
class ProxyImage implements Image {
    private final String path;
    private RealImage real;
    ProxyImage(String p) { this.path = p; }
    public void display() {
        if (real == null) real = new RealImage(path); // lazy load
        real.display();
    }
}

This is how Spring AOP and JPA lazy loading work.

Q14 — Strategy, Observer, Command

Strategy — interchangeable algorithms
@FunctionalInterface
interface SortStrategy { void sort(int[] arr); }

class Sorter {
    private SortStrategy strategy;
    Sorter(SortStrategy s) { this.strategy = s; }
    void sort(int[] arr) { strategy.sort(arr); }
}
// With lambda
Sorter sorter = new Sorter(arr -> Arrays.sort(arr));
Observer — notify dependents on state change
interface Observer { void update(String event); }

class EventBus {
    private final Map<String, List<Observer>> subs = new HashMap<>();
    void subscribe(String evt, Observer o) {
        subs.computeIfAbsent(evt, k -> new ArrayList<>()).add(o);
    }
    void publish(String evt) {
        subs.getOrDefault(evt, List.of()).forEach(o -> o.update(evt));
    }
}
bus.subscribe("ORDER_CREATED", e -> sendEmail(e));
bus.subscribe("ORDER_CREATED", e -> updateInventory(e));
bus.publish("ORDER_CREATED");
Command — encapsulate requests (undo/redo)
interface Command { void execute(); void undo(); }

class AppendCommand implements Command {
    private final TextEditor editor;
    private final String text;
    AppendCommand(TextEditor e, String t) { this.editor=e; this.text=t; }
    public void execute() { editor.append(text); }
    public void undo() { editor.delete(text.length()); }
}

Deque<Command> history = new ArrayDeque<>();
Command cmd = new AppendCommand(editor, "Hello");
cmd.execute(); history.push(cmd);
history.pop().undo(); // undo last

Q15 — When NOT to use a pattern?

See answer

YAGNI: "You Aren't Gonna Need It" — don't add abstraction until the problem demands it.

Rule of 3: Consider extracting a pattern after seeing the same structure 3 times.

// Over-engineered — don't do this
class UserValidatorStrategyFactoryProxyDecorator { ... }

// Simple and clear — prefer this
boolean isValid(String email) {
    return email != null && email.contains("@");
}

"I treat patterns as vocabulary, not a checklist. The goal is readable, maintainable code."

Q16 — Generics & Type Erasure

See answer
List<String> strings = new ArrayList<>();
List<Integer> ints = new ArrayList<>();
strings.getClass() == ints.getClass() // true! — same class at runtime

Consequences: Can't do new T(), instanceof T, or new T[] at runtime.

PECS: Producer Extends, Consumer Super

// Read from — use ? extends
void print(List<? extends Number> list) { ... }

// Write to — use ? super
void addNumbers(List<? super Integer> list) { list.add(42); }

Q17 — synchronized vs volatile vs ReentrantLock

See answer
MechanismGuaranteesUse case
synchronizedMutual exclusion + visibilityProtect block/method
volatileVisibility only (not atomicity)Simple flag, stop signal
ReentrantLockMutual exclusion + try-lock + fairnessAdvanced locking
// volatile — safe for single-write, multi-read
private volatile boolean running = true;

// synchronized — mutual exclusion
public synchronized void increment() { count++; }

// ReentrantLock — more control
lock.lock();
try { count++; }
finally { lock.unlock(); }

For counters, prefer AtomicInteger.

Q18 — Streams: map vs flatMap vs filter vs reduce

See answer
List<String> names = List.of("Alice", "Bob", "Charlie", "Anna");

// filter — keep matching
names.stream().filter(n -> n.startsWith("A")).toList(); // [Alice, Anna]

// map — transform 1-to-1
names.stream().map(String::length).toList(); // [5, 3, 7, 4]

// flatMap — flatten nested collections
List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4));
nested.stream().flatMap(Collection::stream).toList(); // [1,2,3,4]

// reduce — aggregate to single value
List.of(1,2,3,4,5).stream().reduce(0, Integer::sum); // 15

Lazy evaluation: intermediate ops only execute when a terminal op is called.

Q19 — Optional — correct usage & anti-patterns

See answer
// Good — Optional as return type
Optional<User> findUser(String id) { ... }

// Consume correctly
findUser("123")
    .map(User::getEmail)
    .filter(e -> e.contains("@"))
    .ifPresent(e -> sendWelcome(e));

String email = findUser("123")
    .map(User::getEmail)
    .orElse("noreply@example.com");

Anti-patterns:

isPresent() + get() — defeats the purpose

✗ Optional as field — bad

✗ Optional as method parameter — bad

Optional.of(null) — throws NPE, use ofNullable

Q20 — Heap vs Stack & Garbage Collection

See answer
MemoryContainsLifetimeThread
StackLocal vars, method framesMethod call durationPer thread
HeapObjects, arraysGC-managedShared
MetaspaceClass definitionsUntil class unloadShared
void example() {
    int x = 10;                  // stack
    String s = "hello";          // ref on stack, object on heap
    User user = new User("Bob"); // ref on stack, User on heap
}
// When method returns: x and refs popped from stack
// User object eligible for GC when no more references

GC: Generational — Young Gen (Eden + Survivor) for short-lived, Old Gen for long-lived. Most objects die young.

Memory leaks: Static collections, unregistered listeners, caches without eviction.

Interview 7 — Java Quick Q&A

Short and clear answers for common interview questions • Ready to say out loud

1. StringBuilder vs StringBuffer

"What is the difference between StringBuilder and StringBuffer?"

See answer

Both are used to manipulate strings and both are mutable (you can modify the content without creating a new object).

StringBuilderStringBuffer
Thread-safeNoYes (synchronized)
PerformanceFasterSlower (due to locking)
IntroducedJava 5 (2004)Java 1.0 (1996)
Use whenSingle thread (most cases)Multiple threads modifying same string

Key point: StringBuffer came first. It was always synchronized. But most of the time, synchronization is unnecessary overhead. That's why StringBuilder was introduced in Java 5 as a faster alternative.

Say this: "I use StringBuilder by default. I only switch to StringBuffer if multiple threads are modifying the same string, which is rare."

2. Load Balancing in Java

"How is load balancing used in Java?"

See answer

Java does not have built-in load balancing. It's an architectural strategy implemented with external tools.

  • Hardware load balancers: F5, Citrix
  • Software load balancers: HAProxy, Nginx, AWS ALB/NLB
  • Spring Cloud: client-side load balancing with Spring Cloud LoadBalancer
  • Kubernetes: built-in service load balancing

Say this: "Load balancing is not a Java feature — it's an infrastructure concern. I use tools like AWS ALB or Kubernetes services to distribute traffic across multiple instances of my Spring Boot application."

3. Resilience in Java

"What is resilience in Java? How have you implemented it?"

See answer

Resilience is the ability of an application to keep working reliably even when things go wrong.

Three key techniques:

  • Retry mechanisms with exponential backoff to handle temporary failures
  • Circuit breakers to prevent cascading failures
  • Fallback logic to keep the system responsive when a component fails

Common libraries: Resilience4j (modern), Hystrix (deprecated)

Say this: "In my projects, I use Resilience4j for retries with backoff, circuit breakers, and fallback logic. This keeps the system stable even when external services are down."

4. Exponential Backoff

"What is exponential backoff?"

See answer

Exponential backoff is a strategy where each retry is delayed progressively longer after a failure.

Instead of retrying immediately, you wait increasingly longer intervals:

Attempt 1 → wait 1 second
Attempt 2 → wait 2 seconds
Attempt 3 → wait 4 seconds
Attempt 4 → wait 8 seconds

This gives the system time to recover and avoids overloading a failing service.

Say this: "Instead of hammering a failing service, I increase the wait time between retries. This avoids overload and gives the service time to recover."

5. Circuit Breaker

"What is a circuit breaker?"

See answer

A circuit breaker is like an electrical breaker but for your application.

It monitors calls to a service. After a certain number of failures, it "trips" and stops calling the failing service.

CLOSED  → calls go through normally
         (failures counted)

OPEN    → calls are blocked immediately
         (returns fallback, no call to service)
         (waits cooldown period)

HALF-OPEN → lets a few calls through to test
           (if success → back to CLOSED)
           (if failure → back to OPEN)

Say this: "A circuit breaker stops calling a broken service, waits for a cooldown, then tests again. It prevents one failure from taking down the whole system."

6. Cascading Failure

"What is a cascading failure?"

See answer

A cascading failure is when one component's failure triggers failures in other components, spreading across the system like dominoes.

Service A fails
  → Service B keeps calling A, gets timeouts
    → Service B's thread pool fills up
      → Service B stops responding
        → Service C depends on B, also fails
          → Entire system is down

Prevention: Circuit breakers cut the chain before it spreads.

Say this: "A cascading failure is a domino effect where one service going down takes others with it. Circuit breakers prevent this by cutting the chain early."

7. Fallback Logic

"What is fallback logic?"

See answer

Fallback logic means having a backup plan.

If one component fails, the system provides an alternative so the user still gets something:

  • Return a cached response instead of live data
  • Return a simplified response (degraded mode)
  • Redirect to another service
  • Show a friendly error message instead of crashing

Say this: "If a service is down, I don't let the user see an error. I return cached data or a simplified response. The user may not get the full experience, but the system stays responsive."

Interview 8 — Rapid Fire Questions

14 common interview questions • Short, clear answers • Ready to say out loud

1. final / finally / finalize

"What is the difference between final, finally, and finalize?"

See answer
KeywordWhat it doesExample
finalMakes a variable constant, a method non-overridable, or a class non-extendablefinal int MAX = 100;
finallyA block that always executes after try/catch, even if an exception occursClose database connections, release resources
finalizeA method called by GC before destroying an object (deprecated since Java 9)Don't use it — use try-with-resources instead

Say this: "final prevents change, finally ensures cleanup, and finalize is deprecated — I never use it."

2. Checked vs Unchecked Exceptions

"What is the difference between checked and unchecked exceptions?"

See answer
CheckedUnchecked
WhenCompile timeRuntime
Must handle?Yes (try/catch or throws)No (but you should)
ExtendsExceptionRuntimeException
ExamplesIOException, SQLExceptionNullPointerException, ArrayIndexOutOfBounds

Say this: "Checked exceptions are for recoverable situations like file not found. Unchecked are for programming errors like null pointer. I prefer unchecked in business logic and checked for I/O operations."

3. synchronized vs volatile

"When do you use synchronized vs volatile?"

See answer
synchronizedvolatile
GuaranteesMutual exclusion + visibilityVisibility only
AtomicityYes (one thread at a time)No (just makes value visible to all threads)
Use forComplex operations (increment, check-then-act)Simple flags (boolean stop signal)
PerformanceSlower (locking)Faster (no locking)

Say this: "volatile is for simple flags — like a stop signal. synchronized is for anything more complex where I need mutual exclusion. For counters, I use AtomicInteger."

4. Heap vs Stack

"What is the difference between heap and stack memory?"

See answer
StackHeap
StoresLocal variables, method callsObjects, arrays
LifetimeMethod durationUntil GC collects
ThreadPer thread (private)Shared across threads
SpeedVery fastSlower (GC overhead)
ErrorStackOverflowErrorOutOfMemoryError

Say this: "Stack is fast and per-thread — it holds local variables and method frames. Heap is shared — it holds objects, managed by the garbage collector."

5. How Spring Dependency Injection Works

"How does Spring DI work?"

See answer

Spring creates and manages objects called beans. Instead of creating dependencies yourself with new, Spring injects them automatically.

// Without DI (tight coupling)
OrderService service = new OrderService(new OrderRepository());

// With Spring DI (loose coupling)
@Service
public class OrderService {
    private final OrderRepository repo;

    public OrderService(OrderRepository repo) { // Spring injects this
        this.repo = repo;
    }
}

3 injection types: Constructor (preferred), Setter, Field (@Autowired — avoid)

Say this: "Spring manages the lifecycle of beans and injects dependencies automatically. I always use constructor injection — it's immutable, testable, and makes dependencies explicit."

6. @Component vs @Service vs @Repository

"What is the difference?"

See answer
AnnotationLayerExtra behavior
@ComponentGeneric beanNone — base annotation
@ServiceBusiness logicNone (semantic only)
@RepositoryData access (DAO)Translates database exceptions to Spring exceptions
@ControllerWeb layerHandles HTTP requests

They all register a bean. The difference is semantic clarity + @Repository adds exception translation.

Say this: "They all create Spring beans. I use @Service for business logic, @Repository for data access, and @Controller for web endpoints. It keeps the architecture clean and readable."

7. LAZY vs EAGER Loading

"What is the difference between lazy and eager loading?"

See answer
EAGERLAZY
When loadedImmediately with parentOnly when accessed
SQL queriesOne big JOINExtra query when needed
RiskLoading too much dataN+1 problem if not careful
JPA default@ManyToOne, @OneToOne@OneToMany, @ManyToMany

Say this: "I prefer LAZY loading by default to avoid loading unnecessary data. When I need related data, I use JOIN FETCH queries to load it in one shot and avoid the N+1 problem."

8. The N+1 Problem

"What is the N+1 problem?"

See answer
// You load 10 orders (1 query)
SELECT * FROM orders;

// For EACH order, JPA loads the customer (N queries)
SELECT * FROM customers WHERE id = 1;
SELECT * FROM customers WHERE id = 2;
SELECT * FROM customers WHERE id = 3;
... (10 queries)

// Total: 1 + 10 = 11 queries instead of 1!

Fix:

// Use JOIN FETCH — one query loads everything
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomers();

Say this: "The N+1 problem happens when JPA fires one query per related entity. I fix it with JOIN FETCH or @EntityGraph to load everything in a single query."

9. What Does @Transactional Do?

"What does @Transactional do in Spring?"

See answer

@Transactional wraps a method in a database transaction. If anything fails, everything is rolled back.

@Transactional
public void transferMoney(Long from, Long to, double amount) {
    accountRepo.debit(from, amount);   // if this works...
    accountRepo.credit(to, amount);    // ...but this fails
    // BOTH are rolled back — money is safe
}

3 traps to know:

  • Self-invocation: calling a @Transactional method from the same class bypasses the proxy
  • Private methods: @Transactional on private methods is ignored
  • Rollback: only rolls back on RuntimeException by default (use rollbackFor = Exception.class)

Say this: "@Transactional ensures all-or-nothing. If any step fails, the whole transaction rolls back. I always watch out for the self-invocation trap."

10. What is JWT?

"What is JWT and how does it work?"

See answer

JWT (JSON Web Token) is a compact, self-contained token used for authentication.

Structure:  HEADER.PAYLOAD.SIGNATURE

Header:    {"alg": "HS256", "typ": "JWT"}
Payload:   {"sub": "user123", "role": "ADMIN", "exp": 1700000000}
Signature: HMACSHA256(header + payload, secret)

Flow:

  1. User logs in with username/password
  2. Server creates a JWT and sends it back
  3. Client sends JWT in every request header: Authorization: Bearer <token>
  4. Server validates the signature — no need to check database

Say this: "JWT is a stateless token. The server signs it, the client stores it, and sends it with every request. No session needed on the server side."

11. What is a RESTful API?

"What makes an API RESTful?"

See answer

REST is an architectural style for web APIs based on HTTP.

MethodActionExample
GETReadGET /users/42
POSTCreatePOST /users
PUTReplacePUT /users/42
PATCHPartial updatePATCH /users/42
DELETERemoveDELETE /users/42

Key principles: Stateless, resource-based URLs, proper HTTP status codes (200, 201, 400, 404, 500), JSON format.

Say this: "A RESTful API uses HTTP methods to perform CRUD operations on resources. It's stateless, uses proper status codes, and returns JSON."

12. How to Dockerize a Java Application

"How do you dockerize a Spring Boot application?"

See answer
# Dockerfile
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/myapp.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# Build and run
docker build -t myapp .
docker run -p 8080:8080 myapp

Best practices:

  • Use JRE not JDK (smaller image)
  • Use Alpine base image (minimal size)
  • Multi-stage build for even smaller images
  • Don't run as root

Say this: "I use a multi-stage Dockerfile with an Alpine JRE image to keep it small. I build the jar in one stage and copy it to a minimal runtime image."

13. How to Deploy on Kubernetes

"How do you deploy a Spring Boot app on Kubernetes?"

See answer
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    spec:
      containers:
      - name: myapp
        image: myapp:latest
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080

Steps:

  1. Build Docker image
  2. Push to container registry (ECR, Docker Hub)
  3. Apply Deployment + Service YAML
  4. Kubernetes creates pods, load balances traffic

Say this: "I containerize the app with Docker, push to a registry, and deploy with a Kubernetes Deployment for scaling and a Service for load balancing. I use health probes with Spring Actuator."

14. How to Diagnose a Performance Problem

"How do you diagnose a performance issue in a Java application?"

See answer

Step-by-step approach:

  1. Reproduce — identify when and where it's slow
  2. Monitor — check CPU, memory, threads (JVisualVM, Grafana, Spring Actuator)
  3. Profile — find the bottleneck (JProfiler, async-profiler, flame graphs)
  4. Check queries — slow SQL? N+1? Missing indexes? (EXPLAIN ANALYZE)
  5. Check GC — too many full GCs? Memory leaks? (GC logs, heap dumps)
  6. Check threads — deadlocks? Thread pool exhaustion? (thread dumps)
  7. Fix and measure — apply fix, measure before/after

Common causes:

  • N+1 queries (fix: JOIN FETCH)
  • Missing database indexes
  • Memory leaks (static collections, unclosed resources)
  • Thread pool exhaustion (blocking I/O)
  • Large payloads (pagination missing)

Say this: "I start by reproducing the issue, then I check metrics and profiles to find the bottleneck. Most of the time it's a database problem — N+1 queries or missing indexes. I always measure before and after the fix."

Interview 9 — Live Coding Exercises

12 practice exercises • Refactoring, Data Structures, Design, Algorithms • Sonar-style

How to Approach a Live Coding Exercise

The interviewer is watching how you think, not just if you get the answer.

The 5-Step Framework
  1. Clarify — "Can I ask a few questions about the requirements?"
  2. Think out loud — "My first thought is... because..."
  3. Start simple — write a working solution first, even if not perfect
  4. Refactor — "Now let me improve this by..."
  5. Test — "Let me walk through a few test cases..."

What Sonar specifically evaluates:

  • Clean code — naming, readability, structure
  • Refactoring instinct — do you naturally improve code?
  • Communication — explaining WHY, not just WHAT
  • Testing mindset — do you think about edge cases?

Never code in silence. Always talk while coding.

Exercise 1 — Refactor Nested Conditionals

"Refactor this method to improve readability."

See the bad code
public void processOrder(Order order) {
    if (order != null) {
        if (order.getStatus().equals("ACTIVE")) {
            if (order.getItems() != null && !order.getItems().isEmpty()) {
                if (order.getCustomer() != null) {
                    double total = 0;
                    for (Item item : order.getItems()) {
                        total += item.getPrice() * item.getQuantity();
                    }
                    order.setTotal(total);
                    emailService.sendConfirmation(order.getCustomer().getEmail(), total);
                }
            }
        }
    }
}
See the refactored code
public void processOrder(Order order) {
    if (order == null) return;
    if (!"ACTIVE".equals(order.getStatus())) return;
    if (order.getItems() == null || order.getItems().isEmpty()) return;
    if (order.getCustomer() == null) return;

    double total = calculateTotal(order.getItems());
    order.setTotal(total);
    emailService.sendConfirmation(order.getCustomer().getEmail(), total);
}

private double calculateTotal(List<Item> items) {
    return items.stream()
        .mapToDouble(i -> i.getPrice() * i.getQuantity())
        .sum();
}

What you did: Guard clauses, extract method, stream API, "ACTIVE".equals() to avoid NPE.

Exercise 2 — Replace If/Else with Strategy Pattern

"This code needs to support new payment types easily. Refactor it."

See the bad code
public double calculateFee(String paymentType, double amount) {
    if (paymentType.equals("CREDIT_CARD")) {
        return amount * 0.03;
    } else if (paymentType.equals("PAYPAL")) {
        return amount * 0.025;
    } else if (paymentType.equals("BANK_TRANSFER")) {
        return amount * 0.01;
    } else if (paymentType.equals("CRYPTO")) {
        return amount * 0.005;
    } else {
        throw new IllegalArgumentException("Unknown: " + paymentType);
    }
}
See the refactored code
interface FeeCalculator {
    double calculate(double amount);
}

class CreditCardFee implements FeeCalculator {
    public double calculate(double amount) { return amount * 0.03; }
}
class PaypalFee implements FeeCalculator {
    public double calculate(double amount) { return amount * 0.025; }
}

// Registry
Map<String, FeeCalculator> calculators = Map.of(
    "CREDIT_CARD", new CreditCardFee(),
    "PAYPAL", new PaypalFee(),
    "BANK_TRANSFER", amount -> amount * 0.01,
    "CRYPTO", amount -> amount * 0.005
);

public double calculateFee(String type, double amount) {
    FeeCalculator calc = calculators.get(type);
    if (calc == null) throw new IllegalArgumentException("Unknown: " + type);
    return calc.calculate(amount);
}

What you did: Strategy pattern, Open/Closed Principle, lambdas for simple cases. Adding a new type = one line.

Exercise 3 — Split a God Class (SRP)

"This class does too many things. Refactor it."

See the bad code
public class UserService {
    public User createUser(String name, String email) {
        if (name == null || email == null) throw new IllegalArgumentException();
        if (!email.contains("@")) throw new IllegalArgumentException("Bad email");
        User user = new User(name, email);
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        jdbcTemplate.update(sql, name, email);
        String subject = "Welcome!";
        String body = "Hello " + name;
        emailSender.send(email, subject, body);
        log.info("User created: " + email);
        return user;
    }
}
See the refactored code
// Validation
class UserValidator {
    void validate(String name, String email) {
        Objects.requireNonNull(name, "name required");
        Objects.requireNonNull(email, "email required");
        if (!email.contains("@")) throw new IllegalArgumentException("Bad email");
    }
}

// Persistence
@Repository
class UserRepository {
    void save(User user) {
        jdbcTemplate.update("INSERT INTO users (name, email) VALUES (?, ?)",
            user.getName(), user.getEmail());
    }
}

// Notification
@Service
class WelcomeEmailService {
    void sendWelcome(User user) {
        emailSender.send(user.getEmail(), "Welcome!", "Hello " + user.getName());
    }
}

// Orchestration
@Service
class UserService {
    private final UserValidator validator;
    private final UserRepository repo;
    private final WelcomeEmailService welcomeEmail;

    @Transactional
    public User createUser(String name, String email) {
        validator.validate(name, email);
        User user = new User(name, email);
        repo.save(user);
        welcomeEmail.sendWelcome(user);
        return user;
    }
}

What you did: Single Responsibility Principle. Each class has one job. Easy to test, easy to change.

Exercise 4 — Word Frequency Counter (HashMap)

"Count the frequency of each word in a string."

See solution
public Map<String, Integer> wordCount(String text) {
    Map<String, Integer> freq = new HashMap<>();
    for (String word : text.toLowerCase().split("\\s+")) {
        freq.merge(word, 1, Integer::sum);
    }
    return freq;
}

// Or with Streams
public Map<String, Long> wordCountStream(String text) {
    return Arrays.stream(text.toLowerCase().split("\\s+"))
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}

// Test: "hello world hello java" → {hello=2, world=1, java=1}

Say: "I used merge() for the imperative approach and groupingBy for the stream approach. Both are O(n)."

Exercise 5 — Valid Brackets (Stack)

"Check if a string of brackets is valid: (), [], {}"

See solution
public boolean isValid(String s) {
    Deque<Character> stack = new ArrayDeque<>();
    Map<Character, Character> pairs = Map.of(')', '(', ']', '[', '}', '{');

    for (char c : s.toCharArray()) {
        if (pairs.containsValue(c)) {
            stack.push(c);
        } else if (pairs.containsKey(c)) {
            if (stack.isEmpty() || stack.pop() != pairs.get(c)) return false;
        }
    }
    return stack.isEmpty();
}

// Test: "({[]})" → true
// Test: "({[}])" → false
// Test: "((("    → false

Say: "Classic stack problem. O(n) time, O(n) space. I used ArrayDeque instead of the legacy Stack class."

Exercise 6 — Reverse a Linked List

"Reverse a singly linked list."

See solution
class ListNode {
    int val;
    ListNode next;
    ListNode(int val) { this.val = val; }
}

public ListNode reverse(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode next = curr.next;  // save next
        curr.next = prev;           // reverse pointer
        prev = curr;                // move prev forward
        curr = next;                // move curr forward
    }
    return prev;
}

// 1 → 2 → 3 → null  becomes  3 → 2 → 1 → null

Say: "Three pointers: prev, curr, next. O(n) time, O(1) space. This is a classic that tests pointer manipulation."

Exercise 7 — Design a REST Endpoint

"Design and implement an endpoint to create and retrieve users."

See solution
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    @PostMapping
    public ResponseEntity<UserDTO> create(@Valid @RequestBody CreateUserRequest req) {
        UserDTO user = userService.create(req);
        return ResponseEntity.status(HttpStatus.CREATED).body(user);
    }

    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getById(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @GetMapping
    public ResponseEntity<Page<UserDTO>> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        return ResponseEntity.ok(userService.findAll(PageRequest.of(page, size)));
    }
}

// DTO (or Record)
public record CreateUserRequest(
    @NotBlank String name,
    @Email String email
) {}

public record UserDTO(Long id, String name, String email) {}

Say: "I use DTOs to avoid exposing entities, @Valid for input validation, proper status codes (201 Created, 404 Not Found), and pagination for list endpoints."

Exercise 8 — Implement Builder Pattern

"Create an immutable HttpRequest class using the Builder pattern."

See solution
public final class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;
    private final String body;

    private HttpRequest(Builder builder) {
        this.url = Objects.requireNonNull(builder.url, "URL required");
        this.method = builder.method;
        this.headers = Map.copyOf(builder.headers);
        this.body = builder.body;
    }

    public String getUrl() { return url; }
    public String getMethod() { return method; }
    public Map<String, String> getHeaders() { return headers; }
    public String getBody() { return body; }

    public static class Builder {
        private String url;
        private String method = "GET";
        private final Map<String, String> headers = new HashMap<>();
        private String body;

        public Builder url(String url) { this.url = url; return this; }
        public Builder method(String m) { this.method = m; return this; }
        public Builder header(String k, String v) { headers.put(k, v); return this; }
        public Builder body(String b) { this.body = b; return this; }
        public HttpRequest build() { return new HttpRequest(this); }
    }
}

// Usage
HttpRequest req = new HttpRequest.Builder()
    .url("https://api.sonar.com/issues")
    .method("POST")
    .header("Authorization", "Bearer token123")
    .header("Content-Type", "application/json")
    .body("{\"severity\": \"CRITICAL\"}")
    .build();

Say: "Immutable object, defensive copies for collections, required fields validated in constructor. Fluent API makes the call site self-documenting."

Exercise 9 — Binary Search

"Implement binary search on a sorted array."

See solution
public int binarySearch(int[] arr, int target) {
    int lo = 0, hi = arr.length - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;  // avoid overflow
        if (arr[mid] == target) return mid;
        else if (arr[mid] < target) lo = mid + 1;
        else hi = mid - 1;
    }
    return -1; // not found
}

// Test: [1, 3, 5, 7, 9, 11], target=7 → returns 3
// Time: O(log n), Space: O(1)

Say: "I use lo + (hi - lo) / 2 instead of (lo + hi) / 2 to avoid integer overflow. Classic O(log n) search on sorted data."

Exercise 10 — Two Sum

"Find two numbers in an array that add up to a target."

See solution
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> seen = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (seen.containsKey(complement)) {
            return new int[]{seen.get(complement), i};
        }
        seen.put(nums[i], i);
    }
    return new int[]{};
}

// Test: [2, 7, 11, 15], target=9 → [0, 1] (2+7=9)
// Time: O(n), Space: O(n)

Say: "I use a HashMap to store seen values. For each number, I check if the complement exists. One pass, O(n) time."

Exercise 11 — Fix Buggy Code (Sonar Codility Style)

"This code has 4 bugs. Find and fix them."

See the buggy code
public class DiscountCalculator {

    public double calculateDiscount(List<Product> products, String coupon) {
        double total = 0;
        for (int i = 0; i <= products.size(); i++) {    // BUG 1
            Product p = products.get(i);
            if (p.getPrice() > 0) {
                total =+ p.getPrice();                    // BUG 2
            }
        }
        double discount = 0;
        if (coupon == "SAVE20") {                         // BUG 3
            discount = total * 0.20;
        }
        return total - discount;                          // BUG 4: no null check on coupon
    }
}
See the fixed code
public class DiscountCalculator {

    public double calculateDiscount(List<Product> products, String coupon) {
        if (products == null || products.isEmpty()) return 0;  // FIX 4: null safety

        double total = 0;
        for (int i = 0; i < products.size(); i++) {           // FIX 1: < not <=
            Product p = products.get(i);
            if (p.getPrice() > 0) {
                total += p.getPrice();                         // FIX 2: += not =+
            }
        }
        double discount = 0;
        if ("SAVE20".equals(coupon)) {                         // FIX 3: .equals() not ==
            discount = total * 0.20;
        }
        return total - discount;
    }
}
BugIssueFix
1<= causes ArrayIndexOutOfBoundsChange to <
2=+ assigns +value instead of addingChange to +=
3== compares references, not contentUse .equals()
4No null check on products or couponAdd null/empty guard

Exercise 12 — Code Review Exercise

"Review this code. What would you improve?"

See the code to review
public class DataProcessor {
    public static ArrayList<String> process(ArrayList<String> data) {
        ArrayList<String> result = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) {
            String s = data.get(i);
            if (s != null) {
                if (s.length() > 0) {
                    if (!s.equals("SKIP")) {
                        result.add(s.toUpperCase());
                    }
                }
            }
        }
        return result;
    }
}
See the improved code + explanation
public class DataProcessor {

    public List<String> process(List<String> data) {
        if (data == null) return List.of();

        return data.stream()
            .filter(Objects::nonNull)
            .filter(s -> !s.isEmpty())
            .filter(s -> !"SKIP".equals(s))
            .map(String::toUpperCase)
            .toList();
    }
}

Issues found:

  • Use interfaces: List not ArrayList (program to interface)
  • Remove static: makes testing/mocking harder
  • Guard clauses: handle null input at the top
  • Nested ifs: replace with stream pipeline
  • String comparison: use "SKIP".equals(s) to avoid NPE
  • Use isEmpty(): cleaner than length() > 0
  • Immutable return: toList() returns unmodifiable list

Say: "I'd refactor to use streams for readability, program to interfaces, and add null safety. This is exactly the kind of code Sonar would flag — nested conditionals and mutable state."