The Birth of Java

Java was created by James Gosling at Sun Microsystems (now owned by Oracle) in 1995. Originally called "Oak", it was renamed to Java after the Java coffee.

Why Was Java Created?

In the early 1990s, developers faced major challenges:

  • Platform dependency - Code written for Windows wouldn't work on Mac or Unix
  • Memory management issues - Languages like C/C++ required manual memory management
  • Pointer complexity - Pointers in C/C++ caused crashes and security vulnerabilities
  • No internet support - Existing languages weren't designed for networked applications
The Original Goal

Java was initially developed for interactive television and embedded systems like set-top boxes. The project was called "Green Project".

However, the technology was too advanced for the digital cable television industry at that time. When the World Wide Web exploded in popularity, Sun Microsystems realized Java was perfect for internet programming.

Key Principles (WORA)

"Write Once, Run Anywhere" (WORA)

This became Java's revolutionary promise - code compiled on one platform could run on any other platform without modification.

Java's Evolution
Version Year Major Features
Java 1.0 1996 First stable version
Java 5 2004 Generics, Annotations, Enums
Java 8 2014 Lambda expressions, Stream API
Java 11 2018 LTS version, HTTP Client
Java 17 2021 LTS version, Sealed classes
Java 21 2023 LTS version, Virtual Threads
Why Java Became Popular
  • Platform Independence - True WORA capability
  • Object-Oriented - Clean, modular code organization
  • Automatic Memory Management - Garbage collection handled by JVM
  • Rich API - Extensive standard library
  • Security - Built-in security features
  • Multithreading - Native support for concurrent programming
  • Community - Massive developer ecosystem
Java Today

Java remains one of the most popular programming languages, powering:

  • Enterprise applications (banking, finance)
  • Android mobile applications
  • Big data technologies (Hadoop, Spark)
  • Cloud applications (AWS, Azure, Google Cloud)
  • IoT devices
  • Scientific applications

Understanding JDK, JRE, and JVM

The Java Ecosystem Hierarchy
JDK ⊃ JRE ⊃ JVM JDK (Java Development Kit) └── JRE (Java Runtime Environment) └── JVM (Java Virtual Machine)
JVM (Java Virtual Machine)

The JVM is an abstract machine that executes Java bytecode.

What it does:

  • Loads bytecode (.class files)
  • Verifies bytecode for security
  • Executes bytecode
  • Provides runtime environment
  • Manages memory (heap, stack)
  • Performs garbage collection

Platform Independence: JVM is platform-dependent, but Java bytecode is platform-independent. This is why you need different JVMs for Windows, Mac, and Linux, but the same .class file runs on all of them.

JVM Components
  • Class Loader - Loads .class files into memory
  • Bytecode Verifier - Checks code for security violations
  • Execution Engine - Executes bytecode (interpreter + JIT compiler)
  • Runtime Data Areas - Method area, heap, stack, PC registers, native method stacks
  • Garbage Collector - Automatic memory management
JRE (Java Runtime Environment)

The JRE provides the environment to run Java applications.

Contains:

  • JVM
  • Java Class Libraries (java.lang, java.util, etc.)
  • Supporting files and configurations

Important: JRE does NOT include development tools like compiler (javac) or debugger. It's only for running Java programs, not developing them.

JDK (Java Development Kit)

The JDK is a complete kit for developing Java applications.

Contains:

  • JRE (which includes JVM)
  • Java Compiler (javac)
  • Debugger (jdb)
  • Java Documentation (javadoc)
  • Archive tool (jar)
  • Other development tools
Comparison Table
Feature JVM JRE JDK
Purpose Execute bytecode Run Java apps Develop Java apps
Platform Platform-dependent Platform-dependent Platform-dependent
Contains Execution engine JVM + libraries JRE + dev tools
Compiler No No Yes (javac)
For Users End users End users Developers
How Java Code Executes
Step 1: Write Code MyProgram.java Step 2: Compile (using javac from JDK) javac MyProgram.java → Produces MyProgram.class (bytecode) Step 3: Execute (using java command from JRE) java MyProgram → JVM loads and executes bytecode
Detailed Execution Flow
  1. Source Code (.java) - Human-readable code
  2. Compiler (javac) - Converts to bytecode
  3. Bytecode (.class) - Platform-independent intermediate code
  4. Class Loader - Loads bytecode into memory
  5. Bytecode Verifier - Security check
  6. Interpreter/JIT - Converts bytecode to machine code
  7. Execution - Program runs
JIT Compiler (Just-In-Time)

The JIT compiler improves performance by compiling frequently executed bytecode into native machine code at runtime.

  • First run: Bytecode is interpreted (slower)
  • Hotspot detection: JVM identifies frequently executed code
  • Compilation: JIT compiles hotspots to native code
  • Subsequent runs: Uses compiled native code (faster)

Interview Tip: Java is both compiled and interpreted. It's compiled to bytecode, then either interpreted or JIT-compiled to native code.

What is the Collections Framework?

The Collections Framework is a unified architecture for storing and manipulating groups of objects.

Core Interfaces
Collection (interface) ├── List (ordered, duplicates allowed) │ ├── ArrayList │ ├── LinkedList │ └── Vector ├── Set (no duplicates) │ ├── HashSet │ ├── LinkedHashSet │ └── TreeSet (sorted) └── Queue (FIFO) ├── PriorityQueue └── LinkedList Map (key-value pairs) ├── HashMap ├── LinkedHashMap └── TreeMap (sorted by keys)
List Interface

An ordered collection that allows duplicates and maintains insertion order.

ArrayList
List<String> list = new ArrayList<>(); list.add("Java"); list.add("Python"); list.add("Java"); // Duplicates allowed list.get(0); // Access by index list.remove(1); // Remove by index

Characteristics:

  • Dynamic array implementation
  • Fast random access O(1)
  • Slow insertion/deletion O(n)
  • Not synchronized (not thread-safe)
  • Best for: Read-heavy operations
LinkedList
LinkedList<String> list = new LinkedList<>(); list.add("First"); list.addFirst("New First"); // Add at beginning list.addLast("Last"); // Add at end list.removeFirst(); // Remove from beginning

Characteristics:

  • Doubly-linked list implementation
  • Fast insertion/deletion O(1)
  • Slow random access O(n)
  • Implements both List and Deque
  • Best for: Frequent insertions/deletions
Set Interface

A collection that does not allow duplicates.

HashSet
Set<Integer> set = new HashSet<>(); set.add(10); set.add(20); set.add(10); // Ignored - no duplicates set.contains(20); // true

Characteristics:

  • Hash table implementation
  • No guaranteed order
  • Fast operations O(1) average
  • Allows one null element
  • Best for: Uniqueness checking
TreeSet
Set<Integer> set = new TreeSet<>(); set.add(30); set.add(10); set.add(20); // Elements stored in sorted order: [10, 20, 30]

Characteristics:

  • Red-Black tree implementation
  • Sorted in natural order or custom comparator
  • Operations O(log n)
  • No null elements
  • Best for: Sorted unique elements
Map Interface

Stores key-value pairs. Keys must be unique.

HashMap
Map<String, Integer> map = new HashMap<>(); map.put("Alice", 25); map.put("Bob", 30); map.get("Alice"); // 25 map.containsKey("Bob"); // true map.remove("Alice");

Characteristics:

  • Hash table implementation
  • No guaranteed order
  • Fast operations O(1) average
  • Allows one null key and multiple null values
  • Not synchronized
  • Best for: Fast key-value lookups
TreeMap
Map<String, Integer> map = new TreeMap<>(); map.put("Charlie", 35); map.put("Alice", 25); map.put("Bob", 30); // Keys sorted: Alice, Bob, Charlie

Characteristics:

  • Red-Black tree implementation
  • Sorted by keys
  • Operations O(log n)
  • No null keys
  • Best for: Sorted key-value pairs
Queue Interface

Designed for FIFO (First-In-First-Out) operations.

PriorityQueue
Queue<Integer> pq = new PriorityQueue<>(); pq.offer(30); pq.offer(10); pq.offer(20); pq.poll(); // Returns 10 (smallest element)

Characteristics:

  • Heap-based implementation
  • Elements ordered by natural order or comparator
  • Does not allow null
  • Best for: Priority-based processing
Comparison: ArrayList vs LinkedList
Operation ArrayList LinkedList
Access by index O(1) O(n)
Add at beginning O(n) O(1)
Add at end O(1) amortized O(1)
Remove by index O(n) O(n)
Memory Less overhead More overhead (pointers)
Comparison: HashMap vs TreeMap
Feature HashMap TreeMap
Order No order Sorted by keys
Performance O(1) O(log n)
Null keys One allowed Not allowed
Implementation Hash table Red-Black tree
Important Collection Methods
// Common operations add(E element) remove(Object o) contains(Object o) size() isEmpty() clear() iterator() // List specific get(int index) set(int index, E element) indexOf(Object o) // Map specific put(K key, V value) get(Object key) containsKey(Object key) containsValue(Object value) keySet() values() entrySet()

Interview Tip: Always consider thread safety. For concurrent access, use ConcurrentHashMap, CopyOnWriteArrayList, or Collections.synchronizedXxx() methods.

Java Memory Structure

Memory Areas in JVM
JVM Memory ├── Heap (shared) │ ├── Young Generation │ │ ├── Eden Space │ │ └── Survivor Spaces (S0, S1) │ └── Old Generation (Tenured) ├── Method Area (shared) │ └── Runtime Constant Pool ├── Stack (per thread) ├── PC Registers (per thread) └── Native Method Stack (per thread)
Heap Memory

The heap is where all objects and instance variables are stored.

  • Shared among all threads
  • Created at JVM startup
  • Garbage collection happens here
  • Size controlled by: -Xms (initial) and -Xmx (maximum)
Young Generation
  • Eden Space - Where new objects are allocated
  • Survivor Spaces (S0, S1) - Objects that survive one GC cycle
  • Minor GC occurs here frequently
Old Generation (Tenured)
  • Long-lived objects promoted from Young Gen
  • Major GC (Full GC) occurs here
  • Slower but less frequent collection
Stack Memory

The stack stores method calls and local variables.

  • One stack per thread
  • LIFO (Last-In-First-Out) structure
  • Stores: Local variables, method parameters, return addresses
  • Each method call creates a stack frame
  • Automatically cleared when method returns
  • Size controlled by: -Xss
Stack Frame Contains:
  • Local variable array
  • Operand stack
  • Frame data (constant pool reference, etc.)
Heap vs Stack
Feature Heap Stack
Storage Objects, instance variables Method calls, local variables
Access Slower Faster
Size Larger Smaller
Lifetime Until GC collects Method execution only
Thread Safety Shared (requires sync) Thread-local (safe)
Error OutOfMemoryError StackOverflowError
Example: Memory Allocation
public void createPerson() { int age = 25; // Stack String name = "John"; // "John" in heap, reference in stack Person p = new Person(); // Object in heap, reference in stack p.setAge(age); }

What happens:

  • age - Primitive, stored directly in stack
  • name - Reference in stack, "John" object in heap
  • p - Reference in stack, Person object in heap
  • When method returns, stack variables cleared, heap objects remain until GC
Garbage Collection

Garbage Collection (GC) automatically frees memory by removing unreferenced objects.

When Does GC Run?
  • When heap memory is low
  • When System.gc() is called (not guaranteed)
  • At JVM's discretion
Types of GC
  • Minor GC - Cleans Young Generation (fast, frequent)
  • Major GC - Cleans Old Generation
  • Full GC - Cleans entire heap (slow, infrequent)
GC Algorithms
  • Serial GC - Single-threaded, for small apps
  • Parallel GC - Multiple threads, for throughput
  • G1 GC - Default since Java 9, balances latency and throughput
  • ZGC - Low latency, large heaps
Making Objects Eligible for GC
// 1. Nullifying reference Person p = new Person(); p = null; // Now eligible for GC // 2. Reassigning reference Person p1 = new Person(); Person p2 = new Person(); p1 = p2; // First object eligible for GC // 3. Object created inside method void method() { Person p = new Person(); } // After method ends, object eligible for GC // 4. Island of isolation class Node { Node next; } Node n1 = new Node(); Node n2 = new Node(); n1.next = n2; n2.next = n1; n1 = null; n2 = null; // Both eligible despite referencing each other
finalize() Method
@Override protected void finalize() throws Throwable { // Cleanup code before GC System.out.println("Object is being collected"); }

Deprecated: finalize() is deprecated since Java 9. Use try-with-resources or Cleaner API instead.

Memory Leaks in Java

Yes, Java can have memory leaks! Common causes:

  • Unclosed resources - Streams, connections not closed
  • Static collections - Objects added but never removed
  • Listeners not removed - Event listeners keeping references
  • ThreadLocal misuse - Not cleaning up ThreadLocal variables
  • Custom cache - Without eviction policy
Best Practices
  • Use try-with-resources for auto-closeable objects
  • Set references to null when done (especially in long-lived objects)
  • Use weak references for caches
  • Monitor memory usage with tools (VisualVM, JConsole)
  • Tune JVM parameters based on application needs

Understanding Multithreading

What is a Thread?

A thread is the smallest unit of execution within a process. It's a lightweight subprocess.

Process vs Thread
Process Thread
Heavyweight Lightweight
Separate memory space Shared memory space
Expensive creation Cheap creation
IPC needed for communication Direct communication
Creating Threads
Method 1: Extending Thread Class
class MyThread extends Thread { public void run() { System.out.println("Thread is running"); } } // Usage MyThread t = new MyThread(); t.start(); // Don't call run() directly!
Method 2: Implementing Runnable (Preferred)
class MyRunnable implements Runnable { public void run() { System.out.println("Thread is running"); } } // Usage Thread t = new Thread(new MyRunnable()); t.start(); // Lambda (Java 8+) Thread t = new Thread(() -> { System.out.println("Thread is running"); }); t.start();

Why Runnable is preferred: Java doesn't support multiple inheritance. Implementing Runnable allows your class to extend another class.

Thread Lifecycle
New ↓ (start()) Runnable ⟷ Running ↓ ↓ Blocked Waiting/Timed Waiting ↓ ↓ Terminated
States Explained:
  • New - Thread created but not started
  • Runnable - Ready to run, waiting for CPU
  • Running - Executing code
  • Blocked - Waiting for monitor lock
  • Waiting - Waiting indefinitely for another thread
  • Timed Waiting - Waiting for specified time
  • Terminated - Finished execution
Important Thread Methods
// Starting thread t.start(); // Making thread sleep Thread.sleep(1000); // 1 second // Waiting for thread to complete t.join(); // Getting/Setting thread properties t.setName("MyThread"); t.getName(); t.setPriority(Thread.MAX_PRIORITY); t.getPriority(); // Getting current thread Thread current = Thread.currentThread(); // Checking if alive t.isAlive(); // Interrupting thread t.interrupt(); t.isInterrupted();
Synchronization

Synchronization prevents multiple threads from accessing shared resources simultaneously, avoiding race conditions.

Synchronized Method
class Counter { private int count = 0; public synchronized void increment() { count++; // Thread-safe } public synchronized int getCount() { return count; } }
Synchronized Block
class Counter { private int count = 0; private Object lock = new Object(); public void increment() { synchronized(lock) { count++; // Only this block is synchronized } } }
Why Synchronization?
// WITHOUT synchronization (Race condition!) class Counter { int count = 0; void increment() { count++; } } // Thread 1: reads count=0, increments to 1 // Thread 2: reads count=0 (before T1 writes), increments to 1 // Result: count=1 (should be 2!) // WITH synchronization class Counter { int count = 0; synchronized void increment() { count++; } } // Result: count=2 (correct!)
Inter-Thread Communication

Threads can communicate using wait(), notify(), notifyAll().

class SharedResource { synchronized void produce() throws InterruptedException { // Produce item notify(); // Notify consumer wait(); // Wait for consumer } synchronized void consume() throws InterruptedException { // Consume item notify(); // Notify producer wait(); // Wait for producer } }

Important: wait(), notify(), and notifyAll() must be called from synchronized context.

Deadlock

Deadlock occurs when two or more threads are blocked forever, waiting for each other.

// Deadlock scenario class DeadlockExample { Object lock1 = new Object(); Object lock2 = new Object(); void method1() { synchronized(lock1) { synchronized(lock2) { // ... } } } void method2() { synchronized(lock2) { // Different order! synchronized(lock1) { // ... } } } } // Thread 1 holds lock1, waits for lock2 // Thread 2 holds lock2, waits for lock1 // DEADLOCK!
Preventing Deadlock:
  • Always acquire locks in same order
  • Use timeout with tryLock()
  • Avoid nested locks
  • Use concurrent collections
Executor Framework (Java 5+)

Modern way to manage threads using thread pools.

// Fixed thread pool ExecutorService executor = Executors.newFixedThreadPool(5); // Submit tasks executor.submit(() -> { System.out.println("Task executed by " + Thread.currentThread().getName()); }); // Shutdown executor.shutdown();
Types of Thread Pools:
  • newFixedThreadPool(n) - Fixed number of threads
  • newCachedThreadPool() - Creates threads as needed
  • newSingleThreadExecutor() - Single worker thread
  • newScheduledThreadPool(n) - For scheduled tasks
Volatile Keyword
class SharedData { private volatile boolean flag = false; public void setFlag() { flag = true; // Visible to all threads immediately } }

volatile ensures:

  • Variable is always read from/written to main memory
  • Prevents caching in thread-local memory
  • Guarantees visibility across threads
  • Does NOT guarantee atomicity (use synchronized or Atomic classes)
Atomic Classes
AtomicInteger count = new AtomicInteger(0); count.incrementAndGet(); // Atomic increment count.addAndGet(5); // Atomic add count.compareAndSet(5, 10); // Atomic compare-and-swap

Thread-safe without synchronization overhead.

Exception Handling in Java

What is an Exception?

An exception is an unwanted or unexpected event that disrupts the normal flow of program execution.

Exception Hierarchy
Throwable ├── Error (Unchecked) │ ├── OutOfMemoryError │ ├── StackOverflowError │ └── VirtualMachineError └── Exception ├── IOException (Checked) ├── SQLException (Checked) ├── ClassNotFoundException (Checked) └── RuntimeException (Unchecked) ├── NullPointerException ├── ArrayIndexOutOfBoundsException ├── ArithmeticException └── IllegalArgumentException
Checked vs Unchecked Exceptions
Feature Checked Unchecked
When checked Compile time Runtime
Must handle? Yes (or declare) No
Extends Exception class RuntimeException
Examples IOException, SQLException NullPointerException
Try-Catch-Finally
try { // Code that may throw exception int result = 10 / 0; } catch (ArithmeticException e) { // Handle specific exception System.out.println("Cannot divide by zero"); } catch (Exception e) { // Handle any other exception System.out.println("Error: " + e.getMessage()); } finally { // Always executes (cleanup code) System.out.println("Cleanup"); }
Rules:
  • Multiple catch blocks allowed
  • Specific exceptions must come before general ones
  • finally block always executes (even if return in try/catch)
  • finally only skipped if System.exit() or fatal error
Throw vs Throws
throw (keyword)
// Used to explicitly throw an exception public void validateAge(int age) { if (age < 18) { throw new IllegalArgumentException("Age must be 18+"); } }
throws (declaration)
// Declares that method may throw exception public void readFile() throws IOException { FileReader file = new FileReader("file.txt"); // ... }
throw throws
Used inside method Used in method signature
Throws exception object Declares exception types
Only one exception at a time Multiple exceptions allowed
Custom Exceptions
// Custom checked exception class InsufficientBalanceException extends Exception { public InsufficientBalanceException(String message) { super(message); } } // Custom unchecked exception class InvalidAccountException extends RuntimeException { public InvalidAccountException(String message) { super(message); } } // Usage public void withdraw(double amount) throws InsufficientBalanceException { if (balance < amount) { throw new InsufficientBalanceException("Balance too low"); } balance -= amount; }
Try-With-Resources (Java 7+)
// Automatic resource management try (FileReader reader = new FileReader("file.txt"); BufferedReader br = new BufferedReader(reader)) { String line = br.readLine(); } catch (IOException e) { e.printStackTrace(); } // Resources automatically closed, even if exception occurs
Requirements:
  • Resource must implement AutoCloseable
  • close() called automatically in reverse order
  • Cleaner than finally block
Multi-Catch (Java 7+)
try { // Code } catch (IOException | SQLException e) { // Handle both exceptions same way System.out.println("Error: " + e); }
Common Exceptions
// NullPointerException String str = null; str.length(); // NPE // ArrayIndexOutOfBoundsException int[] arr = new int[3]; arr[5] = 10; // AIOOBE // ArithmeticException int result = 10 / 0; // AE // NumberFormatException int num = Integer.parseInt("abc"); // NFE // ClassCastException Object obj = "Hello"; Integer num = (Integer) obj; // CCE // IllegalArgumentException Thread.sleep(-100); // IAE
Best Practices
  • Catch specific exceptions, not generic Exception
  • Don't catch Throwable or Error
  • Never leave catch block empty
  • Use try-with-resources for AutoCloseable resources
  • Log exceptions properly
  • Don't use exceptions for flow control
  • Create custom exceptions for domain-specific errors
Exception Propagation
void method3() { int result = 10 / 0; // Exception thrown here } void method2() { method3(); // Exception propagates to method2 } void method1() { try { method2(); // Exception propagates to method1 } catch (ArithmeticException e) { // Finally caught and handled here System.out.println("Handled in method1"); } }

If exception is not caught, it propagates up the call stack until handled or program terminates.

Types of Classes in Java

1. Regular Class
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void display() { System.out.println(name + ", " + age); } }
2. Abstract Class

Cannot be instantiated. May contain abstract methods (without body) and concrete methods.

abstract class Animal { String name; // Abstract method (no body) abstract void makeSound(); // Concrete method void sleep() { System.out.println("Sleeping..."); } } class Dog extends Animal { // Must implement abstract method void makeSound() { System.out.println("Woof!"); } }
3. Final Class

Cannot be extended/inherited.

final class ImmutableClass { private final int value; public ImmutableClass(int value) { this.value = value; } } // This will cause error: // class SubClass extends ImmutableClass { }
4. Static Nested Class

Static class defined inside another class. Can access only static members of outer class.

class OuterClass { static int outerStatic = 10; int outerInstance = 20; static class StaticNested { void display() { System.out.println(outerStatic); // OK // System.out.println(outerInstance); // ERROR } } } // Usage OuterClass.StaticNested nested = new OuterClass.StaticNested();
5. Inner Class (Non-static Nested Class)

Non-static class inside another class. Can access all members of outer class.

class OuterClass { private int outerValue = 10; class InnerClass { void display() { System.out.println(outerValue); // Can access outer's private members } } } // Usage OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass();
6. Anonymous Inner Class

Class without a name, created on-the-fly. Used for one-time use.

// Anonymous class implementing interface Runnable r = new Runnable() { public void run() { System.out.println("Running..."); } }; // Anonymous class extending class Animal a = new Animal() { void makeSound() { System.out.println("Some sound"); } };
7. Local Inner Class

Class defined inside a method. Scope limited to that method.

class OuterClass { void method() { // Local inner class class LocalInner { void display() { System.out.println("Local inner class"); } } LocalInner local = new LocalInner(); local.display(); } }
8. Singleton Class

Ensures only one instance of the class exists.

class Singleton { private static Singleton instance; // Private constructor private Singleton() {} // Public method to get instance public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } // Usage Singleton obj = Singleton.getInstance();
9. POJO (Plain Old Java Object)

Simple class with private fields, getters, and setters. No special restrictions.

public class Employee { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
10. Immutable Class

State cannot be changed after creation.

public final class ImmutablePerson { private final String name; private final int age; public ImmutablePerson(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } // No setters! }
Rules for Immutable Class:
  • Declare class as final
  • Make all fields private final
  • No setters
  • Initialize all fields in constructor
  • Return copies of mutable objects
Comparison Table
Type Can Instantiate? Can Inherit? Use Case
Regular Yes Yes General purpose
Abstract No Yes Base class template
Final Yes No Prevent inheritance
Static Nested Yes Yes Logical grouping
Inner Yes (via outer) Yes Access outer members
Singleton One instance only Usually no Global access point

Interview Tip: Know when to use each type. Abstract for templates, Final for security, Singleton for resource management, Immutable for thread safety.

Java Data Types

Primitive Data Types

Java has 8 primitive data types:

>
Type Size Range Default
byte 1 byte -128 to 127 0
short 2 bytes -32,768 to 32,767 0
int 4 bytes -2³¹ to 2³¹-1 0
long 8 bytes -2⁶³ to 2⁶³-1 0L
float 4 bytes ~6-7 decimal digits 0.0f
double 8 bytes ~15 decimal digits 0.0d
char 2 bytes 0 to 65,535 (Unicode) '\u0000'
boolean 1 bit true or false false
Examples
byte age = 25; short year = 2024; int population = 1000000; long distance = 987654321L; // Note the 'L' suffix float price = 19.99f; // Note the 'f' suffix double pi = 3.14159265359; char grade = 'A'; boolean isActive = true;
Reference Types

All objects and arrays are reference types:

String name = "Java"; int[] numbers = new int[5]; Person person = new Person(); ArrayList<String> list = new ArrayList<>();
Type Casting
Widening (Implicit)
int i = 100; long l = i; // Automatic double d = i; // Automatic
Narrowing (Explicit)
double d = 99.99; int i = (int) d; // Manual cast needed long l = 100L; int i2 = (int) l; // Manual cast
Important Java Keywords
Access Modifiers
  • public - Accessible everywhere
  • private - Accessible only within class
  • protected - Accessible within package and subclasses
  • default (no keyword) - Package-private
Non-Access Modifiers
  • static - Belongs to class, not instance
  • final - Cannot be modified/inherited/overridden
  • abstract - Incomplete, must be overridden
  • synchronized - Thread-safe method
  • volatile - Variable in main memory
  • transient - Skip during serialization
Other Important Keywords
  • this - Refers to current object
  • super - Refers to parent class
  • new - Creates new object
  • instanceof - Checks object type
  • return - Exits method with value
  • break - Exits loop or switch
  • continue - Skips to next iteration
  • extends - Inherits from class
  • implements - Implements interface
  • throws - Declares exceptions
  • throw - Throws exception
Wrapper Classes

Each primitive has a corresponding wrapper class:

Primitive Wrapper Class
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
Autoboxing & Unboxing
// Autoboxing (primitive → Object) Integer obj = 10; // Automatic // Unboxing (Object → primitive) int primitive = obj; // Automatic // Before Java 5, manual conversion: Integer obj = Integer.valueOf(10); int primitive = obj.intValue();
var Keyword (Java 10+)
// Type inference for local variables var message = "Hello"; // String var count = 10; // int var list = new ArrayList<String>(); // Cannot use with: // - Fields, Method parameters, Return types // - Without initialization

Interview Tip: Understanding the difference between primitives (stored in stack, passed by value) and objects (stored in heap, passed by reference) is crucial.