• HINDI
  •    
  • Saturday, 17-Jan-26 04:37:13 IST
Tech Trending :
* 🤖How OpenAI + MCP Servers Can Power the Next Generation of AI Agents for Automation * 📚 Book Recommendation System Using OpenAI Embeddings And Nomic Atlas Visualization

Core Java Interview Questions

Contents

Table of Contents

    Contents
    Core Java Interview Questions

    Core Java Interview Questions

    🧾 What Is the Default toString() Implementation in Java?

    In Java, every class inherits the toString() method from the Object class.
    If we don’t override this method in our own class, the default implementation provided by Object is used.

    By default, the toString() method returns a string that includes:

    <ClassName>@<Hexadecimal representation of the objects memory address>

    This means:

    • The class name of the object.

    • Followed by the @ symbol.

    • And finally, a hexadecimal value representing the object’s memory reference (derived from its hash code).


    🧠 Example

    class Car {} public class ToStringExample { public static void main(String[] args) { Car car = new Car(); System.out.println(car.toString()); } }

    Output:

    Car@5e91993f

    Here,

    • Car → is the class name.

    • @ → separates the name and hash.

    • 5e91993f → is the hexadecimal form of the object’s hash code.


    ✍️ Overriding toString()

    If you override the toString() method, you can provide a custom, human-readable description of your object:

    class Car { String model = "Honda"; int year = 2024; @Override public String toString() { return "Car [model=" + model + ", year=" + year + "]"; } } public class Main { public static void main(String[] args) { Car car = new Car(); System.out.println(car); } }

    Output:

    Car [model=Honda, year=2024]

    In short:
    The default toString() method returns the class name, followed by @, and the hexadecimal hash code of the object’s memory reference.
    When overridden, it can display custom object details, making debugging and logging more meaningful.

    ⚖️ equals() Method vs. == Operator in Java

    This is one of the most commonly asked interview questions in Java — and understanding the difference between == and equals() is crucial when comparing objects.


    💡 The == Operator

    • The == operator is used to compare references (memory locations) of two objects.

    • It checks whether both variables point to the same object in memory.

    • Hence, it performs a shallow comparison.

    🧠 Example

    class User { int id; String name; User(int id, String name) { this.id = id; this.name = name; } } public class EqualsVsDoubleEquals { public static void main(String[] args) { User u1 = new User(1, "John"); User u2 = new User(1, "John"); System.out.println(u1 == u2); // false } }

    Explanation:
    Even though both objects contain the same data, u1 and u2 occupy different memory locations, so == returns false.


    💡 The equals() Method

    • The equals() method is defined in the Object class, so every Java class inherits it.

    • By default, its implementation in Object also performs a reference comparison (same as ==).

    • Therefore, if you don’t override it, u1.equals(u2) will also return false in the above example.

    To perform a deep comparison (compare contents, not references), you must override equals() in your class.

    ✍️ Overriding equals()

    @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; User user = (User) obj; return id == user.id && name.equals(user.name); }

    Now, comparing two objects with the same data will return true.


    🧩 Special Cases — Strings, Wrappers, and Enums

    There are exceptions where equals() is already overridden in the standard library classes to perform content-based (deep) comparison:

    ✅ Example with String

    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 (content is same)

    ✅ Example with Wrapper Types

    Integer i1 = new Integer(123); Integer i2 = new Integer(123); System.out.println(i1 == i2); // false (different objects) System.out.println(i1.equals(i2)); // true (compares values)

    ✅ Example with Enums

    enum Day { MONDAY, TUESDAY } System.out.println(Day.MONDAY == Day.MONDAY); // true System.out.println(Day.MONDAY.equals(Day.MONDAY)); // true

    Enums are unique instances, so both comparisons return true.


    🧠 Summary

    Aspect== Operatorequals() Method
    Type of ComparisonReference comparisonContent (deep) comparison (if overridden)
    Default BehaviorCompares memory locationsSame as == (unless overridden)
    Can Be Overridden?❌ No✅ Yes
    Used WithPrimitives and objectsObjects only
    Common UseCheck if two references point to the same objectCheck if two objects have equal content


    ✅ In short:

    • == → compares object references (memory locations).

    • equals() → compares object contents, but only if overridden.

    • For Strings, Wrappers, and Enumsequals() is already overridden to perform deep comparison.


    🔑 Difference Between final, finally, and finalize in Java

    Although they sound similar, final, finally, and finalize serve completely different purposes in Java.
    Let’s break them down one by one 👇


    🧱 final — Keyword

    The final keyword is used to apply restrictions on variables, methods, and classes.

    1. Final Variables:
      Once assigned, their value cannot be changed — they act as constants.

      final int i = 123; // i = 200; // ❌ Compile-time error
    2. Final Objects:
      The reference to the object cannot change, but the object’s internal state can still be modified.

      final StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); // ✅ allowed // sb = new StringBuilder("Hi"); // ❌ not allowed
    3. Final Methods:
      A method marked as final cannot be overridden by subclasses.

      class Parent { final void display() { System.out.println("Parent"); } } class Child extends Parent { // void display() {} // ❌ Compile-time error }
    4. Final Classes:
      A final class cannot be inherited.

      final class A {} // class B extends A {} // ❌ Compile-time error

    ⚙️ finally — Block

    The finally block is used in exception handling to execute important cleanup code, such as closing database connections, releasing resources, or shutting down streams.

    • The finally block always executes, whether an exception occurs or not.

    • It can be used with or without a catch block.

    try { int result = 10 / 0; } catch (ArithmeticException e) { System.out.println("Exception caught"); } finally { System.out.println("Finally block executed"); }

    ✅ Output:

    Exception caught Finally block executed

    Even if no exception occurs, the finally block still runs.


    💡 Try-with-Resources (Java 7+)

    Since Java 7, we have the try-with-resources statement that automatically closes any resources (like files or connections) declared inside the parentheses.

    try (FileReader reader = new FileReader("data.txt")) { // use file } catch (IOException e) { e.printStackTrace(); } // No need for finally block — resource auto-closed

    So, when using try-with-resources, an explicit finally block for closing resources is often unnecessary.


    🧹 finalize() — Method

    The finalize() method is part of the Object class.
    It is called by the Garbage Collector (GC) before reclaiming the memory of an object.

    @Override protected void finalize() throws Throwable { System.out.println("Finalize method called before GC"); }

    However, you should not rely on finalize() for resource cleanup because:

    • There’s no guarantee when or even if it will be called.

    • It can cause performance issues.

    • It’s deprecated in modern Java (from Java 9 onward).

    Instead, always release resources manually or use try-with-resources.


    🧠 Summary

    Keyword / MethodTypePurposeCan It Be Overridden / Guaranteed?
    finalModifierPrevent modification or inheritance (variables, methods, classes)N/A
    finallyBlockExecutes cleanup code in exception handlingAlways executes
    finalize()MethodCalled by GC before object destructionNot guaranteed, deprecated



    ✅ In short:

    • final → Used to restrict modification.

    • finally → Used for cleanup in exception handling.

    • finalize() → Used by GC before object deletion, but should be avoided in modern Java.


    🧠 What Are Generics in Java?

    Generics were introduced in Java 5 to enable type-safe and reusable code.
    They allow you to specify the type of objects that a class, method, or collection can work with — ensuring compile-time type checking and reducing runtime errors.


    💡 Problem Before Generics

    Before Java 5, collections could hold any type of object, because their types were not defined.

    For example:

    List employeeIds = new ArrayList(); employeeIds.add(101); employeeIds.add("John"); // ❌ No compile-time error!

    This was allowed, but when retrieving data, you had to cast values manually, which could lead to ClassCastException at runtime.

    Integer id = (Integer) employeeIds.get(1); // Runtime error: ClassCastException

    ✅ Solution: Using Generics

    With Generics, you can declare the data type a collection can store:

    List<Integer> employeeIds = new ArrayList<>(); employeeIds.add(101); employeeIds.add("John"); // ❌ Compile-time error

    Now, only integers are allowed — and type mismatches are caught at compile time instead of runtime.

    Generics bring:

    • Type safety – prevent adding incompatible types.

    • Elimination of casting – no need to manually cast objects.

    • Cleaner, more maintainable code.


    🧩 Example

    List<String> names = new ArrayList<>(); names.add("Alice"); names.add("Bob"); for (String name : names) { System.out.println(name.toUpperCase()); }

    Here, each element is guaranteed to be a String, so there’s no need for casting.


    ⚙️ What Is Type Erasure?

    Type Erasure is how Java implements Generics — it ensures backward compatibility with older versions (pre-Java 5).

    At compile time, the compiler checks type safety using the generic types you specify.
    However, at runtime, the generic type information is erased — meaning the JVM only sees raw types (like List instead of List<Integer>).

    This ensures that older code (from Java 1.4 or 1.3) can still run on newer JVMs.


    🧠 Example of Type Erasure

    List<Integer> ids = new ArrayList<>(); ids.add(10); // After type erasure, JVM sees it as: List ids = new ArrayList(); // Generic info removed

    So, generics in Java exist only at compile time — they do not exist at runtime.


    🧾 Summary

    ConceptDescription
    GenericsAllow specifying a type parameter for classes, methods, or collections to ensure compile-time type safety.
    Type ErasureThe process by which the compiler removes all generic type information during compilation for backward compatibility.
    Runtime GenericsNot supported in Java — generics work only at compile time.

    In short:

    • Generics make your code type-safe and reusable.

    • Type Erasure removes generic type info at runtime to maintain compatibility with older Java versions.

    • Java’s generics are compile-time only, ensuring safety before the code runs.