DEV Community

Cover image for Java Core Mastery Part 1: From Zero to FAANG Ready πŸš€
Rajat Parihar
Rajat Parihar

Posted on

Java Core Mastery Part 1: From Zero to FAANG Ready πŸš€

The ultimate deep-dive guide for mastering Java fundamentals with in-depth explanations and practical examples

Java

πŸ“‹ Table of Contents


🎯 Introduction

Java is one of the most popular programming languages, powering everything from Android apps to enterprise systems. Understanding Java deeply is crucial for building robust applications and succeeding in technical interviews.

This comprehensive guide will take you from basics to advanced concepts with clear explanations and practical examples.

Who This Guide Is For:

  • Developers preparing for technical interviews
  • Beginners wanting to understand Java fundamentals
  • Intermediate developers looking to fill knowledge gaps
  • Anyone needing a comprehensive Java reference

What Makes This Different:

βœ… In-depth explanations with real-world context

βœ… Memory diagrams and visual representations

βœ… Common pitfalls and best practices

βœ… Performance considerations

βœ… Code examples you can run immediately

βœ… Practice questions to test your understanding

Reading Time: 45-60 minutes

Difficulty: Beginner to Advanced


πŸ—οΈ Understanding Java's Architecture

The Big Three: JVM, JRE, JDK

Let me start with a question that's often asked: junior interview:

**Explain the difference between JVM, JRE, and JDK."

Let's break this down. Here's a comprehensive answer:

JVM (Java Virtual Machine)

Simple Answer: The runtime engine that executes Java bytecode.

Deep Answer: The JVM is an abstract computing machine with three key responsibilities:

  1. Loading - Reads .class files
  2. Verification - Ensures bytecode is valid and safe
  3. Execution - Runs the bytecode using an interpreter or JIT compiler
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           JVM ARCHITECTURE          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Class Loader Subsystem             β”‚
β”‚  β”œβ”€ Bootstrap ClassLoader           β”‚
β”‚  β”œβ”€ Extension ClassLoader           β”‚
β”‚  └─ Application ClassLoader         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Runtime Data Areas                 β”‚
β”‚  β”œβ”€ Method Area (Metadata)          β”‚
β”‚  β”œβ”€ Heap (Objects)                  β”‚
β”‚  β”œβ”€ Stack (Method calls)            β”‚
β”‚  β”œβ”€ PC Registers                    β”‚
β”‚  └─ Native Method Stack             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Execution Engine                   β”‚
β”‚  β”œβ”€ Interpreter                     β”‚
β”‚  β”œβ”€ JIT Compiler                    β”‚
β”‚  └─ Garbage Collector               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Key Key Insight: The JVM is platform-independent but implementation-specific. Different vendors (Oracle, OpenJDK, GraalVM) have different JVM implementations.

JRE (Java Runtime Environment)

Simple Answer: JVM + Libraries needed to run Java applications.

Deep Answer:

JRE = JVM + Core Libraries (java.lang, java.util, etc.) + Configuration files
Enter fullscreen mode Exit fullscreen mode

The JRE provides:

  • Class libraries (rt.jar, charsets.jar)
  • Property files (default character encodings)
  • Security policies
  • DLL files (Windows) or SO files (Linux)

Common Trap Question: "Can you develop Java applications with only JRE?"

Explanation: No. JRE is for running applications. You need JDK for development.

JDK (Java Development Kit)

Simple Answer: JRE + Development tools.

Deep Answer:

JDK = JRE + Development Tools + Compiler (javac) + Debugger + JavaDoc
Enter fullscreen mode Exit fullscreen mode

The JDK includes:

  • javac - Compiler
  • java - Launcher
  • javadoc - Documentation generator
  • jar - Archive tool
  • jdb - Debugger
  • jconsole - Monitoring tool

Relationship:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              JDK                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚           JRE                 β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚  β”‚  β”‚        JVM              β”‚ β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

How Java Actually Works

**Walk me through what happens when you run java HelloWorld."

Here's the complete flow:

Step 1: Write Code

// HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Compilation

javac HelloWorld.java
Enter fullscreen mode Exit fullscreen mode

What happens internally:

  1. Lexical Analysis - Breaks code into tokens
  2. Syntax Analysis - Checks grammar
  3. Semantic Analysis - Type checking
  4. Bytecode Generation - Creates .class file

Key Point: Java bytecode is platform-independent. The same .class file runs on Windows, Linux, Mac.

Step 3: Execution

java HelloWorld
Enter fullscreen mode Exit fullscreen mode

What happens internally:

  1. Class Loading
   Bootstrap ClassLoader β†’ Loads core Java classes (java.lang.*)
   Extension ClassLoader β†’ Loads extension classes (javax.*)
   Application ClassLoader β†’ Loads our HelloWorld.class
Enter fullscreen mode Exit fullscreen mode
  1. Bytecode Verification

    • Stack overflow/underflow checks
    • Type safety verification
    • Access control checks
  2. Execution

    • Interpreter reads bytecode line by line
    • JIT compiler converts hot code to native machine code
    • Garbage collector manages memory

Memory Layout During Execution:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            HEAP                     β”‚
β”‚  "Hello, World!" String object      β”‚
β”‚  System object                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            STACK                    β”‚
β”‚  main() method frame                β”‚
β”‚  β”œβ”€ args: String[]                  β”‚
β”‚  └─ local variables                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        METHOD AREA                  β”‚
β”‚  HelloWorld class metadata          β”‚
β”‚  main() method bytecode             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Common Question: "Why is Java called 'Platform Independent but Platform Dependent'?"

Explanation:

  • Platform Independent: Bytecode runs on any platform with JVM
  • Platform Dependent: JVM itself is platform-specific (different JVM for Windows/Linux/Mac)

πŸ“Š Data Types & Variables

The Two Categories

**Explain primitive vs non-primitive types. Why does Java have both?"

Primitive Types (Stored in Stack)

Java has 8 primitive types. Let me show you the complete picture:

Type Size Range Default Wrapper Class
byte 8-bit -128 to 127 0 Byte
short 16-bit -32,768 to 32,767 0 Short
int 32-bit -2Β³ΒΉ to 2Β³ΒΉ-1 0 Integer
long 64-bit -2⁢³ to 2⁢³-1 0L Long
float 32-bit IEEE 754 0.0f Float
double 64-bit IEEE 754 0.0d Double
char 16-bit 0 to 65,535 '\u0000' Character
boolean 1-bit* true/false false Boolean

Important Note: boolean size is JVM-dependent. It's treated as int (4 bytes) in arrays, but 1 byte standalone.

Deep Dive: Integer Types

public class IntegerTypes {
    public static void main(String[] args) {
        // Byte
        byte b = 127;
        // byte b2 = 128;  // ❌ Compilation error: out of range

        // Integer literals
        int decimal = 100;
        int binary = 0b1100100;   // Binary (Java 7+)
        int octal = 0144;         // Octal
        int hex = 0x64;           // Hexadecimal

        // Underscores for readability (Java 7+)
        int million = 1_000_000;
        long creditCard = 1234_5678_9012_3456L;

        // Type promotion
        byte x = 10;
        byte y = 20;
        // byte z = x + y;  // ❌ Error: result is int
        int z = x + y;      // βœ… Correct

        System.out.println("All equal: " + (decimal == binary && binary == octal && octal == hex));
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Mistake: "Why does byte x = 10; byte y = 20; byte z = x + y; fail to compile?"

Explanation: In Java, operations on types smaller than int are promoted to int. So x + y returns an int, which cannot be assigned to byte without explicit casting.

Floating-Point Types

public class FloatingPoint {
    public static void main(String[] args) {
        // Float requires 'f' suffix
        float f1 = 3.14f;
        // float f2 = 3.14;  // ❌ Error: double cannot be converted to float

        // Double is default for decimal literals
        double d1 = 3.14;
        double d2 = 3.14d;  // 'd' is optional

        // Scientific notation
        double scientist = 1.23e5;  // 123000.0

        // Special values
        System.out.println("Positive Infinity: " + (1.0 / 0.0));
        System.out.println("Negative Infinity: " + (-1.0 / 0.0));
        System.out.println("NaN: " + (0.0 / 0.0));

        // Precision issues (CRITICAL FOR INTERVIEWS)
        System.out.println(0.1 + 0.2);  // 0.30000000000000004
        System.out.println(0.1 + 0.2 == 0.3);  // false ⚠️

        // Solution: Use BigDecimal for financial calculations
    }
}
Enter fullscreen mode Exit fullscreen mode

Critical Question: Point: Never use float or double for financial calculations due to precision errors. Use BigDecimal instead.

Character Type

public class CharacterType {
    public static void main(String[] args) {
        // Different ways to declare char
        char c1 = 'A';
        char c2 = 65;        // ASCII value
        char c3 = '\u0041';  // Unicode

        System.out.println(c1 == c2 && c2 == c3);  // true

        // Char is essentially a 16-bit unsigned integer
        char ch = 'A';
        System.out.println(ch + 1);  // 66 (promoted to int)
        System.out.println((char)(ch + 1));  // 'B'

        // Unicode support
        char emoji = 'πŸ˜€';  // Works in Java!
        System.out.println("Emoji: " + emoji);
    }
}
Enter fullscreen mode Exit fullscreen mode

Boolean Type

public class BooleanType {
    public static void main(String[] args) {
        boolean flag = true;

        // ❌ These DON'T work in Java (unlike C/C++)
        // boolean b1 = 1;
        // boolean b2 = 0;
        // if (1) { }

        // βœ… Only true/false allowed
        if (flag) {
            System.out.println("This works!");
        }

        // Size consideration
        boolean[] boolArray = new boolean[100];
        // Each element takes 1 byte (8 bits) in array
        // But boolean variable size is JVM-dependent
    }
}
Enter fullscreen mode Exit fullscreen mode

Non-Primitive Types (Reference Types)

Everything else is a non-primitive type:

  • Classes
  • Interfaces
  • Arrays
  • Enums
  • Annotations
public class ReferenceTypes {
    public static void main(String[] args) {
        // Reference types store addresses, not values
        String s1 = "Hello";
        String s2 = "Hello";
        String s3 = new String("Hello");

        System.out.println(s1 == s2);  // true (same reference in String pool)
        System.out.println(s1 == s3);  // false (different objects)
        System.out.println(s1.equals(s3));  // true (same content)

        // Memory layout
        /*
        STACK:
        s1 β†’ [reference to "Hello" in String pool]
        s2 β†’ [reference to "Hello" in String pool] (same as s1)
        s3 β†’ [reference to new String object in heap]

        HEAP:
        String pool: "Hello" ← (s1 and s2 point here)
        Regular heap: new String("Hello") ← (s3 points here)
        */
    }
}
Enter fullscreen mode Exit fullscreen mode

Variable Types & Scope

public class VariableTypes {
    // 1. Instance Variables (Non-static fields)
    private int instanceVar = 10;

    // 2. Class Variables (Static fields)
    private static int classVar = 20;

    // 3. Local Variables
    public void method() {
        int localVar = 30;

        // 4. Parameters
        processData(localVar);
    }

    public void processData(int parameter) {
        System.out.println(parameter);
    }

    public static void main(String[] args) {
        VariableTypes obj1 = new VariableTypes();
        VariableTypes obj2 = new VariableTypes();

        // Instance variables are per-object
        obj1.instanceVar = 100;
        System.out.println(obj2.instanceVar);  // 10 (unchanged)

        // Class variables are shared across all instances
        obj1.classVar = 200;
        System.out.println(obj2.classVar);  // 200 (changed!)
        System.out.println(VariableTypes.classVar);  // 200
    }
}
Enter fullscreen mode Exit fullscreen mode

Memory Layout:

HEAP:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ VariableTypes obj1  β”‚
β”‚  instanceVar: 100   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ VariableTypes obj2  β”‚
β”‚  instanceVar: 10    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

METHOD AREA:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ VariableTypes class β”‚
β”‚  classVar: 200      β”‚ ← Shared by all instances
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Variable Initialization

public class Initialization {
    // Instance variables - Default initialized
    int x;           // 0
    boolean flag;    // false
    String str;      // null

    // Local variables - NOT default initialized
    public void method() {
        int y;
        // System.out.println(y);  // ❌ Error: variable not initialized

        y = 10;
        System.out.println(y);  // βœ… Now it works
    }

    public static void main(String[] args) {
        Initialization obj = new Initialization();
        System.out.println(obj.x);     // 0 (default)
        System.out.println(obj.flag);  // false (default)
        System.out.println(obj.str);   // null (default)
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Question: "Why are instance variables default-initialized but local variables aren't?"

Explanation:

  • Instance variables are part of object state and must have predictable values. JVM zero-initializes them.
  • Local variables are temporary and forcing initialization would hurt performance. Compiler ensures they're assigned before use.

πŸ”§ Operators in Java

Operator Precedence (High to Low)

public class OperatorPrecedence {
    public static void main(String[] args) {
        int result;

        // 1. Postfix: expr++, expr--
        // 2. Unary: ++expr, --expr, +expr, -expr, !
        // 3. Multiplicative: *, /, %
        // 4. Additive: +, -
        // 5. Shift: <<, >>, >>>
        // 6. Relational: <, >, <=, >=, instanceof
        // 7. Equality: ==, !=
        // 8. Bitwise AND: &
        // 9. Bitwise XOR: ^
        // 10. Bitwise OR: |
        // 11. Logical AND: &&
        // 12. Logical OR: ||
        // 13. Ternary: ? :
        // 14. Assignment: =, +=, -=, *=, /=, %=, etc.

        // Tricky example
        result = 10 + 3 * 2;  // 16 (not 26)
        result = (10 + 3) * 2;  // 26

        result = 10 / 3 * 3;  // 9 (not 10!)
        // Explanation: (10/3)*3 = 3*3 = 9 (integer division)
    }
}
Enter fullscreen mode Exit fullscreen mode

Unary Operators (++ and --)

public class UnaryOperators {
    public static void main(String[] args) {
        int x = 5;

        // Pre-increment: Increment THEN use
        System.out.println(++x);  // 6 (x becomes 6, then prints 6)

        // Post-increment: Use THEN increment
        x = 5;
        System.out.println(x++);  // 5 (prints 5, then x becomes 6)
        System.out.println(x);    // 6

        // Complex example (Question: Favorite)
        int a = 5;
        int b = ++a + a++ + a-- + --a;
        /*
        Step by step:
        1. ++a β†’ a=6, use 6 β†’ expression: 6
        2. a++ β†’ use 6, a=7 β†’ expression: 6+6 = 12
        3. a-- β†’ use 7, a=6 β†’ expression: 12+7 = 19
        4. --a β†’ a=5, use 5 β†’ expression: 19+5 = 24
        Final: b=24, a=5
        */
        System.out.println("b = " + b + ", a = " + a);  // b = 24, a = 5
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Mistake: Never use multiple increment/decrement operators on same variable in one expression. It's undefined behavior in some cases.

Bitwise Operators

public class BitwiseOperators {
    public static void main(String[] args) {
        int a = 5;   // 0101 in binary
        int b = 3;   // 0011 in binary

        // AND (&): Both bits must be 1
        System.out.println(a & b);  // 1 (0001)

        // OR (|): At least one bit must be 1
        System.out.println(a | b);  // 7 (0111)

        // XOR (^): Bits must be different
        System.out.println(a ^ b);  // 6 (0110)

        // NOT (~): Flip all bits
        System.out.println(~a);  // -6 (two's complement)

        // Left shift (<<): Multiply by 2^n
        System.out.println(5 << 2);  // 20 (5 * 2^2)

        // Right shift (>>): Divide by 2^n (signed)
        System.out.println(20 >> 2);  // 5 (20 / 2^2)
        System.out.println(-20 >> 2);  // -5 (sign bit preserved)

        // Unsigned right shift (>>>): Fill with zeros
        System.out.println(-20 >>> 2);  // Large positive number

        // Practical use: Check if number is even/odd
        System.out.println("5 is odd: " + ((5 & 1) == 1));
        System.out.println("4 is even: " + ((4 & 1) == 0));
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Question: "How to swap two numbers without temp variable?"

public static void swap(int a, int b) {
    System.out.println("Before: a=" + a + ", b=" + b);

    a = a ^ b;  // a = a XOR b
    b = a ^ b;  // b = (a XOR b) XOR b = a
    a = a ^ b;  // a = (a XOR b) XOR a = b

    System.out.println("After: a=" + a + ", b=" + b);
}
Enter fullscreen mode Exit fullscreen mode

Short-Circuit Operators

public class ShortCircuit {
    public static void main(String[] args) {
        int x = 0;

        // Logical AND (&&) - Short circuits if first is false
        if (false && ++x > 0) {
            // ++x is NEVER executed
        }
        System.out.println(x);  // 0

        // Bitwise AND (&) - Always evaluates both sides
        if (false & ++x > 0) {
            // ++x IS executed
        }
        System.out.println(x);  // 1

        // Practical example: Null check
        String str = null;

        // βœ… Safe: Short-circuits before null pointer
        if (str != null && str.length() > 0) {
            System.out.println("Has content");
        }

        // ❌ Unsafe: Would throw NullPointerException
        // if (str.length() > 0 && str != null) {
        //     System.out.println("Has content");
        // }
    }
}
Enter fullscreen mode Exit fullscreen mode

Ternary Operator

public class TernaryOperator {
    public static void main(String[] args) {
        int a = 10, b = 20;

        // Basic ternary
        int max = (a > b) ? a : b;
        System.out.println("Max: " + max);

        // Nested ternary (avoid in production!)
        int num = 0;
        String result = (num > 0) ? "Positive" 
                      : (num < 0) ? "Negative" 
                      : "Zero";
        System.out.println(result);

        // Type consideration
        double x = true ? 1 : 2.0;  // Result is double
        System.out.println(x);  // 1.0

        // Interviewer favorite: What's the type?
        Object obj = true ? new Integer(1) : new Double(2.0);
        System.out.println(obj.getClass());  // class java.lang.Double
        // Why? Both branches promoted to their common supertype
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Control Flow Statements

if-else Statements

public class IfElse {
    public static void main(String[] args) {
        int score = 85;

        // Basic if-else
        if (score >= 90) {
            System.out.println("Grade A");
        } else if (score >= 80) {
            System.out.println("Grade B");
        } else if (score >= 70) {
            System.out.println("Grade C");
        } else {
            System.out.println("Grade F");
        }

        // Without braces (DANGEROUS - avoid in production)
        if (score > 80)
            System.out.println("Good score!");
            // System.out.println("This is NOT part of if!"); // Common mistake

        // Always use braces for clarity
        if (score > 80) {
            System.out.println("Good score!");
            System.out.println("Keep it up!");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

switch Statement

public class SwitchStatement {
    public static void main(String[] args) {
        // Traditional switch
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("Monday");
                break;
            case 2:
                System.out.println("Tuesday");
                break;
            case 3:
                System.out.println("Wednesday");
                break;
            default:
                System.out.println("Invalid day");
        }

        // Fall-through behavior
        int month = 2;
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                System.out.println("31 days");
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                System.out.println("30 days");
                break;
            case 2:
                System.out.println("28/29 days");
                break;
            default:
                System.out.println("Invalid month");
        }

        // String switch (Java 7+)
        String fruit = "apple";
        switch (fruit) {
            case "apple":
                System.out.println("Red or green");
                break;
            case "banana":
                System.out.println("Yellow");
                break;
            default:
                System.out.println("Unknown fruit");
        }

        // Switch expression (Java 14+)
        int numDays = switch (month) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> throw new IllegalArgumentException("Invalid month");
        };
        System.out.println("Days: " + numDays);
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Question: "What types can be used in switch?"

Answer (Java 17):

  • byte, short, char, int
  • Wrapper classes: Byte, Short, Character, Integer
  • String (Java 7+)
  • enum types
  • NOT allowed: long, float, double, boolean

Loops

for Loop

public class ForLoop {
    public static void main(String[] args) {
        // Traditional for loop
        for (int i = 0; i < 5; i++) {
            System.out.print(i + " ");
        }
        System.out.println();

        // Multiple variables
        for (int i = 0, j = 10; i < j; i++, j--) {
            System.out.println("i=" + i + ", j=" + j);
        }

        // Infinite loop (intentional)
        // for (;;) {
        //     System.out.println("Forever!");
        //     break;  // Need this to exit
        // }

        // Enhanced for loop (for-each)
        int[] numbers = {1, 2, 3, 4, 5};
        for (int num : numbers) {
            System.out.print(num + " ");
        }
        System.out.println();

        // Common mistake with for-each
        for (int num : numbers) {
            num = num * 2;  // ❌ Doesn't modify array!
        }
        System.out.println(numbers[0]);  // Still 1

        // Correct way to modify
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = numbers[i] * 2;
        }
        System.out.println(numbers[0]);  // Now 2
    }
}
Enter fullscreen mode Exit fullscreen mode

while Loop

public class WhileLoop {
    public static void main(String[] args) {
        // Basic while
        int i = 0;
        while (i < 5) {
            System.out.print(i + " ");
            i++;
        }
        System.out.println();

        // Input validation example
        int attempts = 0;
        boolean success = false;
        while (attempts < 3 && !success) {
            System.out.println("Attempt " + (attempts + 1));
            // Simulate some operation
            success = (attempts == 1);  // Success on 2nd attempt
            attempts++;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

do-while Loop

public class DoWhileLoop {
    public static void main(String[] args) {
        // Key difference: Executes at least once
        int i = 10;

        // while: Doesn't execute
        while (i < 5) {
            System.out.println("while: " + i);
        }

        // do-while: Executes once
        do {
            System.out.println("do-while: " + i);
        } while (i < 5);

        // Practical use: Menu systems
        int choice;
        do {
            System.out.println("\n1. Option A");
            System.out.println("2. Option B");
            System.out.println("3. Exit");
            choice = 3;  // Simulated input
        } while (choice != 3);
    }
}
Enter fullscreen mode Exit fullscreen mode

break and continue

public class BreakContinue {
    public static void main(String[] args) {
        // break: Exit loop entirely
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                break;  // Stop when i is 5
            }
            System.out.print(i + " ");  // 0 1 2 3 4
        }
        System.out.println();

        // continue: Skip current iteration
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0) {
                continue;  // Skip even numbers
            }
            System.out.print(i + " ");  // 1 3 5 7 9
        }
        System.out.println();

        // Labeled break (rarely used, but common question)
        outer:
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (i == 1 && j == 1) {
                    break outer;  // Breaks out of BOTH loops
                }
                System.out.println("i=" + i + ", j=" + j);
            }
        }

        // Labeled continue
        outer2:
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (j == 1) {
                    continue outer2;  // Continue outer loop
                }
                System.out.println("i=" + i + ", j=" + j);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

(To be continued in Part 2...)


🎯 Question: Questions - Quick Fire Round

Section 1: JVM Architecture

  1. What's the difference between JIT and interpreter?
  2. Explain the role of class loader subsystem
  3. What happens during bytecode verification?
  4. Why is Java slower than C++ for startup?
  5. What's ahead-of-time (AOT) compilation?

Section 2: Data Types

  1. Why is boolean size JVM-dependent?
  2. Explain integer promotion in expressions
  3. What's the difference between float and double internally?
  4. Can you have a generic type of primitive? Why not?
  5. What's autoboxing and unboxing? Performance impact?

Section 3: Operators

  1. Explain the difference between & and &&
  2. What's the output: System.out.println(true ? "A" : 1);
  3. How does XOR swap work? When would you use it?
  4. What's the result of -1 >>> 1? Why?
  5. Can ternary operator throw compile-time error for type mismatch?

Section 4: Control Flow

  1. Can switch work with null?
  2. What's the difference between break in switch vs loop?
  3. Explain fall-through in switch statements
  4. When to use do-while vs while?
  5. Performance: for vs for-each loop?

πŸ“š What's Coming in Part 2

  • Object-Oriented Programming (The BIG section)
  • Strings Deep Dive
  • Arrays
  • Interfaces & Abstract Classes
  • Exception Handling
  • And much more...

πŸ’‘ Pro Tips for Interviews

  1. Always explain the "why" - Don't just state facts, explain reasoning
  2. Draw diagrams - Memory layouts, class hierarchies, etc.
  3. Mention trade-offs - Every design decision has pros/cons
  4. Write clean code - Even on whiteboard, follow conventions
  5. Ask clarifying questions - Before jumping into coding

πŸ‘¨β€πŸ’» Author

Rajat

  • GitHub: @rajat12826
  • Follow for more technical deep-dives

Happy Learning! See you in Part 2! πŸš€

Made with ❀️ for the developer community

Top comments (0)