Skip to main content

Command Palette

Search for a command to run...

Java for Automation Tester

Updated
•38 min read•View as Markdown

What is a method?

A method in Java is a named block of code that performs a task (computes a value, updates state, does I/O). Methods are the building blocks of behavior in classes and interfaces.
Think of a method as “a function attached to a type” (object-oriented).

A method has:

  • Modifiers (public, private, static, final, abstract, synchronized, native, etc.)

  • Return type (can be any type or void)

  • Name

  • Parameter list (zero or more parameters)

  • throws clause (optional checked exceptions it may throw)

  • Body { ... } (except abstract methods, which have no body)

Why methods?

  • Reuse code (DRY)

  • Encapsulate behavior & hide implementation

  • Organize program logic into small understandable units

  • Enable polymorphism (different implementations in subclasses)

  • Allow separation of interface (what) and implementation (how)

How to declare & call a method — basic syntax

// instance method
public int add(int a, int b) {
    return a + b;
}

// static method
public static void greet(String name) {
    System.out.println("Hello, " + name);
}

Call:

MyClass obj = new MyClass();
int sum = obj.add(2,3);        // instance call
MyClass.greet("Sam");          // static call

Method components explained

  • Access modifiers: public, protected, (package-private default), private.

  • Non-access modifiers: static, final, abstract, synchronized, native, strictfp.

  • Return type: type or void.

  • Name: must be a valid identifier.

  • Parameters: typed (Type name, ...).

  • Throws: throws IOException.

  • Body: code block. abstract methods omit body.

Types of methods (common kinds)

  • Instance method — operates on an object (uses this).

  • Static method — belongs to the class, not an instance.

  • Abstract method — declared in abstract class (no body).

  • Default & static methods in interfaces (Java 8+).

  • Private methods in interfaces (Java 9+).

  • Synchronized method — acquires a lock when executing.

  • Native method — implemented in other language (C/C++) using JNI.

  • Final method — cannot be overridden.

Declaration examples (various kinds)

public class Example {
    // instance
    public int square(int x) { return x*x; }

    // static
    public static void log(String s) { System.out.println(s); }

    // synchronized
    public synchronized void increment() { /* thread-safe */ }

    // final
    public final void cannotOverride() { }

    // abstract (in abstract class)
    public abstract void doWork();

    // native
    public native long getNativeTime();

    // varargs
    public int sum(int... nums) { /* treat nums as int[] */ return 0; }
}

Parameters — call-by-value (important!)

Java is always pass-by-value:

  • For primitive parameters (e.g., int) the actual primitive value is copied.

  • For object references, the reference is copied (so both caller and method refer to the same object), but the reference itself is passed by value — reassigning that parameter inside the method does not change the caller’s reference.

Examples:

void modifyInt(int x) { x = 10; }          // caller unchanged
void modifyPerson(Person p) { p.name = "X"; } // caller's object's field changes
void reassignPerson(Person p) { p = new Person("Y"); } // caller's ref unchanged

Swap example that fails for primitives:

void swap(int a, int b) { int t=a; a=b; b=t; } // doesn't swap caller ints

But you can swap wrapped values by mutating object fields.

Varargs

Syntax Type... name (treated as array)

public void printAll(String... items) {
    for (String s : items) System.out.println(s);
}

Return types and signatures

  • The method signature in Java (for overloading) is method name + parameter types. Return type is NOT part of the signature. So you cannot overload by only changing return type.

  • Overriding requires same signature (name + param types); return type can be covariant (a subtype of the parent's return type).

Example — invalid (compiler error):

int foo() { ... }
long foo() { ... } // ERROR: duplicate method foo()

Covariant return example — valid:

class A { Number get() { ... } }
class B extends A { @Override Integer get() { ... } } // OK

Overloading vs Overriding

  • Overloading: multiple methods in same class (or subclass) with same name but different parameter lists.

  • Overriding: subclass provides new implementation for inherited method with same signature.

Overloading resolution (compile-time) considers types, boxing, widening and varargs — it can be subtle and sometimes ambiguous.

Example of overriding (polymorphism / dynamic dispatch):

class Animal { void speak() { System.out.println(".."); } }
class Dog extends Animal { @Override void speak() { System.out.println("woof"); } }

Animal a = new Dog();
a.speak(); // prints "woof" — runtime dispatch to Dog.speak()

Notes:

  • private and static methods are not polymorphic — they cannot be overridden (static methods are hidden).

  • Overriding method cannot reduce visibility (public -> private is illegal).

  • Checked-exception rule: overriding method cannot throw broader checked exceptions.

Exceptions in method signature

  • Use throws for checked exceptions.
public void read() throws IOException { /* may throw */ }
  • throw actually throws an exception instance inside the method.

  • Overriding rule: subclass method can't declare new checked exceptions not declared in the parent (unless they are subclasses).

Constructors vs Methods

  • Constructors have no return type and must match class name. They initialize objects.

  • Constructors are invoked using new and are not inherited; they use invokespecial at JVM level.

The main method

Java program entry point:

public static void main(String[] args) { /* ... */ }
// or
public static void main(String... args) { /* ... */ }

The JVM looks for public static void main(String[]). If not present (or signature differs), you get NoSuchMethodError when launching.

Generics & generic methods

You can declare methods with their own type parameters:

public static <T> T pickFirst(T[] arr) { return arr.length>0 ? arr[0] : null; }

Lambdas & method references (Java 8+)

Lambdas implement a functional interface (single abstract method).

List<String> l = List.of("a","b");
l.sort(String::compareToIgnoreCase); // method reference
Consumer<String> c = s -> System.out.println(s); // lambda
Supplier<Date> sd = Date::new; // constructor reference

Reflection & invoking methods at runtime

You can discover and invoke methods using java.lang.reflect.Method:

Method m = MyClass.class.getMethod("greet", String.class);
m.invoke(myInstance, "Alex"); // may throw InvocationTargetException, IllegalAccessException

Reflection can access private methods with setAccessible(true) (subject to security/module rules).

JVM-level details (brief, but important)

  • The JVM stores methods as method_info structures in the class file. Each has a descriptor describing parameter and return types: e.g. (ILjava/lang/String;)V means (int, String)->void.

  • Invocation bytecodes:

    • invokestatic — static methods

    • invokevirtual — most instance method calls (virtual dispatch)

    • invokeinterface — interface method calls

    • invokespecial — private, constructors, super calls

    • invokedynamic — used for lambdas and dynamic call sites (bootstrap mechanism)

  • Method call creates a stack frame: local variables and operand stack. Recursion uses frames; deep recursion → StackOverflowError.

  • The JVM imposes a limit on method code size: the Code attribute of a method in a class file must be ≤ 65535 bytes. (If your method exceeds that, you must split it.)

  • The compiler sometimes emits bridge methods (synthetic methods) to support generics/covariant returns and maintain binary compatibility after type erasure.

  • The JIT compiler can inline frequently used small methods for performance; final/private make inlining easier but JVM does aggressive optimizations anyway.

Concurrency-related method behavior

  • synchronized method locks this for instance methods, or the Class object for static synchronized methods.

  • Consider locking granularity — synchronized on smaller objects when possible.

  • Use java.util.concurrent.locks.Lock for more flexible behavior.

Annotations on methods

You can annotate methods: @Override, @Deprecated, custom annotations, @SafeVarargs (for varargs + generics), etc. Retention policy determines runtime visibility.

Common pitfalls & gotchas

  • Return type not part of signature — can't overload only by return type.

  • Calling instance method on null => NullPointerException:

      MyClass x = null;
      x.instanceMethod(); // NPE at runtime
    

    You can call a static method through a null reference (compiles), but it's strongly discouraged:

      MyClass m = null;
      m.staticMethod(); // works — resolved as MyClass.staticMethod()
    
  • Overloading ambiguity with autoboxing and varargs:

      void f(int x) {}
      void f(Integer x) {} // ambiguous if you pass null
    
  • Recursion without base-case → StackOverflowError.

  • Large methods: hard to read and may exceed bytecode size limit; split into smaller methods.

  • Exposure of internals: returning internal mutable objects exposes state.

Best practices

  • Keep methods small and single-purpose (SRP).

  • Use clear verb-based names: calculateTax, isEmpty, loadFromFile.

  • Prefer immutability / avoid side effects when possible.

  • Validate parameters early (Objects.requireNonNull).

  • Document behavior, especially for nullability and thread-safety.

  • Prefer Optional<T> for "no value" result when appropriate (API choice).

  • Use interfaces and default methods for evolving APIs carefully.

  • Use final for parameters and methods where it improves clarity.

Quick cheat-sheet (summary)

  • Signature = name + parameter types (not return)

  • Invocation opcodes:

    • invokestatic — static

    • invokevirtual — normal instance (virtual dispatch)

    • invokeinterface — interface methods

    • invokespecial — private/constructors/super

    • invokedynamic — lambdas/dynamic

  • Pass-by-value always — object references are copied, not the object

  • Overloading resolved at compile time; overriding resolved at runtime

  • JVM method code size ≤ 65535 bytes

  • Covariant returns allowed for overriding

  • Overriding cannot reduce visibility, cannot add broader checked exceptions

Practical examples (short)

Overloading:

void print(Integer i) { System.out.println("Integer"); }
void print(int i)     { System.out.println("int"); }

print(5);      // prints "int"
print(null);   // compile-time error: ambiguous between Integer and int? (null matches reference type)

Overriding & exceptions:

class Parent { void m() throws IOException {} }
class Child extends Parent {
    @Override
    void m() throws FileNotFoundException { } // OK: narrower checked exception
}

Reflection:

import java.lang.reflect.*;
Method m = String.class.getMethod("toUpperCase");
String s = (String) m.invoke("hello"); // returns "HELLO"

Lambda & method ref:

List<String> names = List.of("bob","Ann");
names.sort(String::compareToIgnoreCase);
Runnable r = () -> System.out.println("hi");

🔹 What is a Constructor?

A constructor in Java is a special method-like block used to initialize objects when they are created using the new keyword.

Key differences from methods:

  • Same name as the class.

  • No return type (not even void).

  • Automatically called by JVM when an object is created.

  • Purpose: set initial state of an object.


🔹 Why do we need Constructors?

  • To provide default values to object fields.

  • To allow different ways of initializing objects (overloading constructors).

  • To enforce rules/validation at the time of object creation.

  • To hide object creation logic (with private constructors in Singleton / Factory pattern).


🔹 How to declare and use a Constructor

class Car {
    String brand;
    int speed;

    // Constructor
    Car(String b, int s) {
        brand = b;
        speed = s;
    }
}

public class Main {
    public static void main(String[] args) {
        Car c = new Car("Tesla", 100); // Constructor called here
        System.out.println(c.brand + " runs at " + c.speed);
    }
}

Output:

Tesla runs at 100

🔹 Types of Constructors

1. Default constructor (no-arg, provided by compiler if none exists)

class Bike {
    Bike() {  // default constructor
        System.out.println("Bike is created");
    }
}

If no constructor is written, Java creates a hidden default constructor:

Bike() { super(); }

2. No-arg constructor (explicitly written by programmer)

class Bike {
    Bike() {
        System.out.println("Custom no-arg constructor");
    }
}

3. Parameterized constructor

class Student {
    String name;
    int age;

    Student(String n, int a) {
        name = n;
        age = a;
    }
}

4. Constructor overloading

Multiple constructors in the same class with different parameter lists.

class Person {
    String name;
    int age;

    Person() { name = "Unknown"; age = 0; }
    Person(String n) { name = n; age = 18; }
    Person(String n, int a) { name = n; age = a; }
}

5. Copy constructor (Java doesn’t provide it, but you can write one)

class Point {
    int x, y;
    Point(int a, int b) { x=a; y=b; }
    Point(Point p) { x = p.x; y = p.y; } // copy constructor
}

6. Private constructor

  • Used in Singleton pattern or Factory methods.
class Singleton {
    private static Singleton instance;
    private Singleton() {} // private constructor

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

🔹 Rules about Constructors

  1. Name must match class name.

  2. Cannot have return type (not even void).

  3. Can have modifiers: public, protected, private, default.

  4. Can call another constructor using this(...).

  5. First line of a constructor must be:

    • this(...) (to call another constructor of the same class), or

    • super(...) (to call parent constructor).
      If not written, compiler inserts super(); automatically.

  6. Constructors are not inherited.

  7. Can be overloaded but not overridden.

  8. Can throw exceptions.

class Test {
    Test() throws IOException { /* ... */ }
}

🔹 Constructor Chaining

Within the same class — use this()

class A {
    A() {
        this(10); // calls parameterized constructor
        System.out.println("Default constructor");
    }
    A(int x) {
        System.out.println("Parameterized constructor: " + x);
    }
}

With parent class — use super()

class Parent {
    Parent() { System.out.println("Parent constructor"); }
}
class Child extends Parent {
    Child() {
        super(); // calls Parent constructor
        System.out.println("Child constructor");
    }
}

🔹 Constructor vs Method

FeatureConstructorMethod
NameSame as classAny name (identifier rules)
Return typeNone (not even void)Must have return type
Called byJVM automatically via newExplicitly by user
PurposeInitialize objectDefine object behavior
InheritanceNot inheritedInherited
OverloadingAllowedAllowed
OverridingNot allowedAllowed

🔹 JVM-level Details

  • When you call new Car(), the JVM does:

    1. Allocate memory for object on heap.

    2. Initialize fields with default values (0, null, false).

    3. Call the constructor (using invokespecial bytecode).

    4. Return the reference to the object.

  • Every constructor must call a parent constructor (super()), directly or indirectly.

  • If no constructor exists in parent, compiler inserts super() call to Object().


🔹 Common Pitfalls

  1. Forgetting to initialize fields (constructor should set them).

  2. Using heavy logic inside constructors (bad practice → slow object creation).

  3. Recursive constructor calls → compilation error.

     class A {
         A() { this(); } // ERROR: recursive constructor invocation
     }
    
  4. Private constructor without factory → object creation impossible.

  5. Not handling exceptions properly in constructor.

  6. Thinking constructors are methods — they are not.


🔹 Best Practices

  • Keep constructors simple (only initialization).

  • Use constructor overloading for flexibility.

  • If many parameters → prefer Builder pattern.

  • Use this(...) and super(...) wisely to avoid duplicate code.

  • Use immutability: make fields final and initialize in constructor.

  • Use @ConstructorProperties for frameworks (Spring, Jackson, etc.).


🔹 Example (Complete)

class Employee {
    String name;
    int id;

    // default constructor
    Employee() {
        this("Unknown", 0); // calls parameterized constructor
    }

    // parameterized constructor
    Employee(String n, int i) {
        name = n;
        id = i;
    }

    // copy constructor
    Employee(Employee e) {
        this.name = e.name;
        this.id = e.id;
    }

    void display() {
        System.out.println("Employee: " + name + " (" + id + ")");
    }
}

public class Main {
    public static void main(String[] args) {
        Employee e1 = new Employee(); // default
        Employee e2 = new Employee("Alice", 101); // parameterized
        Employee e3 = new Employee(e2); // copy

        e1.display();
        e2.display();
        e3.display();
    }
}

Output:

Employee: Unknown (0)
Employee: Alice (101)
Employee: Alice (101)

✅ So, in short:
A constructor is a special method-like block used to initialize an object’s state when created. It can be default, parameterized, overloaded, private, or copy. JVM ensures one constructor in the inheritance chain is always called.

What is a method?

A method in Java is a named block of code that performs a task (computes a value, updates state, does I/O). Methods are the building blocks of behavior in classes and interfaces.
Think of a method as “a function attached to a type” (object-oriented).

A method has:

  • Modifiers (public, private, static, final, abstract, synchronized, native, etc.)

  • Return type (can be any type or void)

  • Name

  • Parameter list (zero or more parameters)

  • throws clause (optional checked exceptions it may throw)

  • Body { ... } (except abstract methods, which have no body)

Why methods?

  • Reuse code (DRY)

  • Encapsulate behavior & hide implementation

  • Organize program logic into small understandable units

  • Enable polymorphism (different implementations in subclasses)

  • Allow separation of interface (what) and implementation (how)

How to declare & call a method — basic syntax

// instance method
public int add(int a, int b) {
    return a + b;
}

// static method
public static void greet(String name) {
    System.out.println("Hello, " + name);
}

Call:

MyClass obj = new MyClass();
int sum = obj.add(2,3);        // instance call
MyClass.greet("Sam");          // static call

Method components explained

  • Access modifiers: public, protected, (package-private default), private.

  • Non-access modifiers: static, final, abstract, synchronized, native, strictfp.

  • Return type: type or void.

  • Name: must be a valid identifier.

  • Parameters: typed (Type name, ...).

  • Throws: throws IOException.

  • Body: code block. abstract methods omit body.

Types of methods (common kinds)

  • Instance method — operates on an object (uses this).

  • Static method — belongs to the class, not an instance.

  • Abstract method — declared in abstract class (no body).

  • Default & static methods in interfaces (Java 8+).

  • Private methods in interfaces (Java 9+).

  • Synchronized method — acquires a lock when executing.

  • Native method — implemented in other language (C/C++) using JNI.

  • Final method — cannot be overridden.

Declaration examples (various kinds)

public class Example {
    // instance
    public int square(int x) { return x*x; }

    // static
    public static void log(String s) { System.out.println(s); }

    // synchronized
    public synchronized void increment() { /* thread-safe */ }

    // final
    public final void cannotOverride() { }

    // abstract (in abstract class)
    public abstract void doWork();

    // native
    public native long getNativeTime();

    // varargs
    public int sum(int... nums) { /* treat nums as int[] */ return 0; }
}

Parameters — call-by-value (important!)

Java is always pass-by-value:

  • For primitive parameters (e.g., int) the actual primitive value is copied.

  • For object references, the reference is copied (so both caller and method refer to the same object), but the reference itself is passed by value — reassigning that parameter inside the method does not change the caller’s reference.

Examples:

void modifyInt(int x) { x = 10; }          // caller unchanged
void modifyPerson(Person p) { p.name = "X"; } // caller's object's field changes
void reassignPerson(Person p) { p = new Person("Y"); } // caller's ref unchanged

Swap example that fails for primitives:

void swap(int a, int b) { int t=a; a=b; b=t; } // doesn't swap caller ints

But you can swap wrapped values by mutating object fields.

Varargs

Syntax Type... name (treated as array)

public void printAll(String... items) {
    for (String s : items) System.out.println(s);
}

Return types and signatures

  • The method signature in Java (for overloading) is method name + parameter types. Return type is NOT part of the signature. So you cannot overload by only changing return type.

  • Overriding requires same signature (name + param types); return type can be covariant (a subtype of the parent's return type).

Example — invalid (compiler error):

int foo() { ... }
long foo() { ... } // ERROR: duplicate method foo()

Covariant return example — valid:

class A { Number get() { ... } }
class B extends A { @Override Integer get() { ... } } // OK

Overloading vs Overriding

  • Overloading: multiple methods in same class (or subclass) with same name but different parameter lists.

  • Overriding: subclass provides new implementation for inherited method with same signature.

Overloading resolution (compile-time) considers types, boxing, widening and varargs — it can be subtle and sometimes ambiguous.

Example of overriding (polymorphism / dynamic dispatch):

class Animal { void speak() { System.out.println(".."); } }
class Dog extends Animal { @Override void speak() { System.out.println("woof"); } }

Animal a = new Dog();
a.speak(); // prints "woof" — runtime dispatch to Dog.speak()

Notes:

  • private and static methods are not polymorphic — they cannot be overridden (static methods are hidden).

  • Overriding method cannot reduce visibility (public -> private is illegal).

  • Checked-exception rule: overriding method cannot throw broader checked exceptions.

Exceptions in method signature

  • Use throws for checked exceptions.
public void read() throws IOException { /* may throw */ }
  • throw actually throws an exception instance inside the method.

  • Overriding rule: subclass method can't declare new checked exceptions not declared in the parent (unless they are subclasses).

Constructors vs Methods

  • Constructors have no return type and must match class name. They initialize objects.

  • Constructors are invoked using new and are not inherited; they use invokespecial at JVM level.

The main method

Java program entry point:

public static void main(String[] args) { /* ... */ }
// or
public static void main(String... args) { /* ... */ }

The JVM looks for public static void main(String[]). If not present (or signature differs), you get NoSuchMethodError when launching.

Generics & generic methods

You can declare methods with their own type parameters:

public static <T> T pickFirst(T[] arr) { return arr.length>0 ? arr[0] : null; }

Lambdas & method references (Java 8+)

Lambdas implement a functional interface (single abstract method).

List<String> l = List.of("a","b");
l.sort(String::compareToIgnoreCase); // method reference
Consumer<String> c = s -> System.out.println(s); // lambda
Supplier<Date> sd = Date::new; // constructor reference

Reflection & invoking methods at runtime

You can discover and invoke methods using java.lang.reflect.Method:

Method m = MyClass.class.getMethod("greet", String.class);
m.invoke(myInstance, "Alex"); // may throw InvocationTargetException, IllegalAccessException

Reflection can access private methods with setAccessible(true) (subject to security/module rules).

JVM-level details (brief, but important)

  • The JVM stores methods as method_info structures in the class file. Each has a descriptor describing parameter and return types: e.g. (ILjava/lang/String;)V means (int, String)->void.

  • Invocation bytecodes:

    • invokestatic — static methods

    • invokevirtual — most instance method calls (virtual dispatch)

    • invokeinterface — interface method calls

    • invokespecial — private, constructors, super calls

    • invokedynamic — used for lambdas and dynamic call sites (bootstrap mechanism)

  • Method call creates a stack frame: local variables and operand stack. Recursion uses frames; deep recursion → StackOverflowError.

  • The JVM imposes a limit on method code size: the Code attribute of a method in a class file must be ≤ 65535 bytes. (If your method exceeds that, you must split it.)

  • The compiler sometimes emits bridge methods (synthetic methods) to support generics/covariant returns and maintain binary compatibility after type erasure.

  • The JIT compiler can inline frequently used small methods for performance; final/private make inlining easier but JVM does aggressive optimizations anyway.

Concurrency-related method behavior

  • synchronized method locks this for instance methods, or the Class object for static synchronized methods.

  • Consider locking granularity — synchronized on smaller objects when possible.

  • Use java.util.concurrent.locks.Lock for more flexible behavior.

Annotations on methods

You can annotate methods: @Override, @Deprecated, custom annotations, @SafeVarargs (for varargs + generics), etc. Retention policy determines runtime visibility.

Common pitfalls & gotchas

  • Return type not part of signature — can't overload only by return type.

  • Calling instance method on null => NullPointerException:

      MyClass x = null;
      x.instanceMethod(); // NPE at runtime
    

    You can call a static method through a null reference (compiles), but it's strongly discouraged:

      MyClass m = null;
      m.staticMethod(); // works — resolved as MyClass.staticMethod()
    
  • Overloading ambiguity with autoboxing and varargs:

      void f(int x) {}
      void f(Integer x) {} // ambiguous if you pass null
    
  • Recursion without base-case → StackOverflowError.

  • Large methods: hard to read and may exceed bytecode size limit; split into smaller methods.

  • Exposure of internals: returning internal mutable objects exposes state.

Best practices

  • Keep methods small and single-purpose (SRP).

  • Use clear verb-based names: calculateTax, isEmpty, loadFromFile.

  • Prefer immutability / avoid side effects when possible.

  • Validate parameters early (Objects.requireNonNull).

  • Document behavior, especially for nullability and thread-safety.

  • Prefer Optional<T> for "no value" result when appropriate (API choice).

  • Use interfaces and default methods for evolving APIs carefully.

  • Use final for parameters and methods where it improves clarity.

Quick cheat-sheet (summary)

  • Signature = name + parameter types (not return)

  • Invocation opcodes:

    • invokestatic — static

    • invokevirtual — normal instance (virtual dispatch)

    • invokeinterface — interface methods

    • invokespecial — private/constructors/super

    • invokedynamic — lambdas/dynamic

  • Pass-by-value always — object references are copied, not the object

  • Overloading resolved at compile time; overriding resolved at runtime

  • JVM method code size ≤ 65535 bytes

  • Covariant returns allowed for overriding

  • Overriding cannot reduce visibility, cannot add broader checked exceptions

Practical examples (short)

Overloading:

void print(Integer i) { System.out.println("Integer"); }
void print(int i)     { System.out.println("int"); }

print(5);      // prints "int"
print(null);   // compile-time error: ambiguous between Integer and int? (null matches reference type)

Overriding & exceptions:

class Parent { void m() throws IOException {} }
class Child extends Parent {
    @Override
    void m() throws FileNotFoundException { } // OK: narrower checked exception
}

Reflection:

import java.lang.reflect.*;
Method m = String.class.getMethod("toUpperCase");
String s = (String) m.invoke("hello"); // returns "HELLO"

Lambda & method ref:

List<String> names = List.of("bob","Ann");
names.sort(String::compareToIgnoreCase);
Runnable r = () -> System.out.println("hi");

This is extensive but not absolutely exhaustive (there are JVM implementation details, compiler flags, and historical corner-cases I didn’t expand into pages of bytecode). If you want I can:

  • give a printable one-page cheat-sheet,

  • provide interactive examples / exercises,

  • show how the JVM compiles a method to bytecode and explain the method_info entries,

  • or deep-dive into method invocation bytecode and JIT inlining behavior with examples.

Which of those next would be most useful to you?

You said:

What is constuctor also give indetails

ChatGPT said:

Perfect — let’s go in detail about Constructors in Java (same as I did for methods).
I’ll cover what, why, how, when, types, rules, JVM-level details, pitfalls, and best practices with examples.


🔹 What is a Constructor?

A constructor in Java is a special method-like block used to initialize objects when they are created using the new keyword.

Key differences from methods:

  • Same name as the class.

  • No return type (not even void).

  • Automatically called by JVM when an object is created.

  • Purpose: set initial state of an object.


🔹 Why do we need Constructors?

  • To provide default values to object fields.

  • To allow different ways of initializing objects (overloading constructors).

  • To enforce rules/validation at the time of object creation.

  • To hide object creation logic (with private constructors in Singleton / Factory pattern).


🔹 How to declare and use a Constructor

class Car {
    String brand;
    int speed;

    // Constructor
    Car(String b, int s) {
        brand = b;
        speed = s;
    }
}

public class Main {
    public static void main(String[] args) {
        Car c = new Car("Tesla", 100); // Constructor called here
        System.out.println(c.brand + " runs at " + c.speed);
    }
}

Output:

Tesla runs at 100

🔹 Types of Constructors

1. Default constructor (no-arg, provided by compiler if none exists)

class Bike {
    Bike() {  // default constructor
        System.out.println("Bike is created");
    }
}

If no constructor is written, Java creates a hidden default constructor:

Bike() { super(); }

2. No-arg constructor (explicitly written by programmer)

class Bike {
    Bike() {
        System.out.println("Custom no-arg constructor");
    }
}

3. Parameterized constructor

class Student {
    String name;
    int age;

    Student(String n, int a) {
        name = n;
        age = a;
    }
}

4. Constructor overloading

Multiple constructors in the same class with different parameter lists.

class Person {
    String name;
    int age;

    Person() { name = "Unknown"; age = 0; }
    Person(String n) { name = n; age = 18; }
    Person(String n, int a) { name = n; age = a; }
}

5. Copy constructor (Java doesn’t provide it, but you can write one)

class Point {
    int x, y;
    Point(int a, int b) { x=a; y=b; }
    Point(Point p) { x = p.x; y = p.y; } // copy constructor
}

6. Private constructor

  • Used in Singleton pattern or Factory methods.
class Singleton {
    private static Singleton instance;
    private Singleton() {} // private constructor

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

🔹 Rules about Constructors

  1. Name must match class name.

  2. Cannot have return type (not even void).

  3. Can have modifiers: public, protected, private, default.

  4. Can call another constructor using this(...).

  5. First line of a constructor must be:

    • this(...) (to call another constructor of the same class), or

    • super(...) (to call parent constructor).
      If not written, compiler inserts super(); automatically.

  6. Constructors are not inherited.

  7. Can be overloaded but not overridden.

  8. Can throw exceptions.

class Test {
    Test() throws IOException { /* ... */ }
}

🔹 Constructor Chaining

Within the same class — use this()

class A {
    A() {
        this(10); // calls parameterized constructor
        System.out.println("Default constructor");
    }
    A(int x) {
        System.out.println("Parameterized constructor: " + x);
    }
}

With parent class — use super()

class Parent {
    Parent() { System.out.println("Parent constructor"); }
}
class Child extends Parent {
    Child() {
        super(); // calls Parent constructor
        System.out.println("Child constructor");
    }
}

🔹 Constructor vs Method

FeatureConstructorMethod
NameSame as classAny name (identifier rules)
Return typeNone (not even void)Must have return type
Called byJVM automatically via newExplicitly by user
PurposeInitialize objectDefine object behavior
InheritanceNot inheritedInherited
OverloadingAllowedAllowed
OverridingNot allowedAllowed

🔹 JVM-level Details

  • When you call new Car(), the JVM does:

    1. Allocate memory for object on heap.

    2. Initialize fields with default values (0, null, false).

    3. Call the constructor (using invokespecial bytecode).

    4. Return the reference to the object.

  • Every constructor must call a parent constructor (super()), directly or indirectly.

  • If no constructor exists in parent, compiler inserts super() call to Object().


🔹 Common Pitfalls

  1. Forgetting to initialize fields (constructor should set them).

  2. Using heavy logic inside constructors (bad practice → slow object creation).

  3. Recursive constructor calls → compilation error.

     class A {
         A() { this(); } // ERROR: recursive constructor invocation
     }
    
  4. Private constructor without factory → object creation impossible.

  5. Not handling exceptions properly in constructor.

  6. Thinking constructors are methods — they are not.


🔹 Best Practices

  • Keep constructors simple (only initialization).

  • Use constructor overloading for flexibility.

  • If many parameters → prefer Builder pattern.

  • Use this(...) and super(...) wisely to avoid duplicate code.

  • Use immutability: make fields final and initialize in constructor.

  • Use @ConstructorProperties for frameworks (Spring, Jackson, etc.).


🔹 Example (Complete)

class Employee {
    String name;
    int id;

    // default constructor
    Employee() {
        this("Unknown", 0); // calls parameterized constructor
    }

    // parameterized constructor
    Employee(String n, int i) {
        name = n;
        id = i;
    }

    // copy constructor
    Employee(Employee e) {
        this.name = e.name;
        this.id = e.id;
    }

    void display() {
        System.out.println("Employee: " + name + " (" + id + ")");
    }
}

public class Main {
    public static void main(String[] args) {
        Employee e1 = new Employee(); // default
        Employee e2 = new Employee("Alice", 101); // parameterized
        Employee e3 = new Employee(e2); // copy

        e1.display();
        e2.display();
        e3.display();
    }
}

Output:

Employee: Unknown (0)
Employee: Alice (101)
Employee: Alice (101)

✅ So, in short:
A constructor is a special method-like block used to initialize an object’s state when created. It can be default, parameterized, overloaded, private, or copy. JVM ensures one constructor in the inheritance chain is always called.


Do you want me to also give you a side-by-side comparison of constructor invocation vs method invocation at JVM bytecode level (like invokespecial vs invokevirtual)? That’s a deeper dive into how the JVM actually executes constructors.

You said:

What is block in details

ChatGPT said:

Great question 👍 Let’s go in detail about Blocks in Java.
(Blocks are often overlooked, but they’re very important for scope, initialization, and readability.)


🔹 What is a Block?

A block in Java is a group of zero or more statements enclosed in curly braces { }.
It defines a scope (lifetime and visibility of variables) and can also be used to initialize data.


🔹 Types of Blocks in Java

There are mainly 4 kinds of blocks:


1. Normal (Local) Block

  • A block inside a method, loop, or control statement.

  • Used to limit variable scope and improve readability.

class Test {
    public static void main(String[] args) {
        {  // local block
            int x = 10;
            System.out.println("Inside block: " + x);
        }
        // System.out.println(x); ❌ Error: x not visible here
    }
}

✅ Use-case: free memory sooner by limiting variable scope.


2. Instance Initialization Block (IIB)

  • Declared inside a class but outside any method/constructor.

  • Runs every time an object is created, before the constructor.

  • Executed after field initializers and before constructor body.

class Example {
    int x;

    // Instance Initialization Block
    {
        x = 100;
        System.out.println("Instance block executed");
    }

    Example() {
        System.out.println("Constructor executed");
    }
}

public class Main {
    public static void main(String[] args) {
        new Example();
        new Example();
    }
}

Output:

Instance block executed
Constructor executed
Instance block executed
Constructor executed

✅ Use-case: shared initialization code among multiple constructors.


3. Static Initialization Block (SIB)

  • Declared with static { }.

  • Runs only once when the class is loaded into memory (by JVM).

  • Used to initialize static variables or perform one-time setup.

class Demo {
    static int count;

    static {
        count = 10;
        System.out.println("Static block executed");
    }

    public static void main(String[] args) {
        System.out.println("Main executed");
        System.out.println("Count = " + count);
    }
}

Output:

Static block executed
Main executed
Count = 10

✅ Use-case:

  • Load configuration (DB connections, files).

  • Initialize static variables.

  • Logging setup.


4. Synchronized Block

  • A block of code inside a method synchronized using an object lock.

  • Used for thread-safety (only one thread executes the block at a time for that object).

class Counter {
    private int count = 0;

    public void increment() {
        synchronized(this) { // lock on 'this'
            count++;
        }
    }

    public int getCount() { return count; }
}

✅ Use-case: critical sections in multi-threaded programs.


🔹 Execution Order of Blocks

For a class with static block, instance block, and constructor, the order is:

  1. Static block (runs once, when class is loaded)

  2. Instance block (runs every time before constructor)

  3. Constructor

Example:

class Order {
    static { System.out.println("1. Static block"); }
    { System.out.println("2. Instance block"); }
    Order() { System.out.println("3. Constructor"); }
}

public class Main {
    public static void main(String[] args) {
        new Order();
        new Order();
    }
}

Output:

1. Static block
2. Instance block
3. Constructor
2. Instance block
3. Constructor

🔹 Nested Blocks

Blocks can be nested within each other, creating scopes inside scopes:

class Nested {
    public static void main(String[] args) {
        int a = 10;
        {
            int b = 20;
            {
                int c = 30;
                System.out.println(a + b + c);
            }
            // c not visible here
        }
        // b not visible here
    }
}

🔹 Anonymous Block (inside objects)

You can also write anonymous inner classes with blocks:

Runnable r = new Runnable() {
    { System.out.println("Anonymous block in anonymous class"); }
    public void run() { System.out.println("Run method"); }
};
r.run();

🔹 Special Notes

  • Multiple static blocks are allowed, executed top-to-bottom when class loads.

  • Multiple instance blocks are allowed, executed top-to-bottom before constructor.

  • A static block cannot access non-static members (no object yet).

  • An instance block can access both static and instance members.

  • A synchronized block can lock on this, a class object (ClassName.class), or any custom object.


🔹 Block vs Method vs Constructor

FeatureBlock (Normal/IIB/SIB)ConstructorMethod
NameNo nameSame as classAny valid identifier
Return typeNoNoYes (or void)
ExecutionAuto by JVM (based on type)Auto by new keywordManual (explicit call)
PurposeScope, init, sync, controlObject initializationObject behavior definition
OverloadingNoYesYes
OverridingNoNoYes

🔹 Best Practices

  • Use normal/local blocks only to reduce variable scope.

  • Use instance blocks sparingly; constructors are usually clearer.

  • Use static blocks for complex static initialization only.

  • Use synchronized blocks instead of whole methods if you want fine-grained locking.

  • Keep blocks small and readable.


✅ Quick Summary

  • Block = code inside {}.

  • Normal/local block → limit scope.

  • Instance block (IIB) → runs before constructor for each object.

  • Static block (SIB) → runs once when class loads.

  • Synchronized block → thread-safety.

  • Execution order: Static block → Instance block → Constructor.

🔹 Execution Flow (when we create an object)

  1. Class Loading Phase

    • Static variables → initialized with default values.

    • Static Block(s) executed (once per class load).

  2. Object Creation Phase (new keyword)

    • Instance variables → initialized with default values.

    • Instance variable initializers (explicit assignments).

    • Instance Block(s) executed (top-to-bottom).

    • Constructor executed.


🔹 Diagram

                ┌─────────────────────┐
                │   Class Loaded       │
                │   (by JVM, once)     │
                └─────────┬───────────┘
                          │
                ┌─────────▼───────────┐
                │  Static Block(s)     │  (only once)
                └─────────┬───────────┘
                          │
                          ▼
                ┌─────────────────────┐
                │   new Object()       │
                └─────────┬───────────┘
                          │
                ┌─────────▼───────────┐
                │ Instance Vars = 0    │ (default values)
                └─────────┬───────────┘
                          │
                ┌─────────▼───────────┐
                │ Instance Initializer │ (field assignments)
                └─────────┬───────────┘
                          │
                ┌─────────▼───────────┐
                │ Instance Block(s)    │ (every time)
                └─────────┬───────────┘
                          │
                ┌─────────▼───────────┐
                │ Constructor          │
                └─────────────────────┘

🔹 Example Code to Match Flow

class Order {
    static { System.out.println("1. Static block"); }
    { System.out.println("2. Instance block"); }
    Order() { System.out.println("3. Constructor"); }
}

public class Main {
    public static void main(String[] args) {
        new Order();
        new Order();
    }
}

🔹 Output

1. Static block
2. Instance block
3. Constructor
2. Instance block
3. Constructor

✅ In short:

  • Static block → runs once (class loading).

  • Instance block → runs every object creation (before constructor).

  • Constructor → runs last, after instance blocks.

🔹 What is OOPS?

Object-Oriented Programming System (OOPS) is a programming paradigm based on the concept of objects.
Objects = entities that combine state (data/fields) and behavior (methods).

In Java, everything (except primitives) is an object.
OOP helps structure software around real-world models.


🔹 Why OOPS?

  • Models real-world entities (Car, Student, BankAccount).

  • Encapsulation of data → hides implementation.

  • Reuse through inheritance → reduces code duplication.

  • Polymorphism → flexible, extensible behavior.

  • Abstraction → focus on what, not how.

  • Maintainability & scalability for large projects.


🔹 4 Main Pillars of OOP (Core Principles)


1. Encapsulation (Data Hiding)

  • Wrapping fields + methods together in a class.

  • Fields often kept private; accessed via getters/setters.

  • Helps control how data is modified.

class Account {
    private double balance;

    public void deposit(double amt) {
        if (amt > 0) balance += amt;
    }

    public double getBalance() {
        return balance;
    }
}

✅ Why: Protects internal state, enforces rules.


2. Inheritance (Code Reuse)

  • Mechanism to acquire properties and behavior of another class.

  • extends → class inheritance, implements → interface inheritance.

class Animal {
    void eat() { System.out.println("Eating"); }
}
class Dog extends Animal {
    void bark() { System.out.println("Barking"); }
}

Usage:

Dog d = new Dog();
d.eat(); // inherited
d.bark();

✅ Why: Avoids duplicate code, establishes is-a relationship.
⚠️ Java supports single inheritance of classes, but multiple inheritance via interfaces.


3. Polymorphism (Many Forms)

Two types:

a) Compile-time polymorphism (Method Overloading)

Same method name, different parameter list.

class MathUtil {
    int add(int a, int b) { return a+b; }
    double add(double a, double b) { return a+b; }
}

b) Runtime polymorphism (Method Overriding / Dynamic Dispatch)

Subclass provides new implementation.

class Animal {
    void sound() { System.out.println("Some sound"); }
}
class Dog extends Animal {
    @Override void sound() { System.out.println("Woof"); }
}

Animal a = new Dog();
a.sound();  // Woof (decided at runtime)

✅ Why: Flexibility & extensibility.


4. Abstraction (Hiding Implementation)

  • Hides how things work, only shows what is available.

  • Achieved via abstract classes & interfaces.

abstract class Shape {
    abstract void draw(); // abstract method
}
class Circle extends Shape {
    void draw() { System.out.println("Drawing Circle"); }
}

Usage:

Shape s = new Circle();
s.draw();  // abstraction: we don’t care *how* it draws

✅ Why: Focus on essential details, ignore complexity.


🔹 OOPS Features in Java

  1. Class → blueprint for objects.

     class Car { String color; void drive() {} }
    
  2. Object → instance of a class.

     Car c = new Car();
    
  3. Inheritance → extends, implements.

  4. Polymorphism → Overloading & Overriding.

  5. Abstraction → abstract classes & interfaces.

  6. Encapsulation → data hiding via private + getter/setter.

  7. Association → relationship between classes (HAS-A).

    • Aggregation (weak HAS-A, independent objects).

    • Composition (strong HAS-A, dependent objects).

    class Engine {}
    class Car { Engine e = new Engine(); } // composition
  1. Message Passing → objects communicate by calling methods.

🔹 Special OOP Concepts in Java

a) Constructors (object initialization)

  • Default, parameterized, copy constructors.

b) this keyword

  • Refers to current object.

  • Used for disambiguation, constructor chaining.

c) super keyword

  • Refers to parent class.

  • Used for parent constructor/method/field access.

d) Method Overloading vs Overriding

  • Overloading → compile-time (same name, diff params).

  • Overriding → runtime (same signature, subclass implementation).

e) final keyword

  • final class → cannot be extended.

  • final method → cannot be overridden.

  • final variable → constant.

f) abstract vs interface

  • Abstract class → can have both abstract & concrete methods.

  • Interface → pure abstraction (Java 8+ allows default & static methods).

g) Object Class

  • Superclass of all classes in Java.

  • Methods: toString(), equals(), hashCode(), clone(), finalize().

h) Inner Classes

  • Member inner, static nested, local inner, anonymous inner.

🔹 OOPS at JVM Level

  • Objects → stored in Heap.

  • References → stored in Stack.

  • Method calls → handled via:

    • invokevirtual → polymorphic instance method calls.

    • invokestatic → static methods.

    • invokespecial → constructors, private, super methods.

    • invokeinterface → interface methods.

  • Dynamic dispatch (polymorphism) → JVM decides method at runtime by looking at the actual object.


🔹 Pitfalls / Misconceptions

  1. Java doesn’t support multiple inheritance of classes (diamond problem) → use interfaces.

  2. Constructors are not methods (no return type, not inherited).

  3. Overloading ≠ Overriding.

  4. Encapsulation ≠ Abstraction → Encapsulation hides data, Abstraction hides implementation.

  5. Java is not 100% OOP (because of primitives).


🔹 Best Practices in OOP (Java)

  • Follow SOLID principles:

    • Single Responsibility Principle

    • Open/Closed Principle

    • Liskov Substitution Principle

    • Interface Segregation Principle

    • Dependency Inversion Principle

  • Use composition over inheritance when possible.

  • Keep fields private and expose through methods.

  • Keep classes small and cohesive.

  • Favor interfaces and abstractions for loose coupling.

  • Avoid deep inheritance chains (hard to maintain).

  • Use design patterns (Singleton, Factory, Builder, Strategy, Observer).


🔹 Quick Example — OOP in Action

// Abstraction + Inheritance + Polymorphism + Encapsulation
abstract class Shape {
    private String color;  // encapsulation
    Shape(String color) { this.color = color; }
    public String getColor() { return color; }
    abstract double area();  // abstraction
}

class Circle extends Shape {
    private double radius;
    Circle(String color, double r) {
        super(color); this.radius = r;
    }
    @Override double area() { return Math.PI * radius * radius; }
}

class Rectangle extends Shape {
    private double w, h;
    Rectangle(String color, double w, double h) {
        super(color); this.w = w; this.h = h;
    }
    @Override double area() { return w * h; }
}

public class Main {
    public static void main(String[] args) {
        Shape s1 = new Circle("Red", 5);     // polymorphism
        Shape s2 = new Rectangle("Blue", 4, 6);

        System.out.println(s1.getColor() + " Circle Area: " + s1.area());
        System.out.println(s2.getColor() + " Rectangle Area: " + s2.area());
    }
}

Output:

Red Circle Area: 78.53981633974483
Blue Rectangle Area: 24.0

✅ In short:
Java OOP = Classes + Objects + 4 Pillars (Encapsulation, Inheritance, Polymorphism, Abstraction) + additional features (association, composition, message passing). It models real-world entities, makes code reusable, scalable, and maintainable.

🔹 OOPS 4 Pillars Diagram

                ┌─────────────────────────┐
                │   OOPS (Java)           │
                │   Object-Oriented       │
                │   Programming System    │
                └───────────┬────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ▼                   ▼                   ▼
 ┌───────────────┐   ┌───────────────┐   ┌───────────────┐
 │ Encapsulation │   │ Inheritance   │   │ Polymorphism  │
 │ (Data Hiding) │   │ (Code Reuse)  │   │ (Many Forms)  │
 └───────┬───────┘   └───────┬───────┘   └───────┬───────┘
         │                   │                   │
   Private fields +      `extends` /          a) Compile-time
   Getter/Setter         `implements`         (Overloading)
   Example:              Example:             b) Runtime
   Account balance       Dog extends          (Overriding)
                         Animal
        │                   │                   │
        └─────────────┬─────┴───────────────┐
                      │                     │
                      ▼                     ▼
             ┌────────────────────┐  ┌───────────────┐
             │   Abstraction      │  │   Others      │
             │ (Hides Impl.)      │  │ Assoc/Comp.   │
             └────────────────────┘  │ Message Pass  │
                   │                  └───────────────┘
       Abstract class / Interface
       Example: Shape → Circle/Rect

🔹 One-line memory trick

👉 “Encapsulation hides, Inheritance reuses, Polymorphism flexes, Abstraction simplifies.”


🔹 Quick Example Mapping

  • Encapsulation → private balance in Account.

  • Inheritance → Dog extends Animal.

  • Polymorphism → sound() overridden in Dog.

  • Abstraction → Shape as abstract class, concrete subclasses.

🚗 OOPS Pillars → Car Analogy

                 ┌────────────────────────────┐
                 │        CAR (Object)        │
                 └───────────┬────────────────┘
                             │
   ┌─────────────────────────┼─────────────────────────┐
   │                         │                         │
   ▼                         ▼                         ▼
┌───────────────┐     ┌───────────────┐         ┌───────────────┐
│ Encapsulation │     │ Inheritance   │         │ Polymorphism  │
│ (Hide Engine) │     │ Car IS-A      │         │ Drive works   │
│ Engine parts  │     │ Vehicle       │         │ differently   │
│ are private   │     │ Truck, Bike   │         │ Auto vs Manual│
│ but Start()   │     │ also extend   │         │ Overloading & │
│ is public     │     │ Vehicle       │         │ Overriding    │
└───────────────┘     └───────────────┘         └───────────────┘
                             │
                             ▼
                      ┌───────────────┐
                      │ Abstraction   │
                      │ CarInterface  │
                      │ start(), stop()│
                      │ driver uses it │
                      │ no idea of impl│
                      └───────────────┘

🔹 Explanation with Car Example

  1. Encapsulation (Data Hiding)

    • A car’s engine is hidden inside.

    • You can’t directly access pistons or fuel injection system.

    • Instead, you use public methods like startCar(), accelerate().

    • 👉 In Java: private variables + getters/setters.

  2. Inheritance (Code Reuse)

    • Car is a subclass of Vehicle.

    • Other vehicles like Truck, Bike also inherit Vehicle.

    • Common features (wheels, fuel, move()) come from Vehicle.

    • 👉 In Java: class Car extends Vehicle.

  3. Polymorphism (Many Forms)

    • drive() works differently:

      • Manual Car → gear + clutch.

      • Automatic Car → no clutch.

    • start() could be:

      • Key ignition.

      • Push button.

    • 👉 In Java: Overloading (start(int key) vs start(String keyless)) and Overriding (Car.start() vs Truck.start()).

  4. Abstraction (Hiding Implementation)

    • As a driver, you only know what to do (press accelerator, brake).

    • You don’t care how engine burns fuel internally.

    • 👉 In Java: interface Vehicle { void start(); void stop(); }.


🔹 Quick Java Mapping with Car

// Abstraction
interface Vehicle {
    void start();
    void stop();
}

// Inheritance + Polymorphism
class Car implements Vehicle {
    private Engine engine;  // Encapsulation

    Car() { engine = new Engine(); }

    @Override
    public void start() {
        System.out.println("Car starting with button ignition.");
    }

    @Override
    public void stop() {
        System.out.println("Car stopping.");
    }
}

class Truck implements Vehicle {
    @Override
    public void start() {
        System.out.println("Truck starting with heavy-duty ignition.");
    }

    @Override
    public void stop() {
        System.out.println("Truck stopping with air brakes.");
    }
}

// Encapsulated class
class Engine {
    private int horsepower = 120;
    void ignite() { System.out.println("Engine ignited with " + horsepower + " HP"); }
}

public class Main {
    public static void main(String[] args) {
        Vehicle v1 = new Car();   // Polymorphism
        Vehicle v2 = new Truck();

        v1.start(); // Car's implementation
        v2.start(); // Truck's implementation
    }
}

Output:

Car starting with button ignition.
Truck starting with heavy-duty ignition.

✅ With this Car analogy, you can easily explain OOP in interviews in a way that’s simple, memorable, and practical.

More from this blog

Shreenibas Samal's blog

49 posts