1 β Why we need to use OOP ? Some major OOP languages ?
Object-Oriented Programming (OOP) is a popular programming paradigm that helps in organizing and managing complex software systems. Hereβs why it is essential:
π 1. Modularity (Code Reusability) Β· Why? Write code once and reuse it across different parts of your application. Β· Example: You can create a Vehicle class and reuse it for Car, Bike, and Truck objects without rewriting code.
π 2. Maintainability (Easier to Update and Debug) Β· Why? Changes in one part of the code can be localized and easier to maintain. Β· Example: If you update a method in a parent class, all child classes automatically inherit the change.
π 3. Scalability (Handle Large Systems) Β· Why? OOP allows you to break complex systems into smaller, manageable objects. Β· Example: In a banking system, you can have classes like Account, Customer, and Transaction to represent different components.
π 4. Abstraction (Hide Complex Implementation) Β· Why? Focus on what an object does rather than how it does it. Β· Example: A Payment class may have different implementations (Credit Card, PayPal) without exposing the internal details.
π 5. Encapsulation (Data Security) Β· Why? Protect object data by restricting direct access. Β· Example: Private fields in a User class ensure sensitive data (like passwords) cannot be modified directly.
π 6. Polymorphism (Flexibility & Extensibility) Β· Why? Use the same interface for different data types. Β· Example: A draw() method can be used to render circles, rectangles, or polygons without changing the calling code.
π 7. Inheritance (Code Efficiency) Β· Why? Create a new class from an existing class, reducing code duplication. Β· Example: A Bird class can inherit properties from an Animal class and add specific behaviors like fly().
β Some Major OOP Languages
Java Pure OOP, platform-independent (Write Once, Run Anywhere) Python Multi-paradigm (OOP + Procedural), dynamic typing C++ High-performance, supports both OOP and procedural C# OOP-centric, commonly used for .NET applications Ruby Everything is an object, dynamic and flexible Swift OOP + Protocol-Oriented, used for iOS/macOS development JavaScript Prototype-based OOP, dominant in web development Kotlin Modern OOP + Functional, used for Android development
π― In Short: Β· Why OOP? For better structure, reusability, and scalability. Β· Major OOP Languages: Java, Python, C++, C#, and more.
2 β Interface vs Abstract class ?
When deciding between an interface and an abstract class, understanding their differences is key. Here's a breakdown:
β 1. Definition Β· Interface: A contract that defines a set of abstract methods (no implementation) that a class must implement. Β· Abstract Class: A class that may contain both abstract methods (without implementation) and concrete methods (with implementation).
Methods | Only abstract methods (before Java 8) | Can have both abstract and concrete methods Fields | Only public, static, and final (constants) | Can have instance variables (non-final) Access Modifiers | Methods are public by default | Can have methods with any visibility Inheritance | A class can implement multiple interfaces | A class can extend only one abstract class Default Methods | Supported (since Java 8) | Supported (via concrete methods) Constructor | No constructors allowed | Can have constructors Use Case | Define a contract for multiple classes | Provide a base class with shared code Performance | Slightly faster (no inheritance hierarchy) | Slightly slower due to inheritance
β 3. When to Use What?
When you want to define a contract | When you want to provide common behavior For multiple inheritance needs | If future changes are expected To achieve loose coupling | When methods have a default behavior Ideal for defining capabilities (e.g., Flyable, Serializable) | For hierarchical relationships (e.g., Animal -> Dog)
β 4. Example Code Interface Example:
interface Animal {
void makeSound(); // Abstract method (no body)
}
class Dog implements Animal {
public void makeSound() {
System.out.println("Woof!");
}
}
Abstract Class Example:
````java
abstract class Animal {
abstract void makeSound(); // Abstract method
void sleep() { // Concrete method
System.out.println("Sleeping...");
}
}
class Dog extends Animal {
void makeSound() {
System.out.println("Woof!");
}
}
β
5. Summary
Β· Interfaces: Ideal for defining a contract for unrelated classes.
Β· Abstract Classes: Best for defining shared behavior in a class hierarchy.
3 β Why wee need equals and hashcode ? When to override ?
β
Why Do We Need equals() and hashCode() in Java?
In Java, the equals() and hashCode() methods play a crucial role in comparing objects and ensuring the correct functioning of collections like HashMap, HashSet, and HashTable.
π 1. equals() Method
Β· Purpose: To compare the contents of two objects for equality.
Β· Default Behavior (from Object class): Compares memory addresses (reference equality), not the actual data.
β
Why Override equals()?
When you want to compare object values rather than memory locations.
Example Without Overriding equals():
````java
class Person {
String name;
Person(String name) {
this.name = name;
}
}
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // Output: false (different memory locations)
Example With Overridden equals():
````java
class Person {
String name;
Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Same reference
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
}
System.out.println(p1.equals(p2)); // Output: true (compares values)
π 2. hashCode() Method
Β· Purpose: Generates a unique integer (hash code) representing an object.
Β· Default Behavior (from Object class): Returns the object`s memory address as a hash code.
β
Why Override hashCode()?
If you override equals(), you must override hashCode() to maintain consistency when using hash-based collections (e.g., HashMap, HashSet).
Example of HashMap Without Overriding hashCode():
````java
Map<Person, String> map = new HashMap<>();
map.put(new Person("Alice"), "Developer");
System.out.println(map.get(new Person("Alice"))); // Output: null (hashes differ)
Fixed by Overriding hashCode():
````java
@Override
public int hashCode() {
return Objects.hash(name); // Generates a hash from the name
}
π 3. When to Override equals() and hashCode()?
You should override these methods when:
β
You want to compare object values instead of references.
β
You plan to use the object in hash-based collections (e.g., HashMap, HashSet, HashTable).
β
You want to ensure logical equality between two objects.
π 4. Key Rules for Overriding
1. Consistency Rule: If two objects are equal using equals(), they must return the same hash code.
2. Symmetry Rule: If a.equals(b) is true, then b.equals(a) must also be true.
3. Transitivity Rule: If a.equals(b) and b.equals(c) are both true, then a.equals(c) must also be true.
π 5. Best Practice: Use Objects Utility
Java provides a convenient Objects class for null-safe comparison and hashing.
Example:
````java
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
π― Summary
Β· equals() checks for content equality (override when comparing by value).
Β· hashCode() provides a unique identifier (override when using hash collections).
Β· Always override both together to maintain collection integrity.
4 β Diamond problem in Java ? How to fix it?
β
What is the Diamond Problem in Java?
The diamond problem occurs in languages that support multiple inheritance, where a class inherits from two classes that have a common ancestor. This leads to ambiguity in method resolution.
π 1. Understanding the Diamond Problem
Consider the following inheritance structure:
A
/ \
B C
\ /
D
Β· B and C both inherit from A.
Β· D inherits from both B and C.
Β· If A has a method (display()), and both B and C override it, then D does not know which version of display() to inherit.
β Does Java Support Multiple Inheritance?
Β· Java does not allow multiple inheritance with classes (i.e., class D extends B, C is not allowed).
Β· This restriction prevents the diamond problem at the class level.
β
How to Fix the Diamond Problem?
πΉ 1. Use Interfaces (Multiple Inheritance with Interfaces)
Java allows multiple inheritance through interfaces, but to resolve the diamond problem, default methods must be explicitly handled.
Example with Default Methods (Causing Diamond Problem)
````java
interface A {
default void show() {
System.out.println("A's show()");
}
}
interface B extends A {
default void show() {
System.out.println("B's show()");
}
}
interface C extends A {
default void show() {
System.out.println("C's show()");
}
}
class D implements B, C {
// Compilation Error: show() method is ambiguous
}
β
Solution: Override the Method in Class D
````java
class D implements B, C {
@Override
public void show() {
// Explicitly choose which interface method to call
B.super.show(); // or C.super.show();
}
}
public class Main {
public static void main(String[] args) {
D obj = new D();
obj.show(); // Output: "B's show()"
}
}
πΉ How This Fix Works:
Β· Java forces D to override show() and explicitly specify which version to use (B.super.show() or C.super.show()).
Β· This removes the ambiguity.
β
Summary
Approach Fix for Diamond Problem
---------------------------------------------------------------------------------------------------------
Classes | Java does not allow multiple inheritance with classes.
Interfaces | Multiple inheritance is allowed, but methods must be resolved explicitly using super.
Solution | Override the method in the subclass (D) and specify which parentβs method to use.
π‘ Java prevents the diamond problem at the class level but requires explicit resolution in interfaces.
5 β Why we need Garbagge Collector ? How does it run ?
β
Why Do We Need a Garbage Collector in Java?
In languages like C and C++, developers must manually allocate and deallocate memory. This can lead to issues like:
1. Memory Leaks: Memory that is no longer used but not released.
2. Dangling Pointers: Accessing memory that has already been freed.
3. Double Free Errors: Trying to free the same memory twice.
Java solves these problems by using the Garbage Collector (GC), which automatically manages memory by reclaiming unused objects.
π 1. What is Garbage Collection (GC)?
Garbage Collection is the process by which Java automatically identifies and removes unused objects to free up memory.
Β· Unused Objects: Objects that are no longer referenced and canβt be accessed by the application.
Example:
````java
public class Example {
public static void main(String[] args) {
Example obj = new Example(); // Object created
obj = null; // Object is now eligible for GC
}
}
When obj is set to null, there are no references to the object, making it eligible for garbage collection.
π 2. How Does the Garbage Collector Work?
The Java Virtual Machine (JVM) manages garbage collection in several steps:
π’ Step 1: Object Allocation
Β· Objects are created in the heap memory.
Β· JVM keeps track of live and unused objects.
π΅ Step 2: Object Reachability
An object is considered reachable if:
1. It is referenced by active threads or static fields.
2. It is accessible via the stack or global references.
Objects not reachable are eligible for garbage collection.
π Step 3: Garbage Collection Process
1. Mark: Identify and mark all reachable objects.
2. Sweep: Remove unreachable (garbage) objects from memory.
3. Compact: Optional step to defragment memory for efficient allocation.
π 3. Types of Garbage Collectors in Java
Java provides different types of Garbage Collectors:
Garbage Collector Description Use Case
----------------------------------------------------------------------------------------------------------------------
Serial GC | Uses a single thread (simple and slow). | Suitable for small applications.
Parallel GC (Throughput GC) | Uses multiple threads for better performance. | Best for multi-core CPUs.
G1 GC (Garbage First) | Splits the heap into regions and cleans incrementally. | Low-latency apps (e.g., web servers).
ZGC | Ultra-low-pause GC for large heaps (up to 16 TB). | Large-scale and real-time systems.
Shenandoah GC | Concurrent, low-pause GC. | Ideal for low-latency applications.
You can specify the collector using JVM options, e.g.:
bash
java -XX:+UseG1GC MyApp
π 4. When Does the Garbage Collector Run?
The JVM triggers the GC under these conditions:
1. Low Memory: When free memory is insufficient for new objects.
2. Explicit Request: Using System.gc() (not guaranteed but suggests GC).
3. Idle Time: JVM may trigger GC during application downtime.
π 5. How to Optimize Garbage Collection?
Β· Use appropriate GC (e.g., G1 for low-latency apps).
Β· Avoid creating unnecessary or large objects.
Β· Use Weak References for cache objects (via WeakReference, SoftReference).
Β· Profile memory using tools like:
o VisualVM
o Eclipse Memory Analyzer (MAT)
o Java Flight Recorder (JFR)
π 6. Common Misconceptions
β "GC frees everything immediately." β GC works asynchronously.
β "System.gc() forces GC." β It only suggests GC but does not guarantee it.
β "GC means no memory leaks." β Strong references prevent GC if you forget to clear them.
π― Summary
Β· Why GC?: Automatic memory management, prevents memory leaks and dangling pointers.
Β· How it works?: Mark objects, sweep garbage, and compact memory.
Β· Types of GC: Serial, Parallel, G1, ZGC, and Shenandoah.
Β· When?: JVM automatically decides, but you can request with System.gc().
6 β Java βstaticβ keyword usage ?
static Keyword in Java β Usage and Examples
The static keyword in Java is used for memory management and allows members (variables, methods, blocks, and nested classes) to belong to the class rather than to specific instances of the class. It means these members are shared across all instances of the class.
π 1. static Variables (Class Variables)
A static variable is shared by all instances of a class. It is initialized once when the class is loaded into memory.
β
Example:
````java
class Employee {
static int count = 0; // Static variable
String name;
Employee(String name) {
this.name = name;
count++; // Increment count for each object
}
void display() {
System.out.println(name + " - Total Employees: " + count);
}
}
public class Main {
public static void main(String[] args) {
Employee e1 = new Employee("Alice");
Employee e2 = new Employee("Bob");
e1.display(); // Output: Alice - Total Employees: 2
e2.display(); // Output: Bob - Total Employees: 2
}
}
π Why use static variables?
Β· Shared across all objects (e.g., counters, constants).
Β· Saves memory because itβs created only once.
π 2. static Methods (Class Methods)
A static method belongs to the class and can be called without creating an object.
β
Example:
````java
class MathUtils {
static int add(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
int sum = MathUtils.add(5, 10);
System.out.println("Sum: " + sum); // Output: Sum: 15
}
}
π Why use static methods?
Β· For utility methods (e.g., Math.max(), Arrays.sort()).
Β· No need to create an object to use the method.
β οΈ Rules for static methods:
1. Cannot access non-static (instance) members directly.
2. Cannot use this or super keywords.
π 3. static Blocks (Static Initialization Block)
A static block is executed once when the class is loaded. It is commonly used to initialize static variables.
β
Example:
````java
class Config {
static String appName;
static {
appName = "MyApp";
System.out.println("Static Block Executed!");
}
static void display() {
System.out.println("App Name: " + appName);
}
}
public class Main {
public static void main(String[] args) {
Config.display();
// Output:
// Static Block Executed!
// App Name: MyApp
}
}
π Why use static blocks?
Β· To initialize complex static data.
Β· For one-time setup (e.g., loading configuration).
π 4. static Classes (Nested Static Class)
Java allows static nested classes inside another class. These are also known as static inner classes.
β
Example:
````java
class Outer {
static class Inner {
void display() {
System.out.println("Inside Static Inner Class");
}
}
}
public class Main {
public static void main(String[] args) {
Outer.Inner innerObj = new Outer.Inner();
innerObj.display(); // Output: Inside Static Inner Class
}
}
π Why use static nested classes?
Β· When a class is logically related to another but doesnβt need access to the outer classβs instance.
Β· Better encapsulation and organization.
π 5. static Imports (Importing Static Members)
You can use static imports to directly reference static methods or variables from another class without qualifying them.
β
Example:
````java
import static java.lang.Math.*;
public class Main {
public static void main(String[] args) {
System.out.println(sqrt(64)); // Output: 8.0 (no Math.sqrt())
}
}
π Why use static imports?
Β· Simplifies access to constants (e.g., PI) and utility methods.
Β· Improves code readability.
π 6. static with main() Method
The main() method in Java is static because:
1. It allows the JVM to call main() without creating an object.
2. It`s the entry point of every Java application.
β
Example:
````java
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}
π 7. static Final Constants
If you want to create a constant value, declare it as static final.
β
Example:
````java
class Constants {
static final double PI = 3.14159;
}
public class Main {
public static void main(String[] args) {
System.out.println(Constants.PI); // Output: 3.14159
}
}
π Summary of static Usage
Feature Usage Purpose
--------------------------------------------------------------------------------------------------
static Variable | static int count; | Share across all instances (e.g., counters).
static Method | static void display() | Call without an object (e.g., utility methods).
static Block | static { /* init code */ } | One-time static initialization (e.g., configs).
static Class | static class Inner {} | Nested class without outer class access.
static Import | import static java.lang.Math.*; | Use static members without class name.
static Final | static final int MAX = 100; | Define constants.
π― When to Use static?
Β· Use for shared data across instances.
Β· For utility methods (e.g., Math.max()).
Β· To manage constants with static final.
Β· When a nested class doesnβt need to access the outer class.
7 β Immutability means ? Where, How and Why to use it ?
β
What is Immutability?
Immutability means that an objectβs state cannot be changed after it is created. Once an immutable object is constructed, its fields and properties are fixed and unchangeable.
π 1. Characteristics of an Immutable Object:
1. State cannot be modified after object creation.
2. Thread-safe by design (no risk of concurrent modifications).
3. No setters (modifying methods).
4. Final fields to prevent reassignment.
π 2. Examples of Immutable Objects in Java
β
Built-in Immutable Classes:
Β· String
Β· Wrapper classes (e.g., Integer, Double, Boolean)
Β· LocalDate, LocalTime, LocalDateTime (from java.time package)
π 3. Why Use Immutability?
Benefit | Explanation
----------------------------------------------------------------------------------------------------------------
Thread Safety | Immutable objects cannot be modified, making them safe in concurrent environments.
Caching & Optimization | Java can cache immutable objects (e.g., String Pool), improving performance.
Security | Immutable objects are inherently safer against unauthorized changes.
Simplified Debugging | No unexpected side effects from object modifications.
Safe Sharing | Can be shared across classes and threads without cloning or defensive copying.
π 4. How to Create Immutable Objects in Java
β
Example: Creating a Custom Immutable Class
````java
final class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
β
Key Rules for Immutability:
1. Declare the class final: Prevents subclass modification.
2. Declare fields private and final: Prevents modification after construction.
3. No setters: Avoid providing methods that change object state.
4. Return copies: For mutable fields, return a defensive copy.
π 5. Handling Mutable Fields in Immutable Classes
If a class contains mutable objects (e.g., List, Map), ensure you:
1. Return a copy of the object.
2. Create defensive copies in the constructor.
β
Example with Mutable Field:
````java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
final class Employee {
private final String name;
private final List<String> tasks;
public Employee(String name, List<String> tasks) {
this.name = name;
this.tasks = new ArrayList<>(tasks); // Defensive copy
}
public String getName() {
return name;
}
public List<String> getTasks() {
return Collections.unmodifiableList(tasks); // Immutable view
}
}
π 6. Where to Use Immutability
1. Data Transfer Objects (DTOs): Pass data between layers in an application.
2. Value Objects: Model values that represent data without an identity (e.g., Money, Date).
3. Configuration Settings: Ensure constant settings are unmodifiable.
4. Caching: Use immutable objects for safe and efficient caching.
5. Concurrency: In multi-threaded applications to avoid synchronization issues.
π 7. Immutability vs. Mutability
Feature Immutable Objects Mutable Objects
-----------------------------------------------------------------------------------------
State Change Cannot be modified after creation Can be changed after creation
Thread Safety Naturally thread-safe Requires external synchronization
Performance May have better caching Higher memory overhead on changes
Example String, Integer, LocalDate StringBuilder, ArrayList, HashMap
π 8. How Java Ensures Immutability Internally
β
String Class Example:
1. Declared as final β Cannot be extended.
2. Character array is private final char[].
3. Any operation (e.g., concat(), replace()) returns a new object.
Example:
````java
String str = "Java";
str.concat(" Rocks!");
System.out.println(str); // Output: "Java"
Explanation: concat() returns a new String, the original str is unchanged.
π 9. Common Mistakes When Implementing Immutability
β Forgetting Defensive Copies: Directly exposing mutable fields.
β Providing Setters: Allowing modifications.
β Non-Final Fields: Allowing value reassignment.
π 10. Best Practices for Immutability
1. Prefer immutability for small objects and value types.
2. Use immutable collections (List.of(), Collections.unmodifiableList()).
3. Use record classes (from Java 14+) for concise immutable objects.
β
Example Using record (Java 14+):
````java
public record Person(String name, int age) {}
This automatically provides:
Β· final fields
Β· A constructor
Β· equals(), hashCode(), toString()
π― In Summary:
Β· Immutability ensures that objects cannot be changed after creation.
Β· Use immutability for safety, performance, and simplicity.
Β· Follow best practices with final, defensive copying, and no setters.
8 β Composition and Aggregation means and differences ?
β
Composition vs. Aggregation in Java (Object-Oriented Programming)
Both Composition and Aggregation represent "Has-A" relationships between objects but differ in ownership and lifecycle management.
π 1. What is Composition?
In Composition, one object is strongly dependent on another. If the parent object is destroyed, the child object is also destroyed.
β
Key Characteristics:
Β· Strong association (Whole-Part relationship).
Β· Child cannot exist without the parent.
Β· Tight coupling between objects.
β
Example of Composition in Java:
````java
class Engine {
private String type;
public Engine(String type) {
this.type = type;
}
public void start() {
System.out.println("Engine started: " + type);
}
}
class Car {
private final Engine engine; // Composition (Car "has-a" Engine)
public Car(String engineType) {
this.engine = new Engine(engineType); // Engine created inside Car
}
public void drive() {
engine.start();
System.out.println("Car is driving");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car("V8");
car.drive();
// Output:
// Engine started: V8
// Car is driving
}
}
π Key Insight:
When the Car object is destroyed, its Engine object is also destroyed.
π 2. What is Aggregation?
In Aggregation, one object is loosely dependent on another. The parent and child can exist independently.
β
Key Characteristics:
Β· Weak association (Has-A relationship).
Β· Child can exist without the parent.
Β· Loose coupling between objects.
β
Example of Aggregation in Java:
````java
class Department {
private String name;
public Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class University {
private String universityName;
private Department department; // Aggregation
public University(String universityName, Department department) {
this.universityName = universityName;
this.department = department; // Injected from outside
}
public void display() {
System.out.println(universityName + " has " + department.getName() + " Department");
}
}
public class Main {
public static void main(String[] args) {
Department cs = new Department("Computer Science");
University uni = new University("Tech University", cs);
uni.display();
// Output:
// Tech University has Computer Science Department
}
}
π Key Insight:
Β· The Department object exists independently of the University.
Β· Destroying the University object does not affect the Department.
π 3. Key Differences: Composition vs. Aggregation
Feature | Composition Aggregation
-----------------------------------------------------------------------------------------------
Relationship Type | Strong (Part-Whole) Weak (Association)
Dependency | Child depends on parent Child is independent of parent
Lifecycle | Parentβs destruction destroys child Parentβs destruction does not affect child
Example | Car has-a Engine (Composition) University has-a Department (Aggregation)
Coupling | Tight coupling (High dependency) Loose coupling (Low dependency)
Object Creation | Child is created inside the parent Child is passed from outside
π 4. When to Use Composition vs. Aggregation
Scenario Use Composition Use Aggregation
------------------------------------------------------------------------------------------------------------
Ownership Parent owns the child completely. Child can exist without the parent.
Lifecycle Dependency Child cannot exist without parent. Child can live independently.
Tight vs Loose Coupling When objects need close integration. When you need flexibility between objects.
Example Car and Engine, Human and Heart. University and Department, Team and Players.
π 5. Advanced Example:
Let`s model a Library System using both Composition and Aggregation.
β
Composition (Book and Pages):
A Book is composed of Pages. If you destroy the book, its pages also disappear.
````java
class Page {
private int pageNumber;
public Page(int pageNumber) {
this.pageNumber = pageNumber;
}
public void printPage() {
System.out.println("Page: " + pageNumber);
}
}
class Book {
private final List<Page> pages = new ArrayList<>();
public Book(int pageCount) {
for (int i = 1; i <= pageCount; i++) {
pages.add(new Page(i)); // Creating pages (Composition)
}
}
public void readBook() {
pages.forEach(Page::printPage);
}
}
public class Main {
public static void main(String[] args) {
Book book = new Book(3);
book.readBook();
}
}
β
Aggregation (Library and Books):
A Library contains Books, but Books can exist independently.
````java
class Book {
private String title;
public Book(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
class Library {
private String name;
private List<Book> books;
public Library(String name, List<Book> books) {
this.name = name;
this.books = books; // Aggregation (Books from outside)
}
public void showBooks() {
for (Book b : books) {
System.out.println(name + " has: " + b.getTitle());
}
}
}
public class Main {
public static void main(String[] args) {
Book b1 = new Book("Java Basics");
Book b2 = new Book("Advanced Java");
List<Book> books = Arrays.asList(b1, b2);
Library library = new Library("City Library", books);
library.showBooks();
}
}
π 6. Summary
Feature | Composition | Aggregation
------------------------------------------------------------------------------------------------------------------
Dependency | Strong β Child depends on Parent | Weak β Child can exist without Parent
Lifecycle | Parentβs removal destroys the child | Parentβs removal does not destroy the child
Object Creation | Created inside the parent | Passed from outside
Coupling | Tightly coupled (Highly dependent) | Loosely coupled (Independent)
Example | Car has-a Engine | University has-a Department
9 β Cohesion and Coupling means and differences ?
β
Cohesion vs. Coupling in Software Design (OOP Concepts)
Cohesion and Coupling are key principles of object-oriented programming that directly impact the maintainability, scalability, and readability of code.
π 1. What is Cohesion?
Cohesion refers to how closely related and focused the responsibilities of a module, class, or method are. A highly cohesive module performs one well-defined task.
β
Key Characteristics of Cohesion:
Β· High cohesion = Focused and performs one specific responsibility.
Β· Low cohesion = Does too many unrelated things, making it hard to maintain.
Β· Improves code readability, reusability, and modularity.
β
Example of High Cohesion (Good Practice)
A Student class with methods only related to student operations:
````java
class Student {
private String name;
private int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
public void displayDetails() {
System.out.println("Student: " + name + ", ID: " + id);
}
public double calculateGrade(double marks) {
return marks / 10.0;
}
}
β
Why is this high cohesion?
Β· All methods are specific to the Student entity.
Β· Each method performs a single responsibility.
β Example of Low Cohesion (Bad Practice)
A Student class doing unrelated tasks (like student info and database operations):
````java
class Student {
private String name;
private int id;
public void displayDetails() {
System.out.println("Student: " + name + ", ID: " + id);
}
// Database operation (unrelated responsibility)
public void saveToDatabase() {
System.out.println("Saving student to database...");
}
// Email sending (unrelated responsibility)
public void sendEmail() {
System.out.println("Sending email to student...");
}
}
β Why is this low cohesion?
Β· The class handles multiple responsibilities:
o Displaying student information
o Database operations
o Sending emails
Β· Violates the Single Responsibility Principle (SRP).
π 2. What is Coupling?
Coupling refers to the degree of dependency between two modules, classes, or components. It defines how interconnected or independent the components are.
β
Key Characteristics of Coupling:
Β· Tight coupling = High dependency between components.
Β· Loose coupling = Low dependency between components (better).
Β· Low coupling improves code flexibility, modularity, and testability.
β
Example of Loose Coupling (Good Practice)
Using interfaces to decouple dependencies:
````java
interface Payment {
void pay(double amount);
}
class CreditCardPayment implements Payment {
public void pay(double amount) {
System.out.println("Paid $" + amount + " via Credit Card");
}
}
class Order {
private Payment payment;
public Order(Payment payment) {
this.payment = payment; // Dependency Injection
}
public void processOrder(double amount) {
payment.pay(amount);
}
}
public class Main {
public static void main(String[] args) {
Payment payment = new CreditCardPayment();
Order order = new Order(payment);
order.processOrder(100.0);
}
}
β
Why is this loose coupling?
Β· The Order class depends on the Payment interface, not a concrete class.
Β· Easy to change the payment method (e.g., PayPal, BankTransfer) without modifying Order.
β Example of Tight Coupling (Bad Practice)
Directly hardcoding dependencies:
````java
class Order {
private CreditCardPayment payment = new CreditCardPayment(); // Hard dependency
public void processOrder(double amount) {
payment.pay(amount);
}
}
class CreditCardPayment {
public void pay(double amount) {
System.out.println("Paid $" + amount + " via Credit Card");
}
}
β Why is this tight coupling?
Β· Directly creates a CreditCardPayment object.
Β· Hard to change payment logic without modifying the Order class.
Β· Reduces code flexibility and increases maintenance complexity.
π 3. Key Differences: Cohesion vs. Coupling
Feature | Cohesion | Coupling
-----------------------------------------------------------------------------------------------------------------------------
Definition | How focused a class/module is on a single task. | Degree of dependency between classes/modules.
Goal | Ensure a single responsibility for each component. | Minimize dependencies between components.
High is Better? | β
Yes β High cohesion means better code clarity. | β
No β Prefer loose (low) coupling for flexibility.
Example of Good Practice | A Student class only manages student data. | Using an interface for payment methods.
Impact on Maintenance | Easier to understand and modify. | Easier to replace or extend functionality.
Testability | Easier to unit-test (isolated logic). | Easier to mock dependencies during testing.
Violation | A class doing too many things. | Directly instantiating dependencies.
π 4. How to Achieve High Cohesion & Low Coupling
β
Best Practices:
1. Follow SOLID Principles:
o Single Responsibility Principle (SRP) for cohesion.
o Dependency Inversion Principle (DIP) for loose coupling.
2. Use Interfaces & Abstract Classes:
o Depend on abstractions, not concrete implementations.
3. Apply Dependency Injection (DI):
o Pass dependencies through constructors or methods.
4. Separate Concerns:
o Keep business logic, presentation, and data access independent.
5. Modular Design:
o Divide your system into small, independent modules.
π 5. Example Combining Both:
High Cohesion + Loose Coupling Example
````java
// High Cohesion: Interface only handles payment
interface Payment {
void pay(double amount);
}
// High Cohesion: Each class handles a specific payment method
class PayPalPayment implements Payment {
public void pay(double amount) {
System.out.println("Paid $" + amount + " via PayPal");
}
}
// Low Coupling: Order depends on abstraction (Payment)
class Order {
private Payment payment;
public Order(Payment payment) {
this.payment = payment;
}
public void processOrder(double amount) {
payment.pay(amount);
}
}
public class Main {
public static void main(String[] args) {
Payment payment = new PayPalPayment();
Order order = new Order(payment);
order.processOrder(150.0);
}
}
π 6. Summary
Concept Good Practice Bad Practice
---------------------------------------------------------------------------------------------------------
Cohesion One task per class (High Cohesion) Multiple responsibilities (Low Cohesion)
Coupling Interface-based dependencies (Loose Coupling) Direct class references (Tight Coupling)
10 - Heap and Stack means and differences ?
β
Heap vs. Stack in Java (Memory Management)
In Java, memory is divided into Heap and Stack, each serving different purposes for storing objects, variables, and method calls.
π 1. What is Stack Memory?
Stack memory is used for:
Β· Method execution (local variables, method calls, parameters).
Β· Primitive data types (e.g., int, char, boolean).
Β· References to objects stored in the Heap.
β
Key Characteristics of Stack Memory:
Β· Fast access (LIFO β Last In, First Out).
Β· Stores method calls and local variables.
Β· Automatically managed (cleared when methods exit).
Β· Thread-safe (each thread has its own stack).
β
Example of Stack Memory in Java:
````java
public class StackExample {
public static void main(String[] args) {
int a = 10; // Stored in Stack (primitive)
int b = add(a, 5); // Method call adds a new frame to Stack
System.out.println(b); // Output: 15
}
public static int add(int x, int y) {
return x + y; // x and y are local variables (Stack)
}
}
Memory Layout:
1. Main Method Stack Frame:
o a = 10
o Method call to add(10, 5)
2. Add Method Stack Frame:
o x = 10, y = 5
o Returns 15 and stack frame is removed.
π 2. What is Heap Memory?
Heap memory is used for:
Β· Objects and instance variables.
Β· Objects created with new keyword.
Β· Garbage collection (GC) manages this area.
β
Key Characteristics of Heap Memory:
Β· Larger and slower than Stack.
Β· Stores objects and their instance variables.
Β· Shared across threads (not thread-safe by default).
Β· Requires Garbage Collection to free unused objects.
β
Example of Heap Memory in Java:
````java
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
public class HeapExample {
public static void main(String[] args) {
Person p = new Person("Alice"); // Object in Heap, 'p' in Stack
System.out.println(p.name); // Output: Alice
}
}
Memory Layout:
1. Stack Memory:
o p (Reference to the Person object).
2. Heap Memory:
o Person object with name = "Alice".
When the program ends, the Garbage Collector clears the Person object.
π 3. Key Differences: Heap vs. Stack Memory
Feature Stack Memory Heap Memory
----------------------------------------------------------------------------------------------------
Purpose Stores method calls, local variables Stores objects and instance variables
Storage Type Primitive types & object references Objects (created using new keyword)
Access Speed Faster (LIFO structure) Slower (Dynamic memory allocation)
Memory Size Smaller (Limited to method scope) Larger (More memory for long-living objects)
Lifetime Temporary (Removed after method call) Persistent (Exists until GC removes it)
Management Automatically by JVM (when method exits) Garbage Collection (by JVM)
Thread Safety Thread-safe (Each thread has its own stack) Not thread-safe (Shared across threads)
Example Variables Local variables, method parameters Objects created with new keyword
π 4. How Stack and Heap Work Together
1. Objects are always stored in the Heap.
2. References to these objects are stored in the Stack.
3. Primitives (like int, float, etc.) are stored directly in the Stack.
β
Example of Stack and Heap Interaction:
````java
class Car {
String model;
Car(String model) {
this.model = model;
}
}
public class MemoryExample {
public static void main(String[] args) {
int x = 100; // Stored in Stack (primitive)
Car car = new Car("BMW"); // car reference: Stack, object: Heap
System.out.println(car.model); // Output: BMW
}
}
Memory Breakdown:
1. Stack Memory:
o x = 100 (primitive)
o car (reference to the Car object in Heap)
2. Heap Memory:
o Car object with model = "BMW"
π 5. Common Issues Related to Stack and Heap
Issue Cause Solution
------------------------------------------------------------------------------------------------------------------
Stack Overflow Too many nested method calls (infinite recursion). Avoid deep recursion, use loops if possible.
Out of Memory (Heap) Large objects or memory leaks. Use Garbage Collection and manage memory wisely.
Memory Leak Unreferenced objects not cleaned by GC. Nullify unused references and use Weak References.
π 6. When to Use Stack vs. Heap
Scenario Use Stack Use Heap
--------------------------------------------------------------------------------------------------------
Method Execution Local variables, method parameters Objects and instance variables
Temporary Data Small, short-lived data Large, persistent data
Performance-Critical Fast execution (LIFO structure) For long-lasting objects (GC handles cleanup)
Multithreading Thread-specific storage (safe) Shared data across threads (requires synchronization)
π 7. Example of Stack vs. Heap in Recursion
β
Recursive Method (Stack Usage):
````java
public class RecursionExample {
public static void main(String[] args) {
int result = factorial(5);
System.out.println("Factorial: " + result);
}
public static int factorial(int n) {
if (n == 1) return 1; // Base case
return n * factorial(n - 1); // Recursive call (new stack frame)
}
}
Stack Behavior: Each recursive call creates a new stack frame. Deep recursion can cause a StackOverflowError.
π 8. Summary
Feature Stack Memory Heap Memory
-----------------------------------------------------------------------------------------------------
Speed Fast (LIFO β Last In, First Out) Slower (Dynamic allocation)
Storage Local variables, method calls, references Objects, instance variables
Lifetime Until method execution ends Until Garbage Collection (GC) cleans up
Management Automatically managed by JVM Managed by Garbage Collector
Thread Safety Each thread has its own stack (thread-safe) Shared between threads (not thread-safe)
Common Errors StackOverflowError (deep recursion) OutOfMemoryError (large object usage)
11 β Exception means ? Type of Exceptions ?
β
What is an Exception in Java?
An exception in Java is an unexpected event or error that occurs during the execution of a program, disrupting the normal flow of instructions.
Java provides a robust exception-handling mechanism to deal with runtime errors and maintain program stability using the try, catch, throw, throws, and finally keywords.
π 1. Why Do Exceptions Occur?
Exceptions typically occur due to:
Β· Invalid Input (e.g., dividing by zero).
Β· Accessing Null Objects (e.g., NullPointerException).
Β· File or Network Errors (e.g., IOException).
Β· Array Out-of-Bounds (e.g., ArrayIndexOutOfBoundsException).
π 2. Types of Exceptions in Java
Exceptions in Java are divided into checked, unchecked, and errors.
pgsql
Throwable (Superclass of all exceptions)
βββ Exception (Recoverable Errors)
β βββ Checked Exceptions (Compile-Time)
β βββ Unchecked Exceptions (Run-Time)
βββ Error (Unrecoverable, System-Level)
π A. Checked Exceptions (Compile-Time Exceptions)
Checked exceptions are checked at compile-timeβif not handled, the code will not compile. They represent anticipated issues that a program should handle.
β
Examples of Checked Exceptions:
Exception When It Occurs
-------------------------------------------------------------
IOException Input/output failures (e.g., file not found).
SQLException Database access issues.
FileNotFoundException File cannot be located.
ClassNotFoundException Class not found during loading.
InterruptedException Thread is interrupted.
β
Example Code (Checked Exception Handling)
````java
import java.io.*;
public class CheckedExceptionExample {
public static void main(String[] args) {
try {
FileReader file = new FileReader("file.txt");
System.out.println("File opened successfully.");
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
}
}
}
π B. Unchecked Exceptions (Run-Time Exceptions)
Unchecked exceptions occur at runtime and are not checked during compilation. They usually stem from programming mistakes.
β
Examples of Unchecked Exceptions:
Exception When It Occurs
------------------------------------------------------------------
NullPointerException Accessing methods/fields of null.
ArrayIndexOutOfBoundsException Accessing invalid array indices.
ArithmeticException Dividing by zero.
IllegalArgumentException Passing invalid arguments.
ClassCastException Invalid object typecasting.
β
Example Code (Unchecked Exception Handling)
````java
public class UncheckedExceptionExample {
public static void main(String[] args) {
try {
int result = 10 / 0; // ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero!");
}
}
}
π C. Errors (System-Level Issues)
Errors are serious problems beyond the application`s control. Usually caused by JVM or system failures.
β
Examples of Errors:
Error Type When It Occurs
------------------------------------------------------------------------
StackOverflowError Infinite recursion or deep method calls.
OutOfMemoryError Insufficient memory (heap overflow).
VirtualMachineError Critical JVM error.
NoClassDefFoundError Class not found at runtime.
β
Example Code (Error Demonstration)
````java
public class StackOverflowErrorExample {
public static void recursiveMethod() {
recursiveMethod(); // Infinite recursion
}
public static void main(String[] args) {
recursiveMethod();
}
}
π¨ Avoid handling errors directlyβfocus on fixing the root cause.
π 3. How to Handle Exceptions in Java
Java provides five main exception-handling keywords:
1. try: Defines a block where exceptions may occur.
2. catch: Handles the specific exception.
3. finally: Executes always (optional).
4. throw: Manually throw an exception.
5. throws: Declare exceptions a method can throw.
β
Example of Exception Handling
````java
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
int num = Integer.parseInt("abc"); // NumberFormatException
} catch (NumberFormatException e) {
System.out.println("Invalid number format: " + e.getMessage());
} finally {
System.out.println("Program completed.");
}
}
}
Output:
Invalid number format: For input string: "abc"
Program completed.
π 4. Custom (User-Defined) Exceptions
You can create your own exception by extending the Exception class.
β
Example of a Custom Exception
````java
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
public class CustomExceptionExample {
public static void validateAge(int age) throws InvalidAgeException {
if (age < 18) {
throw new InvalidAgeException("Age must be 18 or above.");
}
}
public static void main(String[] args) {
try {
validateAge(16);
} catch (InvalidAgeException e) {
System.out.println("Caught: " + e.getMessage());
}
}
}
π 5. When to Use Checked vs. Unchecked Exceptions
Aspect Checked Exceptions Unchecked Exceptions
------------------------------------------------------------------------------------------------------------------------
When to Use For expected issues (I/O, database). For programming mistakes (null pointers, invalid array access).
Handling Mandatory (use try-catch or throws). Optional (can be left unhandled).
Examples IOException, SQLException. NullPointerException, ArithmeticException.
Performance Slightly slower (due to checking). Faster (no compile-time checking).
π 6. Common Exceptions in Java
Exception When It Occurs
-----------------------------------------------------------------------------
NullPointerException Accessing methods/fields of a null object.
ArrayIndexOutOfBoundsException Accessing an invalid index in an array.
ArithmeticException Dividing by zero.
IOException Input/output operations fail (file issues).
ClassCastException Invalid object typecasting.
IllegalArgumentException Passing illegal arguments to a method.
π 7. Best Practices for Exception Handling
1. Use Specific Exceptions β Catch specific exceptions rather than a generic Exception.
2. Log Exceptions β Always log exceptions for debugging (e.printStackTrace() or logging frameworks).
3. Never Suppress Exceptions β Avoid empty catch blocks.
4. Custom Exceptions β Create custom exceptions for domain-specific errors.
5. Resource Management β Use try-with-resources for closing resources (like files, sockets).
12 β How to summarize βclean codeβ as short as possible ?
Clean Code means writing simple, readable, and maintainable code by following best practices. Key principles include:
1. Readable β Use clear, meaningful names and keep functions small.
2. Simple β Write straightforward logic and avoid over-engineering.
3. Modular β Break code into small, reusable components.
4. Consistent β Follow a uniform coding style and structure.
5. Testable β Ensure code is easy to test with automated tests.
6. Efficient β Prioritize performance without sacrificing clarity.
7. Self-Documenting β Write code that explains itself without needing extra comments.
13 - What is the method of hiding in Java ?
In Java, method hiding occurs when a subclass defines a static method with the same name and signature as a static method in its superclass. This hides the superclass's method rather than overriding it.
β
Key Points About Method Hiding:
1. Static methods are hidden, not overridden.
2. Method calls are resolved at compile time (not runtime).
3. Instance methods cannot hide static methods.
4. Access is based on the reference type, not the object type.
π Example of Method Hiding in Java:
````java
class Parent {
static void display() {
System.out.println("Parent static method");
}
}
class Child extends Parent {
static void display() { // Method Hiding
System.out.println("Child static method");
}
}
public class MethodHidingExample {
public static void main(String[] args) {
Parent obj1 = new Child();
obj1.display(); // Calls Parent's display method (hiding)
Child obj2 = new Child();
obj2.display(); // Calls Child's display method
}
}
π§ Output:
sql
KopyalaDΓΌzenle
Parent static method
Child static method
π Method Hiding vs. Method Overriding:
Feature Method Hiding Method Overriding
---------------------------------------------------------------------------------
Method Type Only static methods Only instance methods
Binding Compile-time (reference type) Runtime (object type β polymorphism)
Keyword static (both parent and child) @Override (optional annotation)
Inheritance Hides the parent method Overrides the parent method
Access Based on reference type Based on object type
14 - What is the difference between abstraction and polymorphism in Java ?
β
Difference Between Abstraction and Polymorphism in Java
Feature Abstraction Polymorphism
-----------------------------------------------------------------------------------------------------------------------------------------
Definition Hiding implementation details and showing only essential features. The ability of a method or object to take many forms.
Purpose Focuses on what a class does (not how it does it). Allows different implementations of the same interface or method.
Types β
Abstract Class β
Compile-time (Method Overloading)
β
Interface β
Runtime (Method Overriding)
How It Works By abstract classes and interfaces. By overloading (compile-time) or overriding (runtime).
Implementation Defined using abstract keyword or interface. Achieved using method overloading or method overriding.
Example Use Case Defining a template where subclasses must provide specific behavior. Allowing different objects to respond differently to the same method call.
Key Benefit Provides a blueprint for other classes and promotes loose coupling. Provides flexibility and supports code reusability through dynamic behavior.
π Example of Abstraction:
Using an abstract class to define a general shape:
Java
abstract class Shape {
abstract void draw(); // Abstract method (no body)
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle.");
}
}
public class Main {
public static void main(String[] args) {
Shape shape = new Circle();
shape.draw(); // Output: Drawing a circle.
}
}
π Example of Polymorphism:
Using method overriding (runtime polymorphism):
java
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // Polymorphism
animal.sound(); // Output: Dog barks
}
}
π Quick Summary:
Β· Abstraction: Defines a contract for what should be done.
Β· Polymorphism: Allows objects to behave differently while sharing a common interface.