java important concepts

Java important concepts : For developers preparing for interview.

Java interview preparation should focus on core concepts like OOP principles (encapsulation, inheritance, polymorphism, abstraction), data types, control structures, exception handling, and collections. Understand JVM internals, memory management, garbage collection, and Java 8+ features like streams, lambdas, and functional interfaces. Be confident with multithreading, synchronization, and concurrency utilities. Master important APIs (Collections, IO/NIO, JDBC), design patterns, and object-oriented design principles (SOLID). For backend roles, understand Java frameworks like Spring (Core, Boot, MVC, Data, Security), Hibernate, RESTful services, and testing tools like JUnit. Knowledge of Maven/Gradle, Git, and databases (SQL/ORM) is also crucial for real-world development.

Java 8 Features

  1. Support for Functional Programming
    • Introduced Lambda Expressions and Functional Interfaces (e.g., Predicate, Consumer, Function, etc.)
  2. Nashorn JavaScript Engine
    • A new lightweight JavaScript runtime was introduced to execute JavaScript code on the JVM.
  3. Calling JavaScript from Java
    • Java can now invoke JavaScript code using ScriptEngine API and Nashorn engine.
  4. New Date-Time API
    • java.time package introduced (based on Joda-Time), offering classes like LocalDate, LocalTime, ZonedDateTime, etc.
  5. Streams API
    • Enables functional-style operations on collections using methods like filter(), map(), reduce().

JDK, JRE, and JVM

JDK (Java Development Kit)

  • A software development kit used to develop Java applications.
  • Includes:
    • JRE (Java Runtime Environment)
    • Compiler (javac)
    • Archiver (jar)
    • Document Generator (javadoc)
    • Debugger
    • Interpreter/Loader
  • It is the full-featured software kit required to develop and compile Java programs.

JRE (Java Runtime Environment)

  • Provides the runtime environment to execute Java bytecode.
  • Includes:
    • JVM (Java Virtual Machine)
    • Core libraries
    • Support files
  • It does not include development tools like compiler or debugger.
  • Used to run Java applications, not develop them.

JVM (Java Virtual Machine)

  • An abstract machine that enables your computer to run Java programs.
  • Responsibilities:
    • Converts bytecode into machine code (platform-dependent).
    • Handles memory management, garbage collection, etc.
  • Three key components of JVM:
    1. Specification – Defines how the JVM should work.
    2. Implementation – Actual software (like HotSpot, OpenJ9).
    3. Runtime Instance – Created each time a Java program is run.

Java Compiler

🔧 JIT (Just-In-Time Compiler)

  • Part of JVM that improves performance.
  • Compiles bytecode into native machine code at runtime.
  • First run compiles all, then only compiles modified code subsequently.
  • Enhances performance by avoiding repeated compilation.

Java Data Types

🔹 Primitive Data Types (8 total)

  • byte, short, int, long
  • float, double
  • char
  • boolean

🔹 Reference Data Types

  • Objects, Arrays, Strings, Interfaces, etc.

🔸 Note: Java is not 100% object-oriented because primitive types are not objects.

Methods in the Object Class

These are the common methods inherited by all Java classes:

  • equals()
  • hashCode()
  • toString()
  • getClass()
  • wait()
  • notify()
  • notifyAll()

Packages in Java

📦 What is a Package?

  • A namespace that organizes a set of related classes and interfaces.

📦 Advantages:

  1. Organizes classes logically.
  2. Avoids naming conflicts.
  3. Provides access control and protection.

📦 How to Compile Java Packages:

javac -d <Destination_Folder> FileName.java

Example:

javac -d . Animal.java (same dir)
javac -d animals Animal.java (animals dir)

(Compiles and places class file in correct sub-package structure relative to the current directory)


List vs Set in Java

FeatureListSet
DuplicatesAllows duplicate elementsOnly stores unique elements
OrderMaintains insertion order (e.g., ArrayList, LinkedList)Does not guarantee order (except LinkedHashSet or TreeSet)
Example ImplementationsArrayList, LinkedList, VectorHashSet, LinkedHashSet, TreeSet

Java HashSet Example

javaCopyEditimport java.util.*;

class TestCollection {
    public static void main(String args[]) {
        // Creating HashSet and adding elements
        HashSet<String> set = new HashSet<String>();
        
        // For list comparison, uncomment the next line
        // List<String> al = new ArrayList<String>();

        set.add("Ravi");
        set.add("Vijay");
        set.add("Ravi"); // Duplicate, will not be added
        set.add("Ajay");

        // Traversing elements
        Iterator<String> itr = set.iterator();
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
    }
}

Output (order may vary due to hashing):

nginxCopyEditRavi
Vijay
Ajay

HashMap Overview

  • HashMap stores data in key-value pairs:
    HashMap<String, Integer> map = new HashMap<>();
    map.put("a", 1); map.put("b", 2);
  • Each key must be unique, but values can be duplicated.
  • Not ordered; use LinkedHashMap or TreeMap for order-specific behavior.

HashSet vs HashMap

FeatureHashSetHashMap
PurposeStore unique valuesStore key-value pairs
StructureSet<E>Map<K, V>
Example{1, 2, 3}{a → 1, b → 2}
UnderlyingUses HashMap<K, Object> internallyBuilt-in hash-based map

this Keyword in Java

  • Refers to the current class object.
  • Common uses:
    • Resolve naming conflicts between class fields and constructor parameters.
    • Invoke current class methods or constructors.
    • Pass current object as an argument.

Optional in Java 8

  • A container object which may or may not contain a non-null value.
  • Used to avoid NullPointerException.

Example & Use Case:

import java.util.Optional;

public class JavaTester {
public static void main(String[] args) {
JavaTester javaTester = new JavaTester();

Integer value1 = null;
Integer value2 = 10;

Optional<Integer> a = Optional.ofNullable(value1); // can be null
Optional<Integer> b = Optional.of(value2); // must not be null

System.out.println("Sum is: " + javaTester.sum(a, b));
}

public Integer sum(Optional<Integer> a, Optional<Integer> b) {
System.out.println("First parameter is present: " + a.isPresent());
System.out.println("Second parameter is present: " + b.isPresent());

// Optional.orElse - return value if present, else return default
Integer value1 = a.orElse(0);

// Optional.get - get the value (must be present)
Integer value2 = b.get();

return value1 + value2;
}
}

Output:

First parameter is present: false
Second parameter is present: true
Sum is: 10

✅ JavaBeans

🔹 What is a JavaBean?

JavaBeans are Java classes that follow a specific coding convention and are used to encapsulate multiple objects into a single object (the bean).

🔹 JavaBean Conventions:

  1. All properties are private (use public getters/setters).
  2. Public no-argument constructor (required for instantiation).
  3. Implements java.io.Serializable (to persist object state).
  4. Provides public getter and setter methods for accessing properties.
public class Employee implements Serializable {
private String name;
private int id;

public Employee() {} // No-arg constructor

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}
}

✅ Spring @Bean Annotation

  • @Bean is used in Spring Framework to declare a method as returning a bean object managed by Spring’s IoC container.
  • Typically used inside @Configuration classes.
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}

A Spring Bean is any object that is instantiated, assembled, and managed by the Spring IoC container. These are core components in Spring applications and are created via Dependency Injection (DI).

equals() and hashCode() Methods

equals(Object obj)

  • Compares the current object with another for logical equality.
  • By default, uses reference comparison (same memory address).

hashCode()

  • Returns an integer representation of the object’s memory address.
  • Should be overridden if equals() is overridden.
  • Used in collections like HashMap, HashSet.

✅ Serialization in Java

🔹 What is Serialization?

Serialization is the process of converting a Java object into a byte stream, so it can be:

  • Persisted to a file or database
  • Transmitted over a network
  • Stored in memory (like caching)
  • Sent via RMI (Remote Method Invocation)

🔹 What is Deserialization?

Deserialization is the reverse process of converting a byte stream back into a copy of the original object.

🔹 Why Use Serialization?

Serialization is mainly used for:

  • Saving object state for later use
  • Sending objects between JVMs (e.g., in distributed systems or remote communication)
  • Caching or logging complex data
  • Working with frameworks like Hibernate, JPA, Spring, etc.

🔹 How to Make a Class Serializable?

✅ Requirements:

  1. The class must implement java.io.Serializable interface.
  2. All fields should be serializable (i.e., either primitives or Serializable objects).
  3. Mark non-serializable fields with the transient keyword (optional).
  4. Provide a no-argument constructor (not mandatory but recommended).

✅ Simple Example:

import java.io.*;
class Student implements Serializable {
private static final long serialVersionUID = 1L; // recommended
int id;
String name;

public Student(int id, String name) {
this.id = id;
this.name = name;
}
}

🔹 Writing an Object to File (Serialization)

import java.io.*;

public class SerializeExample {
public static void main(String[] args) {
Student s = new Student(101, "Ravi");

try {
FileOutputStream fout = new FileOutputStream("student.ser");
ObjectOutputStream out = new ObjectOutputStream(fout);
out.writeObject(s);
out.close();
fout.close();
System.out.println("Object Serialized Successfully");
} catch (Exception e) {
e.printStackTrace();
}
}
}

🔹 Reading an Object from File (Deserialization)

import java.io.*;

public class DeserializeExample {
public static void main(String[] args) {
try {
FileInputStream fin = new FileInputStream("student.ser");
ObjectInputStream in = new ObjectInputStream(fin);
Student s = (Student) in.readObject();
in.close();
fin.close();

System.out.println("Deserialized Student:");
System.out.println("ID: " + s.id);
System.out.println("Name: " + s.name);
} catch (Exception e) {
e.printStackTrace();
}
}
}

🔹 serialVersionUID – What and Why?

  • A unique ID to verify the compatibility of sender and receiver classes during deserialization.
  • If not specified and class changes, you may get InvalidClassException.
private static final long serialVersionUID = 1L;

🔹 transient Keyword

If you don’t want a field to be serialized:

transient String password;

Transient fields are ignored during serialization.

🔹 static Fields and Serialization

  • Static fields are not serialized, because they belong to the class, not to the object.
  • Their values are not part of the object’s state.

🔹 Inheritance and Serialization

  • If a superclass implements Serializable, all its subclasses are automatically serializable.
  • If a subclass implements Serializable, the superclass does not need to be Serializable unless it contains fields you want to serialize.

🔹 Custom Serialization: writeObject() and readObject()

You can customize the serialization process:

private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // default behavior
// write custom logic
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // default behavior
// read custom logic
}

🔹 Externalizable Interface

An advanced alternative to Serializable:

class Employee implements Externalizable {
int id;
String name;

public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(id);
out.writeObject(name);
}

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
id = in.readInt();
name = (String) in.readObject();
}
}

You have full control over serialization behavior with Externalizable.

✅ Summary: Key Points for Interview

FeatureSerializable
InterfaceMarker Interface (no methods)
ControlJVM handles serialization
VersioningUse serialVersionUID
transientUsed to exclude fields
staticNot serialized
Custom logicUse writeObject / readObject
AlternativeExternalizable (full control

✅ Scanner Class (Reading Input)

import java.util.Scanner;

public class InputExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

String myString = scanner.next();
int myInt = scanner.nextInt();

scanner.close();

System.out.println("myString is: " + myString);
System.out.println("myInt is: " + myInt);
}
}

✅ Access Control Modifiers

ModifierVisibility
defaultWithin the same package (no keyword)
privateWithin the same class only
publicEverywhere
protectedSame package + subclasses

✅ Non-Access Modifiers

ModifierUsage
staticBelongs to the class, not instance
finalConstant (var), cannot be overridden (method), no inheritance (class)
abstractDeclares abstract methods/classes
transientPrevents fields from being serialized
synchronizedPrevents concurrent access by multiple threads
volatileEnsures value is always read from main memory (not cached)

✅ Process vs Thread

ConceptProcessThread
DefinitionRunning instance of a programA lightweight sub-process within a process
MemoryEach process has separate memoryThreads share memory of the process
OverheadHighLow
CommunicationDifficultEasy (shared memory)

✅ Wrapper Classes & Autoboxing

  • Wrapper classes convert primitives into objects:
    • intInteger, floatFloat, etc.
int i = 10;

// Boxing
Integer bx = new Integer(i);

// Unboxing
int unbx = bx.intValue();

// Autoboxing
Integer abx = i;

// Auto-unboxing
int j = abx;

final, finally, finalize

KeywordPurpose
finalUsed for constants, final methods (can’t override), final classes (can’t extend)
finallyBlock that always executes after try-catch (even if exception occurs)
finalize()Method called by Garbage Collector before destroying object (deprecated in recent versions)
// final
final int x = 5;

// finally
try {
// code
} catch (Exception e) {
// handler
} finally {
System.out.println("Always runs.");
}

// finalize (not recommended)
@Override
protected void finalize() throws Throwable {
System.out.println("Object is being garbage collected");
}

System.gc() can suggest garbage collection, but it’s not guaranteed.

✅ String vs StringBuffer vs StringBuilder

FeatureStringStringBufferStringBuilder
MutabilityImmutableMutableMutable
Thread SafetyNot thread-safeThread-safe (synchronized)Not thread-safe
PerformanceLow (creates new obj)Slower (due to synchronization)Faster (no synchronization overhead)
Reverse Method❌ No reverse()✅ Has reverse()✅ Has reverse()

Code Example:

String str = new String("hi");
StringBuffer buffer = new StringBuffer("hi");
StringBuilder builder = new StringBuilder("hi");

System.out.println(str + " hello"); // Output: hi hello
System.out.println(buffer + " hello"); // Output: hi hello
System.out.println(builder + " hello"); // Output: hi hello

✅ Heap vs Stack Memory in Java

FeatureStack MemoryHeap Memory
ScopeLocal variables, method callsAll objects, class instances
LifetimeUntil method/thread execution endsUntil object is garbage collected
AccessOne thread onlyShared across all threads
SpeedFast (LIFO based)Slower than stack
Memory ManagementHandled via LIFO structureManaged by Garbage Collector
UsageStores primitive values and references to objectsStores actual objects

✅ Example:

Point p = new Point(1, 2);
// 'p' is in stack (reference), object (Point) is in heap

Stack memory is a part of JVM memory used to store:

1. Local Method Data (Local Variables)

  • Any variable declared inside a method.
  • For example:
void display() {
int a = 10; // 'a' is stored in stack
String msg = "Hi"; // 'msg' reference is in stack
}

Here:

  • a (primitive) is directly in the stack.
  • msg (object reference) is in the stack, but the actual "Hi" object is stored in the heap.

2. Function Calls (Method Call Stack Frames)

  • Each time a method is called, a new block (stack frame) is pushed onto the call stack.
  • When the method finishes, that frame is popped off.
void methodA() {
methodB();
}

void methodB() {
// execution here
}
  • Call to methodA() → stack frame created
  • methodA() calls methodB() → new stack frame pushed
  • Once methodB() ends → its frame is removed

3. Reference Variables

  • Stack holds the references (addresses) to objects that are stored in the heap.
Point p = new Point(1, 2);
  • p is a reference variable — stored in stack
  • new Point(1, 2) creates an object — stored in heap

✅ ArrayList vs Vector

FeatureArrayListVector
SynchronizationNot synchronized✅ Synchronized (thread-safe)
PerformanceFaster (no locking overhead)Slower (due to locking)
Growth RateIncreases size by 50%Doubles the size (100%)
TraversalOnly IteratorIterator and Enumeration
Legacy StatusPart of Java Collections FrameworkLegacy class from earlier Java

🔸 Using Iterator with both ArrayList and Vector

import java.util.*;

public class ListIteratorExample {
public static void main(String[] args) {
// Using ArrayList
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add("Cherry");

System.out.println("Iterating ArrayList using Iterator:");
Iterator<String> itr1 = arrayList.iterator();
while (itr1.hasNext()) {
System.out.println(itr1.next());
}

// Using Vector
Vector<String> vector = new Vector<>();
vector.add("Dog");
vector.add("Elephant");
vector.add("Fox");

System.out.println("\nIterating Vector using Iterator:");
Iterator<String> itr2 = vector.iterator();
while (itr2.hasNext()) {
System.out.println(itr2.next());
}
}
}

🔸 Using Enumeration (Only for Vector)

import java.util.*;

public class VectorEnumerationExample {
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("Red");
vector.add("Green");
vector.add("Blue");

System.out.println("Iterating Vector using Enumeration:");
Enumeration<String> en = vector.elements();
while (en.hasMoreElements()) {
System.out.println(en.nextElement());
}
}
}

✅ HashMap vs Hashtable

FeatureHashMapHashtable
Synchronization❌ Not synchronized (not thread-safe)✅ Synchronized (thread-safe)
PerformanceFasterSlower
Null Keys/ValuesAllows 1 null key and multiple null values❌ No null keys or values allowed
TraversalIteratorIterator and Enumeration
Parent ClassAbstractMapDictionary (legacy)
Thread-Safe AlternativeUse Collections.synchronizedMap()Always synchronized
Modern UsePreferred in single-threaded appsMostly avoided in new codebases

✅ Quick Notes for Interviews

  • String: Immutable, good for constant values.
  • StringBuffer: Thread-safe, use in multithreaded apps.
  • StringBuilder: Fastest for string manipulation in single-threaded apps.
  • Stack: Stores local method data, function calls, reference variables.
  • Heap: Stores all objects and class-level fields.
  • ArrayList: Use when thread safety is not a concern.
  • Vector: Only if you need synchronization (legacy alternative).
  • HashMap: Best for performance; use wrapper if thread safety is needed.
  • Hashtable: Rarely used now; replaced by concurrent collections.

✅ Three Different Synchronized Map Implementations in Java

1️⃣ Hashtable – Legacy Synchronized Map

🔹 Description:

  • An early thread-safe Map implementation.
  • All methods are synchronized, making it inherently thread-safe.
  • Extends the obsolete Dictionary class.

🔹 Example:

Map<String, String> map = new Hashtable<>();
map.put("A", "Apple");

🔹 Pros:

  • Thread-safe by design.
  • Easy to use for single-threaded or legacy systems.

🔹 Cons:

  • Synchronized at method level, leading to performance bottlenecks.
  • Slower under concurrent access.
  • Considered legacy — discouraged in modern applications.

Use When:

  • Working with very old or legacy code.
  • Need basic synchronization with minimal effort.

2️⃣ Collections.synchronizedMap(Map<K,V>) – Synchronized Wrapper

🔹 Description:

  • Wraps any standard Map (like HashMap, TreeMap) to make it thread-safe.
  • Synchronization is applied to every method call.

🔹 Example:

Map<String, String> map = new HashMap<>();
Map<String, String> syncMap = Collections.synchronizedMap(map);

// Required for thread-safe iteration
synchronized (syncMap) {
for (Map.Entry<String, String> entry : syncMap.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}

🔹 Pros:

  • Can convert any map into a synchronized one.
  • Useful for basic thread-safety requirements.

🔹 Cons:

  • Blocks the entire map for each operation → slower.
  • Must manually synchronize during iteration.
  • Not designed for high-concurrency environments.

Use When:

  • You need to ensure data consistency across threads.
  • You’re working with existing unsynchronized maps and need a quick fix for thread safety.

3️⃣ ConcurrentHashMap – High-Performance Thread-Safe Map

🔹 Description:

  • Part of java.util.concurrent package.
  • Allows concurrent access by multiple threads without blocking.
  • Uses bucket-level locking or lock-striping.

🔹 Example:

ConcurrentHashMap<String, String> cmap = new ConcurrentHashMap<>();
cmap.put("A", "Apple");

🔹 Pros:

  • Highly efficient in multi-threaded environments.
  • Allows concurrent read/write operations.
  • No need for external synchronization.

🔹 Cons:

  • Does not allow null keys or null values.
  • Slightly more complex than basic maps.

Use When:

  • Performance is critical and the application is highly concurrent.
  • Threads read/write frequently and simultaneously.

🧠 Summary Comparison Table

FeatureHashtableCollections.synchronizedMap()ConcurrentHashMap
Thread Safety✅ Yes✅ Yes✅ Yes
Synchronization LevelMethod-levelEntire MapBucket-level (fine-grained)
Performance❌ Low❌ Low to Medium✅ High
Allows null keys/values❌ No✅ Yes (if base map allows)❌ No
Iteration Safety✅ Yes❌ No (manual sync required)✅ Weakly consistent
Legacy StatusLegacy (Discouraged)IntermediatePreferred (Modern)
Ideal Use CaseOld codebasesLight concurrencyHeavy concurrency

Instead of locking the entire map (like Hashtable or Collections.synchronizedMap()), ConcurrentHashMap only locks a small portion (called a bucket) of the map during updates (like put(), remove()).

✅ This means multiple threads can operate on different buckets at the same time — improving performance and reducing contention.

🧠 Example Analogy:

Think of a bank with 10 counters (buckets).
If all customers had to go to a single counter (like in Hashtable), it creates a long queue.
But with one counter per customer type (like in ConcurrentHashMap), many customers can be served in parallel.

✅ String Comparison in Java

Java provides three main ways to compare strings:

🔹 1. == Operator

  • Compares object references, not actual values.
  • Returns true if both references point to the same object in memory.

✅ Example:

String s1 = new String("abc");
String s2 = new String("abc");

System.out.println(s1 == s2); // false (different objects)
System.out.println(s1.equals(s2)); // true (same value)

🧠 Use == only to compare reference identity, not content.

🔹 2. .equals() Method

  • Compares the contents (values) of the two strings.
  • Returns true if values are equal.

✅ Example:

String s1 = "abc";
String s2 = "abc";
System.out.println(s1.equals(s2)); // true

✅ Use this for value comparison.

🔹 3. .compareTo() Method

  • Lexicographically compares two strings.
  • Returns:
    • 0 → if strings are equal
    • > 0 → if first string is greater
    • < 0 → if first string is smaller

✅ Example:

String s1 = "Sachin";
String s2 = "Sachin";
String s3 = "Ratan";

System.out.println(s1.compareTo(s2)); // 0
System.out.println(s1.compareTo(s3)); // > 0 (positive)
System.out.println(s3.compareTo(s1)); // < 0 (negative)

✅ Case-Insensitive Comparison:

s1.equalsIgnoreCase(s2); // Ignores case

✅ Abstract Class vs Interface in Java

FeatureAbstract ClassInterface
Method TypesCan have abstract & non-abstract methodsOnly abstract methods (Java 8+ allows default, static)
Inheritance SupportSingle inheritance onlySupports multiple inheritance
VariablesCan have any type (static, non-static, final)Only public static final (constants)
Declarationabstract keywordinterface keyword
Extends / ImplementsExtended using extendsImplemented using implements
Can ExtendCan extend a class and implement interfacesCan only extend other interfaces
Access ModifiersCan be private, protected, etc.All methods are implicitly public
ConstructorsCan have constructors❌ Cannot have constructors

✅ Example – Abstract Class

abstract class Shape {
abstract double area();
}

class Circle extends Shape {
double radius;
Circle(double r) { radius = r; }
double area() { return Math.PI * radius * radius; }
}

✅ Example – Interface

interface Drawable {
void draw();
}

class Rectangle implements Drawable {
public void draw() {
System.out.println("Drawing rectangle");
}
}

Why default Methods Were Introduced

Prior to Java 8:

  • Interfaces could only declare methods (no implementations).
  • If a method needed to be added to an interface, all implementing classes had to override it, breaking backward compatibility.

From Java 8 onward:

  • You can provide default implementations inside interfaces.
  • This enables adding new methods to interfaces without breaking existing code.
  • Backward Compatibility: Add functionality to interfaces without breaking existing implementations.
  • Code Reuse: Avoid boilerplate in every implementing class.
  • Flexible Design: Classes can still override the default if needed.

❗ Important Notes

  • If a class implements two interfaces with the same default method, it must override the method to resolve conflict.

🧠 Summary:

UseWhen to Use
Abstract ClassWhen you need base class logic + abstraction.
InterfaceWhen you want to define only behavior/contract (especially for multiple classes).

✅ Java Concepts: Overriding, Overloading, Method Hiding, Servlets, Wrapper Classes

🔹 1. Method Overriding – Runtime Polymorphism

✅ Definition:

Overriding means defining a method in the subclass that already exists in the superclass with same signature.

  • Achieved via inheritance
  • Decided at runtime
  • Enables dynamic method dispatch

✅ Example:

class Car {
void run() {
System.out.println("Car is running");
}
}

class Test extends Car {
void run() {
System.out.println("Audi is running safely with 100km");
}

public static void main(String[] args) {
Car b = new Test(); // upcasting
b.run(); // Output: Audi is running safely with 100km
}
}

Upcasting is the process of assigning a subclass object to a superclass reference.

✅ It is automatic and safe, because every subclass object is-a superclass object.

To access subclass-specific methods, you must downcast:

Test d = (Dog) a; // Downcasting (explicit)
d.run();

🔹 2. Method Overloading – Compile-Time Polymorphism

✅ Definition:

Method overloading means defining multiple methods with same name but different parameter lists in the same class.

  • Decided at compile-time

✅ Example:

class Test {
static int add(int a, int b) {
return a + b;
}

static double add(double a, double b) {
return a + b;
}

public static void main(String args[]) {
System.out.println(Test.add(11, 11)); // 22
System.out.println(Test.add(12.3, 12.6)); // 24.9
}
}

🔹 3. Method Hiding

✅ Definition:

If a static method is defined in both superclass and subclass with the same signature, then the subclass method hides the superclass method — it’s not overriding.

Also:

  • private and final methods cannot be overridden
  • Trying to override a private method simply hides it

❌ Example (Fails due to private):

class Parent {
private void display() {
System.out.println("Parent method");
}
}

class Child extends Parent {
public void display() {
System.out.println("Child method");
}

public static void main(String[] args) {
Parent obj = new Child();
obj.display(); // ❌ Compile-time error: display() not visible in Parent
}
}

🔹 4. Servlets in Java

✅ What is a Servlet?

A Java class that runs on a web server and handles HTTP requests and responses.

✅ Servlet Lifecycle Methods:

StageMethod
Loading
InstantiationConstructor
Initializationinit()
Request handlingservice()
Destructiondestroy()

✅ Packages:

  • javax.servlet.*
  • javax.servlet.http.*

🔹 ServletConfig vs ServletContext

FeatureServletConfigServletContext
ScopeSpecific to one servletShared across entire application
PurposeServlet-specific configApplication-wide parameters

🔹 RequestDispatcher Interface

Used to forward or include another resource (JSP/HTML/Servlet).

RequestDispatcher rd = request.getRequestDispatcher("next.jsp");
rd.forward(request, response); // Forwards to another resource
rd.include(request, response); // Includes content of another resource

🔹 5. int vs Integer (Wrapper Classes)

FeatureintInteger (Wrapper Class)
TypePrimitiveObject (inherits from Object)
Stored asBinary valueObject wrapping primitive
Nullable❌ No✅ Yes
Method Access❌ No methods✅ Yes (e.g., parseInt())

✅ Example:

int i = Integer.parseInt("10"); // returns primitive int
Integer obj = Integer.valueOf(10); // returns Integer object

✅ Every primitive type has a wrapper class:
| Primitive | Wrapper |
|———–|————|
| int | Integer |
| char | Character|
| double | Double |
| float | Float |
| long | Long |
| byte | Byte |
| boolean | Boolean |
| short | Short |

🔹 6. Static Block vs Instance Block

TypeStatic BlockInstance Block
When CalledWhen class is loadedEvery time object is created
Syntaxstatic { ... }{ ... } (non-static block)
Use CaseInitialize static variablesCommon code for constructors

✅ Example:

class Hello {
static {
System.out.println("Static block"); // Executes once when class is loaded
}

{
System.out.println("Instance block"); // Executes each time object is created
}

public static void main(String[] args) {
Hello h1 = new Hello(); // triggers instance block
Hello h2 = new Hello(); // triggers instance block again
}
}

✅ Why Hibernate over JDBC?

FeatureJDBCHibernate
Boilerplate CodeRequired (manual query, connection, etc.)✅ Removes boilerplate via ORM
Object-Oriented Mapping❌ Manual mapping✅ Supports inheritance, association, collection mapping
TransactionsManual✅ Automatic transaction management
Caching❌ Not built-in✅ Built-in caching improves performance
Query LanguageSQL✅ HQL (Hibernate Query Language) — object-oriented
Data HandlingProcedural✅ Uses Java objects (POJOs)

✅ Java Collections Framework

➤ A Collection is a group of objects, represented as a single unit (like a list, set, or queue).

Key operations: add, remove, traverse, search, sort.

🔷 Hierarchy Overview:

Iterable
└── Collection
├── List
├── Set
└── Queue

✅ List Interface

  • Ordered, allows duplicates
  • Maintains insertion order

Implementations:

ClassSynchronizedInternalsUse-case
ArrayList❌ NoDynamic arrayFast random access, slow insert/delete
LinkedList❌ NoDoubly linked listFast insert/delete, slow access
Vector✅ YesDynamic arrayThread-safe but slower
Stack✅ YesExtends VectorLIFO operations
List<String> list1 = new ArrayList<>();
List<String> list2 = new LinkedList<>();
List<String> list3 = new Vector<>();
List<String> list4 = new Stack<>();

✅ Set Interface

  • Unordered, does not allow duplicates

Implementations:

ClassOrderedUnique ElementsNotes
HashSet❌ No✅ YesFast, no order, allows one null
LinkedHashSet✅ Yes✅ YesMaintains insertion order
TreeSet✅ Sorted✅ YesSorted (natural order), no nulls allowed
Set<String> set1 = new HashSet<>();
Set<String> set2 = new LinkedHashSet<>();
Set<String> set3 = new TreeSet<>();

✅ Queue Interface

  • FIFO (First-In-First-Out) structure

Implementations:

ClassOrderedNotes
PriorityQueue✅ YesElements processed by priority; no nulls
ArrayDeque✅ YesAllows add/remove from both ends; faster than Stack/ArrayList
Queue<String> q1 = new PriorityQueue<>();
Queue<String> q2 = new ArrayDeque<>();

✅ Iterator Interface

Used for traversing collections.

3 Methods:

hasNext()   // returns true if next element exists
next() // returns the next element
remove() // removes the current element (optional)

✅ List vs Set vs Queue

FeatureListSetQueue
Duplicates✅ Allowed❌ Not allowed✅ Allowed
Order✅ Maintained❌ Unordered (HashSet)✅ FIFO (Queue), LIFO (Stack)
Nulls Allowed✅ Yes (1+)✅ (max one in HashSet)❌ Not in PriorityQueue

✅ ArrayList vs LinkedList

FeatureArrayListLinkedList
InternalsDynamic arrayDoubly linked list
Access Speed✅ Fast (index-based)❌ Slow (linear traversal)
Insertion/Deletion❌ Slow (shift required)✅ Fast (just node pointers)
Acts AsOnly ListList + Queue
Nulls✅ Allowed✅ Allowed

List A = new ArrayList<>(); vs ArrayList A = new ArrayList<>();

DeclarationExplanation
List<String> A = new ArrayList<>();✅ Best practice — flexible, code to interface
ArrayList<String> A = new ArrayList<>();Tied to specific implementation — less flexible

✅ Internal Working of HashMap in Java

HashMap<String, Integer> hm = new HashMap<>();
hm.put("Run", 572);

🔹 Step-by-Step Working

1. HashCode Generation

  • Java calls key.hashCode() on "Run", which returns an integer hash code.
  • For example: "Run".hashCode() => 773737773

2. Index Calculation (Bucket Selection)

  • The HashMap uses the hash code to compute an index in the internal array (called buckets).
  • Formula: index = (n - 1) & hash Where n is the current capacity of the HashMap (default is 16), and & is bitwise AND. E.g.: 773737773 & (16 - 1) = index 5 (say)
hash = 773737773 = 0010 1111 1110 1001 1010 1011 1101 1101
mask =         15 = 0000 0000 0000 0000 0000 0000 0000 1111
---------------------------------------------------------
result =            0000 0000 0000 0000 0000 0000 0000 1101 = 13

bucketIndex = 773737773 & 15 = 13

3. Node Creation and Storage

  • The key-value pair ("Run"572) is stored at the computed bucket index.
  • Internally, it’s stored as an object like: Node(hash=773737773, key="Run", value=572, next=null)

4. Handling Collisions

If another key generates the same index, a collision happens.

Example:

hm.put("Run2", 3333); // Also hashes to index 5
  • Collision resolution is handled by linked list or tree (since Java 8) at that bucket: index 5: → Node(hash1, "Run", 572) → Node(hash2, "Run2", 3333)

🔍 get("Run") – Retrieval Process

  1. Hash code of "Run" is calculated again.
  2. Index is computed.
  3. HashMap goes to that bucket index.
  4. It traverses the list (or tree) comparing both:
    • hash value
    • .equals() on the key
  5. Returns the value once match is found.

🔧 Java 8+ Improvement:

  • When a linked list exceeds threshold (8 nodes) at a bucket, it converts it to a Red-Black Tree to improve lookup performance from O(n) to O(log n).

🔁 Summary Diagram:

Bucket Array (default size 16)
Index: 0 1 2 3 4 5 6 ...
- - - - - [Run|572] → [Run2|3333]

📌 Important Notes:

  • Keys must implement hashCode() and equals() properly.
  • HashMap allows one null key and multiple null values.
  • Not thread-safe. Use ConcurrentHashMap for thread safety.

✅ Java 8 Stream API

🔷 What is a Stream?

A Stream in Java is a sequence of data (objects) from a source (like a Collection, Array, or I/O channel) that supports aggregate operations like filter, map, reduce, collect, etc.

Think of a stream like a data pipeline that transforms data step by step.

🔷 Why Streams?

✅ Traditional (Imperative) Style:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = new ArrayList<>();
for (Integer i : list) {
squares.add(i * i);
}

✅ Stream (Declarative) Style:

List<Integer> squares = list.stream().map(i -> i * i).collect(Collectors.toList());

🔷 Key Characteristics

  • No storage: It doesn’t hold data. It pulls data from a source.
  • Laziness: Intermediate operations are lazy (executed only when terminal op is called).
  • Functional: Supports lambda expressions and functional-style operations.
  • Chained operations: Can chain multiple operations together.

🔷 Stream Creation

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream(); // Sequential
Stream<String> parallelStream = list.parallelStream(); // Parallel

🔷 Core Stream Operations

✅ Intermediate (return a stream)

  • filter(Predicate<T>) – filters elements
  • map(Function<T, R>) – transforms elements
  • distinct() – removes duplicates
  • sorted() – sorts the stream
  • limit(n) – restricts to first n elements

✅ Terminal (consumes stream)

  • forEach() – processes each element
  • collect() – gathers elements into list/set/map
  • count() – counts elements
  • reduce() – reduces to a single result (sum, min, etc.)
  • anyMatch(), allMatch() – matching criteria

🔷 Examples with Output

✅ 1. Square & Distinct

List<Integer> numbers = Arrays.asList(6, 2, 2, 3, 7, 3, 5);
numbers.stream().map(i -> i * i).distinct().forEach(System.out::println);

🔸 Output:

36
4
9
49
25

✅ 2. Count Empty Strings

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jki");
long count = strings.stream().filter(String::isEmpty).count();
System.out.println(count);

🔸 Output:

2

✅ 3. Limit & Sort Random Integers

new Random().ints().limit(5).sorted().forEach(System.out::println);

🔸 Output (varies):

-1663187973  
-1209031040
-707437559
408095708
1167106420

✅ 4. Parallel Stream

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
long count = strings.parallelStream().filter(String::isEmpty).count();
System.out.println(count);

🔸 Output:

2

✅ 5. Collectors (toList & joining)

List<String> strings = Arrays.asList("abc", "bc", "efg", "abcd", "jkl");

List<String> filtered = strings.stream()
.filter(s -> s.length() > 2)
.collect(Collectors.toList());

System.out.println("Filtered: " + filtered);

String merged = strings.stream().collect(Collectors.joining(", "));
System.out.println("Merged: " + merged);

🔸 Output:

Filtered: [abc, efg, abcd, jkl]  
Merged: abc, bc, efg, abcd, jkl

✅ 6. Summary Statistics

List<String> list = Arrays.asList("3", "6", "8", "14", "15");
IntSummaryStatistics stats = list.stream()
.mapToInt(Integer::parseInt)
.summaryStatistics();

System.out.println("Max: " + stats.getMax());
System.out.println("Min: " + stats.getMin());
System.out.println("Sum: " + stats.getSum());
System.out.println("Average: " + stats.getAverage());

🔸 Output:

Max: 15  
Min: 3
Sum: 46
Average: 9.2

🔷 Parallel vs Sequential Stream

Featurestream()parallelStream()
ExecutionSingle-threadedMulti-threaded
PerformanceSlower on big dataFaster on big data (CPU-bound)
Thread-safe required?NoYes (use Concurrent Collections)
Use caseSimpler tasksHeavy CPU tasks (e.g., processing large JSON)

🔷 When to Use Streams

✅ Use Streams when:

  • You want concise and readable code
  • You’re doing collection transformations
  • You want to avoid for-loops

❌ Avoid Streams when:

  • You need indexed access
  • You need side effects or complex stateful logic

✅ Summary

ConceptDescription
StreamAbstraction for processing sequences of data
Intermediate Opsmap, filter, sorted, limit
Terminal OpsforEach, collect, count, reduce
CollectorstoList, toSet, joining, groupingBy
Parallel StreamsUse for large data processing

Employee Class Definition. (Stram example)

class Employee {
int id;
String name;
int age;
String sex;
String dept;
double salary;

public Employee(int id, String name, int age, String sex, String dept, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.dept = dept;
this.salary = salary;
}

@Override
public String toString() {
return id + " - " + name + " (" + sex + ", " + age + ") - " + dept + " - ₹" + salary;
}
}

✅ Sample Employee List

List<Employee> employees = Arrays.asList(
new Employee(1, "Alice", 30, "Female", "HR", 50000),
new Employee(2, "Bob", 45, "Male", "IT", 90000),
new Employee(3, "Charlie", 25, "Male", "IT", 60000),
new Employee(4, "Diana", 35, "Female", "Finance", 70000),
new Employee(5, "Ethan", 28, "Male", "HR", 45000),
new Employee(6, "Fiona", 50, "Female", "Finance", 80000)
);

✅ Stream-Based Operations

🔹 A) Filter all employees whose salary > 60,000

List<Employee> highEarners = employees.stream()
.filter(e -> e.salary > 60000)
.collect(Collectors.toList());

highEarners.forEach(System.out::println);

📌 Output:

2 - Bob (Male, 45) - IT - ₹90000.0  
4 - Diana (Female, 35) - Finance - ₹70000.0
6 - Fiona (Female, 50) - Finance - ₹80000.
0

🔹 B) Count number of Male and Female employees

Map<String, Long> genderCount = employees.stream()
.collect(Collectors.groupingBy(e -> e.sex, Collectors.counting()));

System.out.println(genderCount);

📌 Output:

{Female=3, Male=3}

🔹 C) Average salary by department

Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(e -> e.dept, Collectors.averagingDouble(e -> e.salary)));

System.out.println(avgSalaryByDept);

📌 Output:

{Finance=75000.0, IT=75000.0, HR=47500.0}

🔹 D) Highest paid employee

Optional<Employee> highestPaid = employees.stream()
.max(Comparator.comparingDouble(e -> e.salary));

highestPaid.ifPresent(System.out::println);

📌 Output:

2 - Bob (Male, 45) - IT - ₹90000.0

🔹 E) Group employees by department

Map<String, List<Employee>> deptEmployees = employees.stream()
.collect(Collectors.groupingBy(e -> e.dept));

deptEmployees.forEach((dept, list) -> {
System.out.println(dept + ":");
list.forEach(System.out::println);
});

📌 Output (formatted):

HR:
1 - Alice (Female, 30) - HR - ₹50000.0
5 - Ethan (Male, 28) - HR - ₹45000.0
IT:
2 - Bob (Male, 45) - IT - ₹90000.0
3 - Charlie (Male, 25) - IT - ₹60000.0
Finance:
4 - Diana (Female, 35) - Finance - ₹70000.0
6 - Fiona (Female, 50) - Finance - ₹80000.0

🔹 F) Names of all employees above age 30

List<String> names = employees.stream()
.filter(e -> e.age > 30)
.map(e -> e.name)
.collect(Collectors.toList());

System.out.println(names);

📌 Output:

[Alice, Bob, Diana, Fiona]

✅ Bonus: Salary Summary Statistics

DoubleSummaryStatistics salaryStats = employees.stream()
.mapToDouble(e -> e.salary)
.summaryStatistics();

System.out.println("Max: " + salaryStats.getMax());
System.out.println("Min: " + salaryStats.getMin());
System.out.println("Sum: " + salaryStats.getSum());
System.out.println("Average: " + salaryStats.getAverage());

📌 Output:

Max: 90000.0  
Min: 45000.0
Sum: 395000.0
Average: 65833.33

🔷 What is a ClassLoader in Java?

Definition:
A ClassLoader in Java is a part of the Java Runtime Environment that dynamically loads Java classes into memory during runtime. Classes are not loaded until they are referenced for the first time.

Main ClassLoader types:

  1. Bootstrap ClassLoader – loads core Java classes from rt.jar.
  2. Extension ClassLoader – loads classes from the ext directory (jre/lib/ext).
  3. System/Application ClassLoader – loads classes from the application’s classpath.

Core method:

javaCopyEditClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass("com.example.MyClass");

✅ What is Caching?

Caching is a performance enhancement technique. It stores frequently used data in a temporary storage (memory) so that future requests for that data are served faster.

🔹 Benefits:

  • Reduces database hits
  • Improves response time
  • Reduces server load

🔹 Types of Caching:

TypeDescription
In-memoryStores data in memory (e.g. HashMap, EhCache, Redis)
Database CachingUsed in ORM like Hibernate (Session, 2nd level)
Web Server CachingStatic content like CSS, JS, images

🔷 How to Implement Caching in Spring Boot?

✅ Step-by-step:

  1. Enable caching:
@SpringBootApplication
@EnableCaching
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
  1. Use @Cacheable:
@Cacheable(value = "books", condition = "#name.length() < 50")
public Book findBook(String name) {
// Simulate DB call
return bookRepo.findByName(name);
}
  1. Evicting cache:
@CacheEvict(value = "books", allEntries = true)
public void clearCache() {
// Clears all books cache
}

🔷 Hibernate Caching Levels

Hibernate provides three levels of caching:

1️⃣ First-Level Cache (Session Cache)

  • Enabled by default
  • Works per session (same session = same cached object)
  • Doesn’t require configuration

2️⃣ Second-Level Cache

  • Caches objects across sessions (application level)
  • Needs config and external provider (e.g., EhCache, Infinispan, Redis)
  • Example:
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

3️⃣ Query Cache

  • Caches query results (not just entities)
  • Needs @Cacheable annotations on queries

✅ WAR vs EAR

FormatUse CaseContains
WAR (Web Application Archive)For web appsJSP, Servlets, static files
EAR (Enterprise Archive)For enterprise appsCan include WARs, JARs, EJBs

🔷 Class File Conflict in Two JARs

Q: If a class is present in two JARs in the classpath, which one is used?

Answer: The class from the first JAR in the classpath order is used. ClassLoader searches sequentially and loads the first match.

java -cp jar1.jar:jar2.jar MyApp  # Class from jar1 will be loaded

📌 In WAR or EAR files:

  • WEB-INF/classes is loaded before WEB-INF/lib/*.jar
  • Result may vary between servers and startups due to classloader hierarchy

✅ Utility: Convert String to Char Array and Sort

String s = "aasa";
char[] arr = s.toCharArray();
Arrays.sort(arr);

System.out.println(Arrays.toString(arr)); // Output: [a, a, a, s]

✅ Spring Boot Debugging

To enable remote debugging in Spring Boot:

🔹 Using JAR:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 -jar myapp.jar

🔹 Using Maven:

mvn spring-boot:run -Dagentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000

✅ Singleton Class in Java

➤ Only one instance of the class is created in the JVM.

🔹 Rules:

  1. Constructor should be private.
  2. Use a static method to return the instance.

🔹 Example:

class Singleton {
private static Singleton instance;
public String s;

private Singleton() {
s = "Hello, I am Singleton";
}

public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}

Usage:

public class Test {
public static void main(String[] args) {
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();

a.s = a.s.toUpperCase();

System.out.println("From a: " + a.s); // FROM A: HELLO, I AM SINGLETON
System.out.println("From b: " + b.s); // FROM B: HELLO, I AM SINGLETON
}
}

✅ Aggregation vs Composition

🔹 Aggregation (HAS-A Relationship):

  • Weak association.
  • Child can exist independently of parent.
class Address {
String city, state;
}

class Employee {
int id;
Address address; // Aggregation
}

🔹 Composition (Strong HAS-A):

  • Strong association.
  • Child can’t exist independently of parent.
class Room { }

class House {
private final Room room = new Room(); // Composition
}

✅ JDBC vs Hibernate

🔹 JDBC:

  • Manual SQL queries.
  • Boilerplate code for connections, statements, result sets.

🔹 Hibernate (ORM):

  • Object-Relational Mapping.
  • Automatically maps Java classes to database tables.

Advantages of Hibernate:

  • Removes boilerplate code.
  • Supports associations, inheritance.
  • Built-in caching, transaction management.
  • Uses HQL (Hibernate Query Language), more object-oriented than SQL.

✅ JDBC execute() vs executeQuery() vs executeUpdate()

MethodUse CaseReturn Type
execute()Any SQL (SELECT, DDL)boolean
executeQuery()SELECT onlyResultSet
executeUpdate()INSERT/UPDATE/DELETEint
Statement stmt = conn.createStatement();

// SELECT
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

// UPDATE
int rows = stmt.executeUpdate("UPDATE users SET name='John' WHERE id=1");

// ANY
boolean status = stmt.execute("SELECT * FROM users");

✅ Exception Handling in Java

Keywords:

  • try
  • catch
  • finally
  • throw – to throw an exception
  • throws – to declare potential exceptions

➤ Checked vs Unchecked:

Checked ExceptionsUnchecked Exceptions
Handled at compile-timeHandled at runtime
e.g., IOExceptione.g., NullPointerException

Example:

public void readFile() throws FileNotFoundException {
FileReader file = new FileReader("abc.txt");
}

✅ Error vs Exception

Error (Serious)Exception (Recoverable)
Not meant to be caught normallyCatch and handle in code
e.g., OutOfMemoryErrore.g., ArithmeticException, IOException

✅ throw vs throws

throwthrows
Used to throw an exceptionDeclares that a method might throw
Used inside method bodyUsed with method signature
void validateAge(int age) {
if (age < 18)
throw new ArithmeticException("Not eligible");
}

void someMethod() throws IOException, SQLException {
// method body
}

✅ Exception Hierarchy in Java

Object
└── Throwable
├── Exception (Checked & Unchecked)
│ ├── Checked Exceptions (IOException, SQLException, etc.)
│ └── Unchecked Exceptions (RuntimeException, NullPointerException, etc.)
└── Error
├── VirtualMachineError
└── AssertionError
  • Checked Exception → must be declared or handled (e.g. IOException)
  • Unchecked Exception → not mandatory to handle (e.g. NullPointerException)
  • Error → serious issues that should not be caught (e.g. OutOfMemoryError)

❓ Can we write multiple catch blocks under a single try block?

✅ Yes

🔄 System.exit() vs return

FeaturereturnSystem.exit()
ScopeExits current methodTerminates the entire JVM immediately
Use CaseUsed for returning from a methodUsed to forcefully shut down an app
Flow ContinuityProgram continues after returnProgram ends completely

🧩 Regular Expression in Java

import java.util.regex.*;

public class RegexDemo {
public static void main(String[] args) {
String line = "This order was placed for QT3000! OK?";
String pattern = "(.*)(\\d+)(.*)"; // match a digit pattern

Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(line);

if (m.find()) {
System.out.println("Group 0: " + m.group(0)); // entire match
System.out.println("Group 1: " + m.group(1)); // before digits
System.out.println("Group 2: " + m.group(2)); // digits
} else {
System.out.println("NO MATCH");
}
}
}

📞 Twilio

  • Twilio is a cloud communication platform.
  • Enables SMS, voice calls, and messaging from your application using APIs.
  • You can send SMS using Java SDK by registering and using Twilio credentials.

🧠 Spring Singleton vs Prototype Scope

By Default:

Spring beans are singleton scoped:

@Service
public class DataService { }

@RestController
public class Controller1 {
@Autowired
DataService dataService; // Singleton - shared instance
}

@RestController
public class Controller2 {
@Autowired
DataService dataService; // Same instance as in Controller1
}

To Make it Non-Singleton (Prototype):

@Service
@Scope("prototype")
public class DataService { }

Now each time DataService is autowired, a new instance will be created.

🔁 Alternative to @Autowired (Constructor Injection)

Instead of field injection:

@Autowired
EmployeeRepository repo;

Use constructor injection:

@Component
public class MyController {
private final EmployeeRepository repo;

public MyController(EmployeeRepository repo) {
this.repo = repo;
}
}
  • ✅ Preferred for immutability, testability, and clean code

🔧 Functional Interface (Java 8)

  • Functional Interface: Interface with exactly one abstract method
  • Can contain default and static methods (Java 8+)
  • Used in Lambda expressions and Streams

Example:

@FunctionalInterface
interface MyFunctional {
void execute(); // Only one abstract method
}

Usage with Lambda:

@FunctionalInterface
interface MyFunctional {
void execute(); // Only one abstract method
}

public class FunctionalExample {
public static void main(String[] args) {
// Lambda expression for functional interface
MyFunctional mf = () -> System.out.println("Running Functional Interface");

// Calling the method
mf.execute(); // Output: Running Functional Interface
}
}

Notes:

  • If you try to add another abstract method in MyFunctional, it will throw a compiler error because the interface is marked with @FunctionalInterface.
  • Default and static methods do not count towards the single-abstract-method rule.

✅ Example with default Method:

@FunctionalInterface
interface MyFunctional {
void execute(); // Only abstract method

default void show() {
System.out.println("Default method in Functional Interface");
}
}

What is Spring Boot?

Spring Boot is a module of Spring Framework that simplifies the development of production-ready Spring applications.
It provides:

  • Auto-configuration
  • Embedded server (Tomcat, Jetty)
  • Opinionated starter dependencies

Microservices in Spring Boot

A Microservice is an architecture style where each module of an application is a loosely coupled, independently deployable service.

Advantages:

  • Easy deployment
  • Scalable and lightweight
  • Works well with containers (e.g., Docker)
  • Easy to maintain and develop independently

HTTPS Configuration in Spring Boot

server.port=443
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=springboot
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat

Enables SSL with a keystore on port 443 (HTTPS).

Scheduling in Spring Boot

Annotations used:

  • @EnableScheduling – to enable scheduling feature.
  • @Scheduled – to run a method at fixed interval or cron expression.

Example:

@SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Component
public class ScheduledTasks {
// Runs every minute
@Scheduled(cron = "0 * * * * ?")
public void cronJob() {
System.out.println("Running scheduled task at: " + new Date());
}
}

Logging with SLF4J and Logback

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(MyClass.class);

logger.info("Info message");
logger.error("Error message");
logger.warn("Warning message");

SLF4J acts as a facade for different logging frameworks.

Spring Boot Actuator

Provides endpoints to monitor and manage applications (e.g., health, metrics, info).

Add to pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Sample Endpoints:

GET /actuator/health

health info::
{
"status": "UP"
}

for detail info add in application.properties
management.endpoint.health.show-details=always

now deailed health info
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "H2",
"validationQuery": "isValid()"
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 500000000000,
"free": 120000000000,
"threshold": 10485760
}
},
"ping": {
"status": "UP"
}
}
}
GET /actuator/info

{} // nothing without configuraion

You can provide custom info in your application.properties:

info.app.name=My Spring Boot App
info.app.description=A demo Spring Boot application
info.app.version=1.0.0

{
  "app": {
    "name": "My Spring Boot App",
    "description": "A demo Spring Boot application",
    "version": "1.0.0"
  }
}

DTO (Data Transfer Object)

Used to transfer data between layers without exposing the full entity.

@Data // Lombok annotation to reduce boilerplate
public class UserDTO {
private String name;
private String email;
}

✅ What is a POJO in Java?

POJO stands for Plain Old Java Object. A POJO is a simple Java object that doesn’t depend on any special Java frameworks, classes, or annotations. It follows standard conventions and is primarily used for data representation.

✅ Characteristics of a POJO:

  • No need to extend or implement any specific classes/interfaces.
  • Contains fields (variables), constructors, getters/setters.
  • No business logic.
  • No framework-specific annotations (optional in some frameworks like Lombok).

✅ Example: Simple POJO

public class Employee {
private int id;
private String name;
private double salary;

// Default constructor
public Employee() {}

// Parameterized constructor
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}

// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public double getSalary() { return salary; }
public void setSalary(double salary) { this.salary = salary; }

// toString() method
@Override
public String toString() {
return "Employee{id=" + id + ", name='" + name + "', salary=" + salary + "}";
}
}

✅ POJO with Lombok (to reduce boilerplate)

import lombok.Data;

@Data
public class Employee {
private int id;
private String name;
private double salary;
}

🔹 The @Data annotation automatically generates:

  • Getters and setters
  • toString()
  • equals() and hashCode()
  • A constructor if needed

✅ Where are POJOs used?

  • As entities in JPA/Hibernate
  • As DTOs (Data Transfer Objects)
  • As model classes in Spring MVC
  • As request/response objects in REST APIs

Lombok Annotations

  • @Data → generates getters, setters, toString, equals, and hashCode.
  • @Getter / @Setter → individually for fields
  • @EqualsAndHashCode → for equality comparison

Example:

@Data
public class Point {
private double x;
private double y;
}

@CrossOrigin

Enables Cross-Origin Resource Sharing (CORS) at controller/method level.

@RestController
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll();
}
}

🧠 Summary

ScopeAnnotation/ApproachUsage
Method level@CrossOriginOn specific controller methods
Controller@CrossOriginOn entire controller
ApplicationWebMvcConfigurer classBest for global configuration (all endpoints)
Spring Gatewayapplication.ymlSpecific to Spring Cloud Gateway

global setting using webMvsConfigurer class

@Configuration
public class GlobalCorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")  // Allow all paths
                        .allowedOrigins("*")  // Allow all origins
                        .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")  // Allow these methods
                        .allowedHeaders("*")  // Allow all headers
                        .allowCredentials(false);  // You can make it true for cookies/session support
            }
        };
    }
}

@Transactional

Manages transaction boundaries automatically.

@Transactional
public void transferMoney(Account from, Account to, BigDecimal amount) {
from.withdraw(amount);
to.deposit(amount);
}

JPA vs Hibernate

AspectJPA (Java Persistence API)Hibernate
TypeSpecification (Interface)Implementation (Framework/API)
DefinitionA standard for ORM in JavaA popular implementation of the JPA specification
Provided ByJava EE / Jakarta EERed Hat
Code AvailabilityOnly interfaces (no implementation logic)Full implementation + extra features
AnnotationsDefines standard annotations like @Entity, @IdUses JPA annotations + its own (@Where, @CreationTimestamp)
ConfigurationNeeds provider (e.g., Hibernate) to workComes with full configuration & mapping engine
Query LanguageJPQL (Java Persistence Query Language)JPQL + HQL (Hibernate Query Language – more powerful)
CachingJPA defines second-level cache (optional)Hibernate provides L1, L2, and query caching out of the box
Support for FeaturesBasic ORM features onlyAdvanced features: custom naming strategies, interceptors, filters
Ease of UseNeeds more code and setupEasier with built-in tools, widely used in Spring Boot
Learning CurveAbstract, needs an implementation to testSteeper but well-documented with lots of examples

JPA Fetch Types

  • LAZY – fetch when needed
  • EAGER – fetch immediately
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;

@JsonIgnore vs @JsonManagedReference / @JsonBackReference

  • @JsonIgnore skips serialization/deserialization.
  • @JsonManagedReference & @JsonBackReference help manage parent-child references in bi-directional relationships to avoid recursion.

✅ Java Annotations Overview

Annotations are metadata that provide data about a program but are not part of the program itself. They serve various purposes:

🔹 Used For:

  • Compiler instructions
  • Build-time processing
  • Runtime behavior (Spring, Hibernate, JPA)

🔹 Common Built-in Annotations:

  • @Override
  • @Deprecated
  • @SuppressWarnings

✅ Important Spring Annotations

AnnotationPurpose
@EntityMarks class as a JPA entity
@ComponentMarks a Spring-managed bean
@AutowiredAuto-inject dependencies
@QualifierSpecify bean to inject
@PostConstructMethod to run after bean is initialized
@PreDestroyMethod to run before bean is destroyed
@ServiceMarks a service-layer component
@RepositoryMarks data access object

🔹 Custom Annotation Example:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String value();
}

@PostLoad Annotation in JPA

  • The @PostLoad annotation marks a method that is invoked after an entity is loaded from the database, after all eagerly fetched fields are populated.
  • It is commonly used to perform post-initialization logic.

🔹 Example:

@Entity
public class User {
@Id
private Long id;
private String name;
private String email;

@Transient
private String domain;

@PostLoad
public void extractEmailDomain() {
if (email != null && email.contains("@")) {
domain = email.substring(email.indexOf("@") + 1);
}
}
}

✅ JPA Annotations

@Entity
@Table(name = "vehicles")
@NamedQuery(name = "Vehicle.findAll", query = "SELECT v FROM Vehicle v")
public class Vehicle {
@Id
@GeneratedValue
private Long id;

private String name;
}
  • @Entity: Marks class as a JPA entity.
  • @NamedQuery: Pre-defined, static query.
  • @Table: Custom table name mapping.

@Lob Annotation

The @Lob annotation is used to store large objects like binary or character data.

@Lob
private byte[] data;
  • @Lob maps the byte[] field to a BLOB (Binary Large Object) in the database.
  • If the type were String, it would map to a CLOB (Character Large Object).

Iterable Interface in Java

  • Root interface in the collection hierarchy.
  • Enables enhanced for-loop (for-each) usage.
  • Collection extends Iterable.

✅ Dependency Injection Conflict Resolution

When using Spring’s @Autowired annotation for dependency injection, Spring tries to inject a matching bean by type. However, if multiple beans of the same type are found, Spring throws an exception due to ambiguity.

This commonly happens when two or more classes implement the same interface, and Spring doesn’t know which one to inject.

Scenario:

interface Animal {
String characteristics();
}

@Service("dog")
public class Dog implements Animal {
public String characteristics() { return "bark"; }
}

@Service("cat")
public class Cat implements Animal {
public String characteristics() { return "meow"; }
}

Solutions:

  1. Use @Qualifier
@Autowired
@Qualifier("dog")
private Animal animal;
  1. Use @Primary
@Primary
@Service
public class Dog implements Animal { ... }
  1. Setter Injection
private Animal animal;

@Autowired
public void setAnimal(@Qualifier("cat") Animal animal) {
this.animal = animal;
}

✅ Spring Profiles

@Profile("dev")
@Service
public class DevDataSourceConfig { ... }
  • Use application-dev.properties
  • Activate via --spring.profiles.active=dev

@PreAuthorize, @PostAuthorize (Spring Security)

Used for method-level access control:

@PreAuthorize("hasRole('ROLE_ADMIN')")
public void deleteUser(Long id) { ... }

@PostAuthorize("returnObject.owner == authentication.name")
public Book getBook() { ... }

@PreAuthorize("#book.owner == authentication.name")
public void updateBook(Book book) { ...

hasRole() vs hasAuthority()

AnnotationDescription
hasRole('X')Spring Security adds "ROLE_" prefix automatically. Check for "ROLE_X"
hasAuthority('X')No prefix added. You must match the exact authority name (e.g., "ADMIN")

So if your role is stored as "ROLE_TestSuperAdmin" in DB, hasRole('TestSuperAdmin') will match it, but hasAuthority('TestSuperAdmin') will not, unless no prefix exists.

🔸 Example:

@PreAuthorize("hasRole('TestSuperAdmin')")
@GetMapping("/blogs/{id}")
public ResponseEntity<?> getPostById(@PathVariable Long id) {
return ResponseEntity.ok(blogRepository.findById(id));
}

@NaturalId — Natural Identifier in Hibernate

A Natural ID is a domain-specific unique identifier, like:

  • ISBN for a book
  • Email or SSN for a person
  • PAN for a tax entity

These are unique in real-world logic and are used in business logic often.

In contrast to a Surrogate Key (e.g., auto-generated primary key), a Natural ID is usually assigned manually and has business meaning.

🔸 Example:

@Entity
public class Book {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;

@NaturalId
private String isbn; // natural unique identifier

private String title;
}

⚠ Why not use Natural IDs as primary keys?

  • They can change (rare but possible, e.g., email updates)
  • They are often longer or more complex than numeric IDs
  • Auto-generated keys (surrogate keys) are faster and lighter for joins and indexing

@GeneratedValue — Strategy for Primary Key Generation

Used to automatically generate the primary key value.

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;

🔹 Generation Types in JPA

StrategyDescriptionUse Case / Notes
AUTODefault. JPA provider chooses the strategy.Flexible but implicit (can vary per DB)
IDENTITYUses DB auto-increment. New row inserted, then DB gives ID.Simple, but can’t batch inserts; not best for performance
SEQUENCEUses DB sequence object to fetch the next ID.Preferred if DB supports it (e.g., PostgreSQL, Oracle)
TABLEUses a separate table to simulate sequence generation.Rarely used; works with all DBs, but poor performance

ResponseEntity in Spring Boot

ResponseEntity<T> is used to represent the entire HTTP response, including:

  • Status code
  • Headers
  • Body (payload)

🔸 Example:

return new ResponseEntity<>(
new ApiResponse(false, "TestUser is already taken!"),
HttpStatus.BAD_REQUEST
);

You can also use helper methods like ResponseEntity.ok():

return ResponseEntity.ok(new ApiResponse(true, "User created successfully!"));

✅ Maven Scope: <scope> in pom.xml

🔹 <scope>compile</scope> (default)

  • Needed for compiling and running
  • Included in both build and runtime classpaths

🔹 <scope>provided</scope>

  • Needed for compiling, but not packaged
  • Example: Servlet APIs in web containers like Tomcat
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>

✅ JPA Cascade Types

Cascade types let related entities be updated/deleted/saved automatically when the owning entity is acted upon.

TypeDescription
PERSISTSaves child when parent is saved
MERGEMerges changes from parent to child
REMOVEDeletes child entities when parent is deleted
REFRESHRefreshes children when parent is refreshed
DETACHDetaches children when parent is detached
ALLAll the above operations

🔸 Example:

@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Address> addresses;
  • orphanRemoval = true: deletes the child (Address) if it is removed from the list.

save() vs persist() in JPA

MethodDescription
save()(Spring Data JPA) Immediately generates the primary key
persist()(JPA EntityManager) Doesn’t assign the ID immediately unless flush() is called

✅ UUID Strategy in Hibernate

UUID is a universally unique identifier (128-bit). You can generate primary keys using this strategy.

🔸 Example:

@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(columnDefinition = "BINARY(16)")
private UUID id;

✅ Spring Boot Embedded Server Configuration

🔸 Default Server

Spring Boot uses Tomcat as the default embedded server.

🔸 Switch to Jetty

To switch from Tomcat to Jetty:

pom.xml

<!-- Exclude Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Add Jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

🔸 Switch to Undertow

<!-- Add Undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

✅ Change Default Server Port & Context Path

In application.properties:

# Change HTTP Server port
server.port=8081

# Set custom context path
server.servlet.context-path=/myapp

App URL: http://localhost:8081/myapp

✅ Enable GZip Compression

GZIP compression helps improve performance by reducing response payload size.

# Enable compression
server.compression.enabled=true

# MIME types to compress
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json

# Minimum size to compress (in bytes)
server.compression.min-response-size=1024

✅ Enable HTTP/2 in Spring Boot

HTTP/2 improves performance through features like multiplexing and header compression.

# Enable HTTP/2 (only works on HTTPS)
server.http2.enabled=true

Note: Your server must support HTTP/2 and it works only over HTTPS.

✅ Enable Static Resource Caching

Improves performance by allowing browser caching of static files (JS, CSS, images).

# Cache duration for static resources (in seconds)
spring.resources.cache.cachecontrol.max-age=120

# Forces revalidation before using stale cache
spring.resources.cache.cachecontrol.must-revalidate=true

✅ Configuring Spring Boot to Use Gson Instead of Jackson

By default, Spring Boot uses Jackson for JSON serialization/deserialization.

To use Gson instead:

🔸 Step 1: Exclude Jackson in pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>

🔸 Step 2: Add Gson Dependency

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

Spring Boot will automatically detect and use Gson if Jackson is excluded.

✅ Docker – Containerization Platform

Docker allows you to package your application and its dependencies into a container, which can run consistently across any environment.
Build->Ship->Deploy

🔸 Key Features:

  • Lightweight & portable
  • Consistent environments for development & production
  • Isolated containers on the same host

🔸 Common Docker Terms:

TermDescription
ImageBlueprint of your application (like a class)
ContainerRunning instance of an image (like an object)
DockerfileScript with instructions to build an image

🔸 Example Dockerfile for Spring Boot:

FROM openjdk:17
VOLUME /tmp
COPY target/myapp.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

🔸 Docker Commands:

# Build image
docker build -t myapp .

# Run container
docker run -p 8080:8080 myapp

✅ Container Isolation in Docker

Each Docker container:

  • Has its own process space, network, and filesystem
  • Runs independently of other containers
  • Can be configured with different versions of OS, libraries, or dependencies

This ensures:

  • No conflict between apps
  • Efficient resource usage
  • Rapid deployment and scalability

✅ Remove Duplicates from a List in Java

List<Integer> sd = new ArrayList<>();
sd.add(3);
sd.add(2);
sd.add(4);
sd.add(5);
sd.add(2);

sd.stream()
.distinct() // removes duplicates
.forEach(System.out::println); // prints each unique element

If you want to double each value before filtering:

✅ Multithreading in Java

🧠 What is Multithreading?

  • Executing multiple threads simultaneously
  • Thread = lightweight sub-process. (a process can have multiple thread. process is a running instance of an application)
  • Used for multitasking in games, animations, servers

💡 Benefits of Threads:

  • Share memory space (unlike processes)
  • Lightweight: fast context-switching
  • Saves resources

🧵 Types of Multitasking

  1. Process-based: Multiple processes (each has its own memory)
  2. Thread-based: Multiple threads within the same process (shared memory)

✅ Ways to Create Threads

1. By Extending Thread Class

class TestThread extends Thread {
public void run() {
System.out.println("Thread is running...");
}

public static void main(String[] args) {
TestThread t1 = new TestThread();
t1.start(); // starts new thread, internally calls run()
}
}

2. By Implementing Runnable Interface

class TestRunnable implements Runnable {
public void run() {
System.out.println("Thread is running...");
}

public static void main(String[] args) {
TestRunnable m1 = new TestRunnable();
Thread t1 = new Thread(m1); // Runnable object passed to Thread
t1.start();
}
}

✅ Thread Lifecycle (Managed by JVM)

  1. New – thread object created but not started
  2. Runnable – start() called, ready for execution
  3. Running – thread is executing
  4. Blocked/Waiting – waiting for resource or lock
  5. Terminated – thread has finished or stopped

🛠 Common Thread Methods

MethodDescription
start()Starts thread execution
run()Defines task of the thread
sleep(ms)Pause execution
join()Waits for thread to finish
currentThread()Returns currently executing thread
notify()/notifyAll()Wakes up waiting thread
wait()Pauses thread until notified

✅ When to Use Which?

CaseUse
You only want to override run()Implement Runnable
You want to override other thread methods tooExtend Thread
Your class already extends another classImplement Runnable (Java doesn’t support multiple inheritance)

✅ Multitasking vs Multithreading

ConceptMultitaskingMultithreading
DefinitionRun multiple processesRun multiple threads inside a process
MemoryEach process has own memoryThreads share memory
SpeedSlower due to heavy memory usageFaster due to lightweight nature
Use CaseRunning different applicationsBackground tasks within app, e.g., logging, timers

class MyThread extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");

        // sleep() example
        try {
            Thread.sleep(1000);  // pause for 1 second
            System.out.println(Thread.currentThread().getName() + " woke up after sleeping");
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " was interrupted");
        }
    }
}

public class ThreadMethodsExample {
    public static void main(String[] args) {

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        // setName()
        t1.setName("Worker-1");
        t2.setName("Worker-2");

        // start() calls run() in new thread
        t1.start();
        t2.start();

        // isAlive() - true if thread is still running
        System.out.println(t1.getName() + " is alive: " + t1.isAlive());
        System.out.println(t2.getName() + " is alive: " + t2.isAlive());

        // join() - main thread waits for both threads to complete
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            System.out.println("Main thread interrupted");
        }

        System.out.println("Main thread: " + Thread.currentThread().getName());
        System.out.println("All threads have finished");
    }
}

Data Structure

1. Stack – Last In First Out (LIFO)

A Stack is a linear data structure that follows the LIFO (Last In First Out) principle, meaning the last element added is the first one to be removed.

🧠 Real-world Analogy:

A stack of plates — the last plate placed on top is the first one you pick up.

🔧 Common Java Methods:

  • push(E item) – Adds an item to the top
  • pop() – Removes the top item
  • peek() – Views the top item without removing it
  • search(Object o) – Returns the 1-based position from the top

🧪 Example in Java:

Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
System.out.println(stack.pop()); // 20
System.out.println(stack.peek()); // 10

2. Queue – First In First Out (FIFO)

A Queue is a linear data structure that follows the FIFO (First In First Out) principle, where the element added first is the one removed first.

🧠 Real-world Analogy:

A queue of people in a line — the person who comes first is served first.

🔧 Common Java Methods:

  • add(E e) – Adds an item to the queue
  • remove() – Removes and returns the head of the queue
  • peek() – Returns the head without removing
  • size() – Returns the number of elements

🧪 Example in Java:

Queue<String> queue = new LinkedList<>();
queue.add("Alice");
queue.add("Bob");
System.out.println(queue.remove()); // Alice
System.out.println(queue.peek()); // Bob

3. Linked List – Dynamic Linear Data Structure

A Linked List is a linear data structure in which elements are stored in non-contiguous memory locations. Each element is called a node and has a reference to the next node.

🧠 Real-world Analogy:

A train — each coach (node) is linked to the next.

🔧 Features:

  • Dynamic memory allocation
  • Easier insertions/deletions compared to arrays

🔧 Common Java Methods:

  • add(E e) – Adds at the end
  • add(int index, E element) – Adds at a specific index
  • remove(int index) – Removes element at index
  • get(int index) – Fetches element at index

🧪 Example in Java:

LinkedList<String> list = new LinkedList<>();
list.add("First");
list.add("Second");
list.addFirst("Zero");
list.remove("Second");
System.out.println(list); // [Zero, First]

📚 Summary Table

StructureOrderJava ClassCore OperationsUse Case Example
StackLIFOjava.util.Stackpush, pop, peek, searchBrowser history, Undo
QueueFIFOjava.util.Queueadd, remove, peek, sizeCall center, Printer
Linked ListN/Ajava.util.LinkedListadd, remove, get, addFirstMusic playlist, Navigation

✅ What Are Generics?

Generics allow you to define classes, interfaces, and methods with type parameters, providing compile-time type safety and eliminating the need for casting.

🔹 1. Generic Class Example

public class Box<T> {
private T value;

public void set(T value) {
this.value = value;
}

public T get() {
return value;
}
}

✅ Usage:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
System.out.println(stringBox.get()); // Output: Hello

Box<Integer> intBox = new Box<>();
intBox.set(123);
System.out.println(intBox.get()); // Output: 123

🔹 2. Generic Method Example

public class Utility {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
}

✅ Usage:

public class Main {
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"A", "B", "C"};

Utility.printArray(intArray); // Output: 1 2 3
Utility.printArray(strArray); // Output: A B C
}
}

🔹 3. Bounded Type Parameters

You can restrict the types that can be used as type arguments.

public class Calculator<T extends Number> {
public double square(T num) {
return num.doubleValue() * num.doubleValue();
}
}

✅ Usage:

Calculator<Integer> intCalc = new Calculator<>();
System.out.println(intCalc.square(4)); // Output: 16.0

🔹 4. Wildcards (?)

Used when you want to accept unknown types.

public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}

✅ Benefits of Generics

  • Type Safety – Errors are caught at compile-time.
  • Code Reusability – Write a single class/method for multiple data types.
  • No casting needed – Improves code readability and maintainability.

JPA Complete tutorial : Click for details

Similar Posts