Java scenario based questions and its answers - Series 2

 

Question 11: The "E-Commerce Discount" Scenario (Polymorphism & Open-Closed Principle)

You are building a checkout system. Different customers get different discounts: StudentDiscount (10%), VIPDiscount (20%), and SeasonalDiscount (variable).

Your lead developer says: 'I don't want to see a single if-else or switch statement in the calculateTotal() method when checking for the discount type.'

How would you use Polymorphism to design this so that adding a new discount type in the future (like BlackFridayDiscount) requires zero changes to the existing checkout logic?

I will create an interface called Discount, which will have the abstract method calculateTotal() and each classes such as StudentDiscount, VIPDiscount and SeasonalDiscount etc will have its own implementation for calculating total, depending on the type of object doing checked out the right class which implementing the Discount interface will get called.

That is exactly the right approach. By shifting the logic from a central "checker" into the objects themselves, you are following the Strategy Design Pattern.

Interviewer Feedback & Correct Answer

  • The Strategy Pattern: You nailed it. By using the Discount interface, the Checkout class only needs to call discount.calculateTotal(). It doesn't care if it's a student, a VIP, or a seasonal promotion.

  • Open-Closed Principle: This is a perfect example of being Open for extension (you can add BlackFridayDiscount by creating a new class) but Closed for modification (you never have to touch the Checkout class code again).

  • Pro-Tip: In a real interview, you might mention that these discount objects could be passed into the Checkout service via Dependency Injection, making the system even more decoupled.



Question 12: The "Circular Reference" Scenario (Memory & Relationships)

Imagine you have two classes: Employee and Department.

  • An Employee has a reference to their Department.

  • A Department has a List<Employee> of everyone working there.

  1. This is a Bidirectional Relationship. From an OOP design perspective, how do you ensure that when an Employee moves to a new Department, both references are updated correctly to prevent 'ghost' data?

  2. Does this circular reference cause a Memory Leak in Java's Garbage Collection process (like it might in older languages like C++)? Why or why not?

Interviewer Feedback & Correct Answer

  • Manual Synchronization: In plain Java (which JPA does behind the scenes), you must implement "Defensive Link Management." Typically, you would modify the setDepartment method in the Employee class to automatically remove the employee from the old department's list and add them to the new one. This maintains Referential Integrity.

  • Memory Management: You'll be glad to know that circular references do not cause memory leaks in Java.

    • Why? Unlike older systems that used "Reference Counting," the JVM uses "Reachability Analysis" (Mark-and-Sweep).

    • The Logic: Even if Employee and Department point to each other, if they are no longer reachable from a GC Root (like a running Thread or a Static variable), the Garbage Collector is smart enough to see them as an isolated "island" and reclaim them both.


Question 13: The "Deep vs. Shallow Copy" Scenario (Cloning Objects)

You are working on a Graphic Design App. You have a Document class that contains a List<Shape> and a Metadata object.

  1. If you implement a 'Duplicate Document' feature using a Shallow Copy, what happens to the shapes in the new document when a user modifies a shape in the original document?

  2. How would you implement a Deep Copy to ensure that the duplicated document is entirely independent, and what are the common pitfalls (like performance or circular references) when doing this manually?

Interviewer Feedback & Correct Answer

  • Shallow Copy: In your scenario, a shallow copy creates a new Document object, but it copies the reference to the existing List<Shape>.

    • The Result: Both the "Original" and the "Duplicate" documents point to the exact same list in memory. If a user moves a circle in Document A, it moves in Document B too. This is usually a bug in a "Duplicate" feature.

  • Deep Copy: To implement this, you must iterate through the List<Shape>, create a new instance of every single Shape, and add those to a brand-new List.

  • Common Pitfalls:

    • Performance: Deep copying very large object graphs (thousands of shapes) can be slow and memory-intensive.

    • Circular References: If Shape A points to Shape B, and Shape B points back to Shape A, a simple recursive deep copy will trigger a StackOverflowError.


Question 14: The "Immutable User Profile" (Design Patterns & Encapsulation)

In a distributed system, you want to ensure that once a UserSession object is created, it cannot be changed by any thread (making it thread-safe without using synchronization).

  1. What are the four main steps you must take to make a Java class truly Immutable?

  2. How does the Builder Pattern complement an Immutable class when that class has 15+ optional fields?

Interviewer Feedback & Correct Answer

  • Refining Step 3 (The Getter Rule): You mentioned "setters," but a truly immutable class should not have any setters at all. For the getters, you are exactly right—if you have a mutable field (like a Date or a List), you must return a defensive copy so the caller can't modify the internal state of your object.

  • The 4 Steps Summary:

    1. Mark the class as final.

    2. Make all fields private and final.

    3. No setter methods.

    4. Initialize all fields via a constructor (performing deep copies of mutable inputs) and return defensive copies in getters.

  • The Builder Pattern: Spot on. When you have 15+ fields, a constructor with 15 arguments is a nightmare to read (new User("John", null, null, 25, true...)). The Builder allows you to set only what you need: User.builder().name("John").age(25).build(). This keeps the final User object immutable while making the creation process readable and flexible.


Question 15: The "Multiple Payment Interfaces" (Interface Segregation)

You have an interface SmartPaymentProvider with methods: payByCreditCard(), payByCrypto(), and payByBankTransfer().

Now, you want to onboard a small vendor who only accepts Credit Cards. If they implement your interface, they are forced to provide 'dummy' or 'empty' implementations for Crypto and Bank Transfers, which is a bad design.

  1. Which SOLID principle are we violating here?

  2. How would you refactor these interfaces to support different vendors with different capabilities without forcing them to implement methods they don't need?

Interviewer Feedback & Correct Answer

  • ISP Violation: You're spot on. Forcing a class to implement methods it doesn't use creates "fat" interfaces and leads to UnsupportedOperationException risks.

  • Refactoring Strategy: While your makePayment() idea is a good general abstraction, to truly follow ISP, the standard approach is to split the one large interface into several smaller, specialized ones:

    • CreditCardPaymentProvider { payByCreditCard() }

    • CryptoPaymentProvider { payByCrypto() }

    • BankTransferProvider { payByBankTransfer() }

  • The Benefit: Now, your small vendor only implements CreditCardPaymentProvider. A larger vendor like PayPal could implement all three. This keeps the code clean and the dependencies minimal.


Question 16: The "Database Connection" (Static Blocks vs. Constructors)

You are writing a class DatabaseConfig. You have some heavy configuration data that needs to be loaded from a file exactly once when the class is first loaded into memory, regardless of how many objects of that class are created.

  1. Would you put this loading logic in a Static Initializer Block or the Constructor? Why?

  2. If the loading logic throws an exception (e.g., FileNotFoundException), how does the JVM handle a failure inside a static block compared to a failure inside a constructor?

Interviewer Feedback & Correct Answer

  • Static Initializer Block: Correct. This is the right place for "one-time" setup. Logic in a Constructor runs every time a new object is created, whereas a Static Block runs once when the class is first loaded by the ClassLoader.

  • Exception Handling (The "Deadly" Part):

    • Constructor: If a constructor fails, you just get an exception, and that specific object isn't created.

    • Static Block: If an unchecked exception occurs here, the JVM throws a ExceptionInInitializerError.

    • The Consequence: Once a class fails to initialize, the JVM marks it as "unusable." Any further attempt to use the class during the application's lifecycle will result in a NoClassDefFoundError. You can't just "try again" without restarting the JVM or reloading the class.


Question 17: The "Method Hiding" Scenario (Static Methods & Polymorphism)

Suppose you have a parent class Parent with a static method display(), and a child class Child that also has a static method display() with the exact same signature.

Parent obj = new Child();
obj.display();
  1. Which version of display() will be called? The one in Parent or the one in Child?

  2. Is this considered Method Overriding? Why or why not?

Interviewer Feedback & Correct Answer
  • The Result: Actually, Parent.display() will be called.

  • The Reasoning: Even though the object is a new Child(), the reference variable obj is of type Parent. Since static methods are resolved at compile-time based on the reference type (not the actual object on the heap), the compiler looks at Parent and links it there.

  • Method Hiding: You nailed this! It is called Method Hiding, not Overriding. Because static methods do not use dynamic method dispatch (the mechanism that enables polymorphism), they don't participate in the "runtime lookup" that makes overriding work.


Question 18: The "Abstract Constructor" (Logic vs. Syntax)

Here is a conceptual question:

  1. Can an Abstract Class have a Constructor?

  2. If you cannot instantiate an abstract class (new MyAbstractClass() is illegal), what is the purpose of having a constructor inside it? How and when does it get executed?

Interviewer Feedback & Correct Answer

  • The Purpose: Even though you can't create an instance of an AbstractClass directly, that class still defines the state (fields) that its children will inherit. The constructor ensures that the "base" part of the object is in a valid state before the child’s constructor even starts.

  • The Execution: When you call new Child(), the first line of the child constructor is implicitly (or explicitly) super(). This chains up to the abstract parent.

  • Real-World Use: Imagine an abstract class BaseService that requires a DatabaseConnection. You can force all subclasses to provide that connection by defining a constructor in the abstract class: public BaseService(DatabaseConnection conn) { ... }.


Question 19: The "Diamond Problem" (Default Methods in Interfaces)

Before Java 8, multiple inheritance of state was forbidden to avoid the 'Diamond Problem.' However, with the introduction of Default Methods in interfaces, we now have a form of multiple inheritance of behavior.

  1. If a class MyService implements two interfaces, InterfaceA and InterfaceB, and both have a default method called logMessage(), what happens when you try to compile the code?

  2. How does Java force you to resolve this conflict?

Interviewer Feedback & Correct Answer

  • The Conflict: If InterfaceA and InterfaceB both have a default logMessage() method, the compiler is paralyzed. It doesn't know which one you want to use. Unlike C++, Java refuses to "guess" or pick one based on order. It will throw an error: "Duplicate default methods named logMessage... inherit from types InterfaceA and InterfaceB."

  • The Resolution: Java forces you to override the method in your class. You must explicitly tell the compiler how to handle the overlap. You can either write your own logic or call a specific interface's version using this unique syntax:

    @Override
    public void logMessage() {
        InterfaceA.super.logMessage(); // Explicitly choosing A
    }

Question 20: The "Object Class Contract" (equals() and hashCode())

You are using a HashMap to store Employee objects as keys.

  1. If you override the equals() method but forget to override the hashCode() method in your Employee class, what happens when you try to retrieve an employee from the Map using a 'theoretically equal' object?

  2. What is the general contract between these two methods? If obj1.equals(obj2) is true, what must be true about their hash codes?

Interviewer Feedback & Correct Answer

  • The Bucket Disaster: You're exactly right. If obj1.equals(obj2) is true but they have different hash codes, the HashMap will look in the wrong bucket when you try to retrieve the value. Even though the object is "equal," the Map thinks it's missing. This leads to duplicate keys in your Map (which shouldn't happen) and "lost" data.

  • The Contract: The rule is absolute: If two objects are equal according to equals(), they MUST have the same hashCode().

    • Note: The reverse is not necessarily true. Two different objects can have the same hash code (this is called a Collision), but it hurts performance.

  • Performance Impact: If your hashCode() method is poorly written and returns the same constant (like return 1;), the HashMap degrades into a LinkedList, turning $O(1)$ lookups into $O(n)$.


Question 21: The "Fragile Base Class" (Composition vs. Inheritance Revisited)

There is a famous saying in OOP: 'Favor Composition over Inheritance.'

  1. Imagine you inherit from a LibraryScanner class to add a 'logging' feature. A year later, the library owners update LibraryScanner and add a new method that your child class already has, but with a different return type. What happens to your code?

  2. How does Composition (holding an instance of LibraryScanner as a field) protect you from these 'Side Effects' of parent class changes?

Interviewer Feedback & Correct Answer

  1. The Conflict: If you have a method public void process() in your child class, and the Library owners later add public int process() to the parent class, your code will not compile. In Java, you cannot have two methods with the same name and parameters but different return types in the same class hierarchy.

  2. Composition as the Savior: With composition, you don't "inherit" the parent's methods. You just call them. If the library adds a new process() method, it doesn't affect your class because your class isn't a "type of" that library class. You have total control over your own method signatures.


Question 22: The "Nested Class" Choice (Static vs. Non-Static)

In your Java project, you have a Car class and a Wheel class. You decide to make Wheel an Inner Class (nested inside Car).

  1. If you declare Wheel as a Static Nested Class, can it access the private String engineType field of the outer Car class?

  2. If you declare it as a Non-Static Inner Class, what 'hidden' reference does the Wheel object hold, and how does this affect memory/garbage collection?

Interviewer Feedback & Correct Answer
  • Static Nested Class: Correct. A static nested class behaves like any other top-level class; it just happens to be packaged inside another. It has no access to the instance variables of the Car (like engineType) because it doesn't "belong" to a specific Car object.

  • Non-Static Inner Class: You're on the right track, but here is the key "under-the-hood" detail:

    • The Hidden Reference: Every instance of a non-static inner class holds an implicit reference to its outer class instance.

    • Memory Impact: This means as long as the Wheel object is alive (e.g., it’s stuck in a long-running queue), the entire Car object cannot be garbage collected, even if you aren't using the car anymore. This is a very common source of memory leaks!


Question 23: The "Checked vs. Unchecked" OOP Design

When designing a custom exception for a Banking Application, you have to choose between extending Exception (Checked) or RuntimeException (Unchecked).

  1. If you create an InsufficientFundsException, which one should it extend and why?

  2. From an OOP perspective, how does using a Checked Exception change the 'Contract' of the method for the developer who calls your API?

Interviewer Feedback & Correct Answer

  • Checked vs. Unchecked: For an InsufficientFundsException, most senior architects recommend extending Exception (Checked).

    • The Reasoning: In a banking app, an "insufficient funds" error is not a programming bug (like a NullPointerException); it is a contingency that is expected to happen in the business flow. By making it checked, you force the developer to write code that handles the scenario (e.g., prompting the user to transfer money or show an error message).

    • When to use Runtime: Use RuntimeException for things that shouldn't happen if the code is correct (like IllegalTransactionStateException).

  • The Method "Contract": You are correct. When you add throws InsufficientFundsException to a method signature, that exception becomes part of the Public API Contract.

    • It signals to the caller: "This operation might fail for this specific reason, and you are legally required to decide how to handle it before your code will even compile.


Question 24: The "Super vs This" in Constructors

In Java, we use super() and this() to call constructors.

  1. Can you have both super() and this() as the first lines in the same constructor?

  2. What happens if you don't write super() or this() in your constructor at all? Does the compiler do anything behind the scenes?

Interviewer Feedback & Correct Answer

  • The "First Line" Rule: You are absolutely correct. Since both this() and super() must be the first statement in a constructor, it is physically impossible to have both. This ensures that the object is initialized in a strictly linear fashion—you either delegate to another constructor in the same class or move up to the parent.

  • The Invisible super(): Correct. If you don't provide a call to super() or this(), the compiler automatically inserts an implicit super() (the no-argument version).

    • The Trap: If the parent class has a constructor with parameters (like public Parent(String name)) but no no-argument constructor, your child class will fail to compile unless you manually call super("some name").


Question 25: The "Polymorphic Variable" Challenge (Object vs Reference)

Suppose you have: List<String> names = new ArrayList<>();

  1. Why is it considered better practice to declare the variable as the interface type (List) rather than the concrete type (ArrayList)?

  2. If you later decide to change ArrayList to LinkedList, what is the impact on the rest of your code that uses the names variable?

Interviewer Feedback & Correct Answer

  • Coding to an Interface: By using List<String> names, you are telling the rest of your program, "I only care that this object behaves like a List; I don't care how it's implemented under the hood." This is a classic example of Abstration and Upcasting.

  • Minimal Impact: Exactly. Because the rest of your code (loops, add() calls, size() checks) only interacts with the List interface methods, changing ArrayList to LinkedList (or even CopyOnWriteArrayList for threading) requires changing exactly one line of code.

  • The Catch: The only time this backfires is if you were relying on a specific method that exists in ArrayList but not in the List interface (like ensureCapacity()), but that is rare in high-quality OOP design.

Java scenario based questions and its answers

 

Question 1: The "Payment Gateway" Scenario (Abstraction vs. Encapsulation)

"Imagine you are designing a Payment Processing System for an e-commerce platform. You need to support multiple payment methods like Credit Cards, PayPal, and Bitcoin.

From a design perspective, how would you use Abstraction to handle these different payment methods? Additionally, how would you use Encapsulation to ensure that sensitive data, like a user's Credit Card CVV or a Private Key, is handled securely within those classes?" 

Interviewer Feedback & Correct Answer

Abstraction: Your approach using an abstract class (or an interface) is spot on. By defining makePayment() at a high level, the calling code (the checkout service) doesn't need to know how a payment is processed, only that it can be processed.

Encapsulation: You correctly identified the use of private fields.

Pro-Tip for Interviews: Mentioning immutability and defensive copying (returning a copy of a mutable object) shows a senior level of understanding.

One small correction: In a real-world high-security scenario, you might actually avoid providing setters for sensitive data like a CVV. You would ideally pass that data via a constructor and use it immediately, never storing it longer than necessary or allowing it to be changed.


Question 2: The "Overcrowded Library" Scenario (Inheritance vs. Composition)

"You are building a Library Management System. You have a class Book. You now need to implement EBook and PhysicalBook.

Initially, you might think of using Inheritance (EBook extends Book). However, many experienced developers prefer Composition (Has-A relationship) over Inheritance (Is-A relationship).

Can you explain a scenario in this Library system where deep inheritance might cause issues (the 'Fragile Base Class' problem), and how you might use Composition instead to make the system more flexible?"

Interviewer Feedback & Correct Answer

While interfaces solve the "multiple inheritance" problem, the Composition vs. Inheritance debate usually focuses on how we reuse code.

  • The Inheritance Trap: If you use EBook extends Book, and later you want to add a RareBook or a DigitalAudioBook, your inheritance tree can become a "Deep Hierarchy." If you change a method in the base Book class, it might accidentally break logic in EBook or PhysicalBook—this is the Fragile Base Class problem.

  • The Composition Solution: Instead of saying an EBook is a Book, you could say a LibraryItem has a Format (Electronic, Physical) and has a Content (The Book details). This allows you to swap behaviors at runtime without changing the class structure.


Question 3: The "Global Shipping" Scenario (Polymorphism)

"You are working for a shipping company like FedEx. You have a method calculateShippingCost(Package pkg).

  1. How would you use Method Overriding (Runtime Polymorphism) to handle different shipping rules for InternationalPackage and DomesticPackage?

  2. Now, suppose you also need to calculate costs differently based on the input provided: one version of the method takes just weight, and another takes weight, dimensions, and priority. How does Method Overloading (Compile-time Polymorphism) help you here, and why is it useful for the developer using your API?"

Interviewer Feedback & Correct Answer

Method Overriding (Runtime):
You nailed the core concept. By calling calculateShippingCost() on a Shipping reference that points to either an InternationalPackage or DomesticPackage instance, the JVM decides which method to run at runtime based on the actual object type.

Method Overloading (Compile-time): You mentioned the input parameters, which is the key. Overloading allows the same method name to exist with different signatures (e.g., calculate(weight) vs calculate(weight, dimensions)). This is determined at compile-time.

Benefit: It makes the API "cleaner." The developer doesn't have to remember names like calculateByWeight() and calculateByWeightAndDim(); they just call calculate() and the compiler picks the right one.

Question 4: The "Final Security" Scenario (The final Keyword)

"In a high-security banking application, you have a class called InterestCalculator.

  1. Why might you mark the calculateBaseRate() method as final? What specific risk are you trying to prevent?

  2. If you decide to make the entire InterestCalculator class final, how does that change the way other developers can interact with your code, and when is this a better choice than just marking methods as final?"Final Method: By marking the method final, you ensure the integrity of the calculation. No matter how many subclasses you create for different regions (e.g., USInterestCalculator), the core "Base Rate" logic remains immutable.

Interviewer Feedback & Correct Answer

Final Class:
Marking the entire class final prevents Inheritance altogether.

Why use it? This is often done for "Utility" classes (like java.lang.Math) or "Immutable" classes (like java.lang.String).

The Benefit: It ensures that no one can ever create a "fake" version of your calculator. If your class is final, you can be 100% sure that any InterestCalculator object in the system behaves exactly as you wrote it, with no hidden subclass behavior.

Question 5: The "Smart Home" Scenario (Interface vs. Abstract Class)

"You are designing a Smart Home Controller. You have various devices: SmartLight, SmartThermostat, and SmartSecurityCamera.

  1. All devices need to have a powerOn() and powerOff() method.

  2. However, some devices (like the Thermostat) need to keep track of a currentTemperature state, whereas the Light doesn't care about temperature.

In this scenario, would you use an Abstract Class or an Interface to define the 'Device'? Explain your choice based on how you would handle the shared state (like a deviceID or status) versus shared behavior."

Interviewer Feedback & Correct Answer

The Interface Approach: You are correct that an interface like SmartDeviceActions is perfect for defining what the devices do (powerOn, powerOff).

The Missing Piece (Shared State): If every device needs a deviceID, manufacturerName, and connectionStatus, using only an interface would force you to declare those variables in every single class (SmartLight, SmartThermostat, etc.). This leads to code duplication.

  • The Hybrid Solution (Best Practice): In a real-world interview, the "pro" answer is often a mix:

    1. Interface: Defines the contract (Switchable).

    2. Abstract Class: Implements the interface and holds the common state (private fields like id and status).

    3. Concrete Classes: Extend the abstract class.

Key takeaway: Use an Abstract Class when you want to share code and state (fields). Use an Interface when you only want to share method signatures (behavior).

 

Question 6: The "Memory Leak" Scenario (Static vs. Instance)

"You are building a Logging Utility for a large application. You decide to create a class Logger.

  1. If you make the log(String message) method static, how does that change how other classes call it compared to making it an instance method?

  2. From a Memory Management perspective, if you have a static List<String> logHistory inside that class that stores every log message ever created, what is the risk as the application runs for several days? How does the JVM Garbage Collector treat static variables?"

Interviewer Feedback & Correct Answer

Static Access: Correct. Using Logger.log() is convenient because it doesn't require state, making it a perfect candidate for a utility method.
The Memory Risk (Static Analysis): You accurately identified the "Memory Leak" risk.

The GC Behavior: Since static variables are stored in the Metaspace (specifically within the Class object) and are reachable as long as the ClassLoader is active, the Garbage Collector will not reclaim the memory used by your logHistory list.

The Consequence: If the list grows indefinitely, you will eventually hit an OutOfMemoryError. In a real-world scenario, you would use a "Circular Buffer" or a file-based appender to prevent this.

Question 7: The "Order Processing" Scenario (Exception Handling & OOP)

"Now, let's talk about how OOP handles errors. You are designing an Order Placement System.

  1. If a user tries to buy an item that is out of stock, should you throw a Checked Exception (e.g., extends Exception) or an Unchecked Exception (e.g., extends RuntimeException)? Why?

  2. How does the 'Catch-or-Specify' requirement of Checked Exceptions influence the design of your service layer? Does it make the code more robust or just 'noisier'?"


Clarification on "Compile-time": Actually, all exceptions happen at runtime. The difference is that for Checked Exceptions, the compiler forces you to acknowledge them before the code will even run. For Unchecked (Runtime) Exceptions, the compiler lets you ignore them.

The "Out of Stock" Choice: * The Case for Unchecked: Many modern frameworks (like Spring) prefer RuntimeExceptions because they keep the code clean and avoid "Exception Plumbing" (passing throws declarations through 10 layers of code).

The Case for Checked: Traditionally, an "Out of Stock" scenario is a recoverable business condition. By making it a Checked Exception, you force the developer writing the UI to handle it (e.g., show a "Notify Me" button) rather than letting the whole application crash.

The "Noisiness": Checked exceptions make the code "noisier" because of the mandatory try-catch blocks, but they act as a form of API Documentation—telling the next developer, "Hey, you must handle this specific scenario."

Interviewer Feedback & Correct Answer
  • Constructor Overloading: Correct. It provides flexibility. One tip: to avoid duplicating code, you can use this(...) to have one constructor call another (Constructor Chaining). This ensures your validation logic stays in one place.

  • Private Constructors: You correctly identified the Singleton Pattern.

    • How to get the instance: Since the constructor is private, the class provides a public static method (usually named getInstance()). Inside that method, the class checks if an instance already exists; if not, it creates one and returns it.

    • Other use cases: Private constructors are also used in Utility Classes (like java.lang.Math) where you only have static methods and want to prevent anyone from ever creating an object of that class.


Interviewer Feedback & Correct Answer
  • instanceof Syntax: In Java, the syntax is actually object instanceof ClassName. So, it would be if (result instanceof WebPage). Since Java 14, we have Pattern Matching for instanceof, which allows you to check and cast in one line: if (result instanceof WebPage wp) { wp.display(); }. This avoids the extra step of manual casting.

  • The Risk of Downcasting: You mentioned "inconsistent behavior," but specifically, the risk is a ClassCastException.

    • If you tell the JVM "Treat this Object as a VideoResult" but the object is actually a WebPage, the JVM will throw that exception at runtime and crash your program.

    • The Best Practice: Always guard your downcasts with an instanceof check or use Polymorphism (an interface) to avoid casting altogether.


Question 10: The "Final Challenge" (Composition & Encapsulation in a System)

"This is a common 'System Design Lite' question to wrap up OOP.

Imagine you are designing an Email Notification System. You have a User class and an EmailService class.

  1. Should the User class extend EmailService, or should the User class have a private field of type EmailService? Why?

  2. If the EmailService needs to change its provider from 'SendGrid' to 'AWS SES', which of the two designs (Inheritance vs. Composition) makes it easier to update the code without touching the User class?"

Interviewer Feedback & Correct Answer

  • Composition vs. Inheritance: You are 100% correct. If User extended EmailService, every User object would "be" a mailer, which makes no sense semantically. By making it a private field, you keep the User class focused on user data (Single Responsibility Principle).

  • The Provider Switch: Your suggestion of using an Interface is the "Senior Developer" answer.

    • If User depends on an EmailService interface, you can swap the implementation from SendGridProvider to AwsSesProvider at runtime or via configuration (Dependency Injection).

    • The User class doesn't even need to know the provider changed because it only interacts with the interface's send() method.

 


How to Create Multi-Module Maven Springboot Project

Hello Readers,

Today I will try to explain how we can create maven multi-module project for Springboot applications. There are multiple ways to achieve this like using maven commands but I am going to use a simple approach of using start.spring.io and then importing this created project into eclipse. Then adding the new maven modules.

Lets start.
  1. Navigate to https://start.spring.io/
  2. Select project as Maven Project, Language as Java, Spring Boot version as 2.1.3
  3. Under project matadata, enter group name as something like com.yourcomapny and artifact as project name like patient-service
  4. Under dependencies select what all the dependencies you want like Web, DevTools, Actuators etc

  5. Once the project is created, download and unzip into some location
  6. Open your Eclipse IDE, Click on Import and then import the project into eclipse as Existing maven project and then click finish
  7. Once the project got imported, change the patient-servie pom.xml packaging type to pom as shown in the picture below


  8. Now on the patient-service project, right click --> Maven --> New Maven Module Project


  9. Select the checkbox mentioning Create a simple project and provide module name as patient-service-api and then click finish
  10. Now the new maven module project is been created. After this create one more maven module project by repeating step 8 and step 9 with the new module name as patient-service-app
  11. The intention behind the multi module project creation is that in one module we will keep all the interfaces which might be required to be exposed to outside world and keeping the implementation module hidden with outside world.
  12. Now that we have a parent project and two submodules ready, it’s a time for us to have dependency of patient-service-api module onto patient-service-app like below


  13. Just type few characters of the dependency name and it will display the all relevant dependencies by that name. Select the dependency we need and click OK
  14. Once the patient-service-api dependency is added into patient-service-app module the pom.xml file of patient-service-api will look like below



  15. However, the patient-service-api pom.xml is simple one with no dependencies apart from inheriting the parent patient-service project like below




  16. The parent, i.e. patient-service pom.xml would have all springboot starter dependencies.
  17. Now reactor the main class of patient-service and move that main class to patient-service-app
  18. Right click on that main class from patient-service-app then run as Java application or if you have STS plugins added you can select run as springboot application. This will start the application on the port 8080 by default unless you have explicit port configurations.

So by following all the above steps we can have our multi-module maven springboot project ready for working.

in Java, what are the ways to create an object?

Hi Folks,

After a long time I am back with the new post to discuss about the ways of creating a new object in java.

Basically there are multiple ways in which you can create an object instance in java and those are as below:

1. Using new keyword




2. Using class.newInstance()



3. Using class.getConstructors()


4. Using clone()



5. Using Object de-serialization
Make sure the employee class implements Serializable interface

So these are some of the ways in which we create objects in java. Let me know what did you felt about this blog in comments section.