Core Java Complete Notes 1
Core Java Complete Notes 1
Table of Contents
PART A — JAVA BASICS
1. Tokens
2. Keywords
3. Identifiers
4. Data Types & 5. Primitive Types
6. Casting
PART F — MULTITHREADING
40. Multithreading
1. Tokens
A token is the smallest unit in a Java program that the compiler can understand. When Java reads your source
code, it first breaks it down into tokens — just like breaking a sentence into individual words. Every element you
write belongs to one of five categories:
Keywords Reserved words with a fixed meaning int, class, if, return
Literals Fixed constant values in code 42, 3.14, 'A', "Hi", true
public class TokenDemo { // 'public','class' = Keywords public static void main(String[] args) {
int number = 42; // 'int'=Keyword | 'number'=Identifier | '42'=Literal int result = number + 10;
// '+'=Operator | ';'=Separator [Link](result); } }
Real-world analogy: Tokens are to Java code what words are to a sentence. Just as sentences have nouns,
verbs, and punctuation — Java programs have keywords, identifiers, operators, literals, and separators.
2. Keywords
Keywords are reserved words that Java has already assigned a special meaning. You cannot use them as
variable/class/method names. Java has 53 reserved keywords. Here are the most important ones grouped by
category:
Category Keywords
Data Types byte, short, int, long, float, double, char, boolean, void
Class & Object class, interface, extends, implements, abstract, new, this, super, instanceof, enum
Control Flow if, else, switch, case, default, for, while, do, break, continue, return
Modifiers & Others static, final, synchronized, volatile, transient, import, package, assert, native
Important: Keywords are always in lowercase. Java is case-sensitive, so 'Int' is NOT a keyword — only 'int'
is. Also, true, false, and null are technically literals but also reserved — you cannot use them as identifiers.
3. Identifiers
An identifier is any name YOU give to a variable, class, method, or interface. Java has strict rules:
• Can contain: letters (a–z, A–Z), digits (0–9), underscore (_), dollar sign ($)
• Cannot start with a digit — '2name' is invalid; 'name2' is valid
• Cannot be a Java keyword — you cannot name a variable 'int' or 'class'
• No spaces or special characters (@, #, !, etc.)
• Case-sensitive — 'Age', 'age', and 'AGE' are three different identifiers
4. Data Types
A data type tells Java what kind of value a variable stores and how much memory to allocate. Java is strongly
typed — every variable must be declared with a type. Data types fall into two broad groups:
• Primitive — 8 built-in types. Values stored directly in stack memory.
• Non-Primitive (Reference) — Objects, Arrays, Strings. Variable stores a reference (address) to heap
memory.
Primitive byte, short, int, long, float, double, char, boolean — store values directly
byte 1 byte 0 Small numbers (-128 to 127). Saving memory in large arrays.
int 4 bytes 0 Most whole numbers. Default integer type. (~±2.1 billion)
long 8 bytes 0L Very large whole numbers. Add 'L' suffix. Use for timestamps.
float 4 bytes 0.0f Decimal numbers, less precise. Add 'f' suffix. (~6-7 digits)
double 8 bytes 0.0 Decimal numbers, more precise. Default decimal type. (~15 digits)
char 2 bytes '\u0000' Single Unicode character enclosed in single quotes: 'A', '5'.
boolean 1 bit false Only two values: true or false. Used in conditions.
// Declaring each primitive type byte b = 100; short sh = 30_000; int i = 1_000_000; //
underscores allowed for readability long l = 9_999_999_999L; // must end with 'L' float f =
3.14f; // must end with 'f' double d = 3.14159265358; // default decimal char c = 'A'; // single
quotes boolean ok = true; // Checking sizes at runtime [Link](Integer.MAX_VALUE);
Kind Explanation
Widening (Implicit/Automatic) Smaller → Larger type. No data loss. Java handles it automatically. Order: byte →
short → int → long → float → double
Narrowing (Explicit/Manual) Larger → Smaller type. Data may be lost. You must write the target type in
parentheses: (int) myDouble. The decimal part is DROPPED (not rounded).
// WIDENING — automatic, no syntax needed int myInt = 9; double myDouble = myInt; // int →
double, auto [Link](myDouble); // 9.0 // NARROWING — must cast explicitly double
price = 9.99; int intPrice = (int) price; // double → int, manual [Link](intPrice);
// 9 (NOT 10 — decimal is CUT, not rounded) // Char ↔ int char letter = 'A'; int ascii =
letter; // widening: char → int [Link](ascii); // 65 (ASCII value of 'A') char back
= (char) 66; // narrowing: int → char [Link](back); // B
Warning: Casting 300 into a byte gives -56, NOT 300, because byte max is 127. Always verify the value fits in
the target type before narrowing.
Key point: s1 and s2 share the same structure but hold completely independent data. Changing [Link]
does NOT affect [Link].
9. Variables in Java
Java has three variable types based on where they are declared:
Instance Variable Inside class, outside Each object gets its own copy. Default value given. Stored in heap.
methods
Static Variable Inside class with 'static' ONE shared copy for ALL objects. Lives as long as program runs.
Local Variable Inside a method or block Must be initialised before use (no default). Stored in stack. Destroyed
when method ends.
public class VariableDemo { int instanceVar = 10; // Instance — each object gets own copy static
int staticVar = 100; // Static — shared across ALL objects void show() { int localVar = 50; //
Local — only lives inside this method [Link](instanceVar + " " + staticVar + " " +
localVar); } public static void main(String[] args) { VariableDemo obj1 = new VariableDemo();
VariableDemo obj2 = new VariableDemo(); [Link] = 20;
[Link]([Link]); // 20 [Link]([Link]); // 10 —
unaffected [Link] = 999; [Link]([Link]); // 999 — shared!
[Link]([Link]); // 999 — shared! } }
10. Methods
A method is a named block of code that performs a task and can be reused. Syntax: accessModifier
returnType methodName(parameters) { body }
// No parameters, no return void greet() { [Link]("Hello!"); } // Parameters and a
return value int add(int a, int b) { return a + b; } // Multiple parameters String
fullName(String first, String last) { return first + " " + last; } // Varargs — variable number
of arguments int sumAll(int... nums) { int total = 0; for (int n : nums) total += n; return
total; } [Link](sumAll(1, 2, 3, 4, 5)); // 15 // Pass-by-VALUE for primitives (copy
is passed, original unchanged) void changeInt(int x) { x = 999; } int n = 10; changeInt(n);
[Link](n); // still 10 // Pass-by-REFERENCE for objects (the address is copied —
object CAN be modified) void changeName(Student s) { [Link] = "Changed"; }
Stores Local variables, method call frames, Objects and instance variables
references
Management Automatic (LIFO — Last In First Out) Garbage Collector cleans unused objects
Thread Each thread has its own private stack Shared among ALL threads
void calculate() { int x = 5; // 'x' on STACK Student s = new Student(); // reference 's' on
STACK // actual Student object in HEAP [Link] = "Alice"; // 'name' stored in HEAP (inside the
object) } // When calculate() ends: x and s removed from stack // Student object stays in HEAP
until GC collects it
Non-static access Non-static methods CAN access both static and instance members
public class BankAccount { static int totalAccounts = 0; // shared counter String owner; int
balance; // per-object data static { [Link]("Class loaded"); } // static block
BankAccount(String owner, int balance) { [Link] = owner; [Link] = balance;
totalAccounts++; // update shared counter } static void showTotal() { // static method — no
object needed [Link]("Total accounts: " + totalAccounts); } void showBalance() { //
instance method — needs object [Link](owner + ": " + balance); } } BankAccount a1 =
new BankAccount("Alice", 5000); BankAccount a2 = new BankAccount("Bob", 3000);
[Link](); // Total accounts: 2 [Link](); // Alice: 5000
Use Explanation
[Link] Distinguish instance variable from parameter with the same name
this(...) Constructor chaining — call another constructor of the same class. MUST be first line.
return this Return the current object — enables method chaining (builder pattern)
public class Rectangle { double width, height; Rectangle(double width, double height) {
[Link] = width; [Link] = height; // use 1: resolve shadowing } Rectangle() { this(1.0,
1.0); } // use 3: constructor chaining Rectangle setWidth(double w) { [Link] = w; return
this; } // use 4 Rectangle setHeight(double h) { [Link] = h; return this; } // use 4 double
area() { return [Link] * [Link]; } // use 2 } // Method chaining double a = new
Rectangle().setWidth(5).setHeight(3).area(); // 15.0
15. Constructors
A constructor is automatically called when you create an object with new. It initialises the object. Rules: same
name as class, no return type, can be overloaded (multiple constructors), if none is written Java provides a
default one.
public class Employee { String name; int id; double salary; // Default constructor Employee() {
name = "Unknown"; id = 0; salary = 0.0; } // Parameterised constructor Employee(String name, int
id, double salary) { [Link] = name; [Link] = id; [Link] = salary; } // Copy constructor
Employee(Employee other) { [Link] = [Link]; [Link] = [Link]; [Link] =
[Link]; } void display() { [Link]("ID:"+id+" Name:"+name+" Salary:"+salary);
} } Employee e1 = new Employee(); Employee e2 = new Employee("Alice", 101, 75000.0); Employee e3
= new Employee(e2); // independent copy [Link](); // ID:0 Name:Unknown Salary:0.0
[Link](); // ID:101 Name:Alice Salary:75000.0 [Link](); // ID:101 Name:Alice
Salary:75000.0
16. Inheritance
Inheritance lets a child class acquire properties and behaviours of a parent class using the extends keyword. It
models an IS-A relationship and promotes code reuse. Java supports only single class inheritance but multiple
interface implementation.
class Vehicle { String brand; int speed; Vehicle(String brand, int speed) { [Link]=brand;
[Link]=speed; } void start() { [Link](brand + " started"); } } class Car extends
Vehicle { // IS-A Vehicle int doors; Car(String brand, int speed, int doors) { super(brand,
speed); // call parent constructor [Link] = doors; } void honk() { [Link](brand
+ " honks!"); } } class ElectricCar extends Car { // IS-A Car, IS-A Vehicle (multilevel) int
battery; ElectricCar(String brand, int speed, int doors, int battery) { super(brand, speed,
doors); [Link] = battery; } void charge() { [Link](brand + " charging..."); }
} ElectricCar ec = new ElectricCar("Tesla", 250, 4, 100); [Link](); // inherited from Vehicle
[Link](); // inherited from Car [Link](); // own method
Variable access [Link] — resolve shadowing [Link] — access hidden parent variable
Constructor call this(...) — same class constructor super(...) — parent class constructor
Enums A fixed set of named constants: enum Day { MON, TUE, WED }
Upcasting (Implicit/Safe) Child class reference → Parent class reference. Automatic. Object is still a child —
you just see it through the parent lens. Can only access parent members via this
reference.
Downcasting (Explicit/Risky) Parent reference → Child class reference. Must be explicit. Can fail with
ClassCastException if the actual object is NOT of that type. Always check with
instanceof before downcasting.
class Animal { void eat() { [Link]("eating"); } } class Dog extends Animal { void
bark() { [Link]("Woof!"); } } // UPCASTING — automatic Animal a = new Dog(); // Dog
object, Animal reference [Link](); // works — eat() is in Animal // [Link](); // ERROR — Animal
reference can't see bark() // DOWNCASTING — manual if (a instanceof Dog) { // always check
first! Dog d = (Dog) a; // explicit downcast [Link](); // Woof! }
20. Polymorphism
Type Key Points
Compile-time (Method Overloading) Same method name, different parameters in the SAME class. Resolved by
compiler at compile time (static binding).
Runtime (Method Overriding) Child class redefines parent's method. Same name + same parameters. Resolved
by JVM at runtime (dynamic binding). Use @Override.
// OVERLOADING — same class, different parameters class Calc { int add(int a, int b) { return a
+ b; } double add(double a, double b) { return a + b; } int add(int a, int b, int c) { return
a+b+c; } } // OVERRIDING — child replaces parent's method class Shape { void draw() {
[Link]("Shape"); } } class Circle extends Shape { @Override void draw() {
[Link]("Circle"); } } class Triangle extends Shape { @Override void draw() {
[Link]("Triangle"); } } // Runtime polymorphism Shape[] shapes = { new Circle(),
new Triangle(), new Shape() }; for (Shape s : shapes) [Link](); // Circle | Triangle | Shape
the reference TYPE determines which variable is accessed, NOT the object type.
class Parent { String type = "Parent"; } class Child extends Parent { String type = "Child"; }
// hides parent's 'type' Parent p = new Child(); // upcasting [Link]([Link]); //
"Parent" — reference is Parent, so Parent's variable Child c = new Child();
[Link]([Link]); // "Child" — reference is Child, so Child's variable // CONTRAST
with method overriding (runtime): // Even if reference is Parent, overridden methods use the
CHILD's version
Interview tip: With methods → runtime decides (object type). With variables → compile time decides
(reference type). Variable hiding is generally considered bad practice — avoid it.
23. Encapsulation
Encapsulation = wrapping data + methods in one unit AND hiding the internal data from the outside. Achieved
by: (1) declaring fields private, (2) providing public getter/setter methods with optional validation.
public class Student { private String name; // hidden private int age; private double marks; //
Getter — read-only access public String getName() { return name; } public int getAge() { return
age; } public double getMarks() { return marks; } // Setter WITH validation — data protection
public void setName(String n) { if (n != null && ![Link]()) [Link] = n; else
[Link]("Invalid name!"); } public void setAge(int a) { if (a > 0 && a < 150)
[Link] = a; else [Link]("Invalid age!"); } public void setMarks(double m) { if (m
>= 0 && m <= 100) [Link] = m; else [Link]("Marks must be 0-100!"); } }
24. Abstraction
Abstraction means hiding complex implementation details and showing only what is necessary. You define
WHAT something does, not HOW. Achieved in Java using abstract classes and interfaces.
Abstract methods Yes (can also have concrete methods) All abstract by default (pre-Java 8)
Constructor Yes No
Best used when Related classes share common code Unrelated classes need same capability
An abstract class cannot be instantiated (no objects directly). It may have abstract methods (no body) that child
classes MUST implement, plus concrete methods with full implementations.
abstract class Shape { String color; Shape(String color) { [Link] = color; } abstract double
area(); // child MUST implement abstract double perimeter(); // child MUST implement void
describe() { // shared concrete method [Link]("Color: "+color+" | Area: "+area());
} } class Circle extends Shape { double radius; Circle(String color, double r) { super(color);
[Link] = r; } @Override double area() { return 3.14 * radius * radius; } @Override double
perimeter() { return 2 * 3.14 * radius; } } class Rect extends Shape { double w, h; Rect(String
color, double w, double h) { super(color); this.w=w; this.h=h; } @Override double area() {
return w * h; } @Override double perimeter() { return 2 * (w + h); } } // Shape s = new Shape();
// ERROR — cannot instantiate abstract class Circle c = new Circle("red", 5); [Link](); //
Color: red | Area: 78.5
26. Interface
An interface defines a contract — a list of methods that implementing classes MUST provide. From Java 8,
interfaces can also have default and static methods. A class can implement multiple interfaces.
interface Flyable { void fly(); // abstract — MUST implement default void land() { // optional
to override (Java 8+) [Link]("Landing..."); } static void rules() { // called on
interface directly (Java 8+) [Link]("Aviation rules apply"); } } interface
Swimmable { void swim(); } // Implementing MULTIPLE interfaces class Duck implements Flyable,
Swimmable { @Override public void fly() { [Link]("Duck flying"); } @Override public
void swim() { [Link]("Duck swimming"); } // land() uses default — not required to
override } Duck d = new Duck(); [Link](); [Link](); [Link](); // Duck flying | Duck swimming |
Landing... [Link](); // Aviation rules apply // Polymorphism with interface Flyable f =
new Duck(); // upcasting [Link](); // Duck flying (runtime decides)
IS-A extends keyword Child cannot exist without parent Dog IS-A Animal
type
Has-A Field of another class Strong — inner object's lifecycle Car HAS-A Engine
(Composition) tied to outer
Has-A (Aggregation) Field reference Weak — inner object can exist Dept HAS-A Employee
independently
// COMPOSITION — Engine lives inside Car class Engine { int hp; Engine(int hp){[Link]=hp;} void
start(){[Link]("Engine ON");} } class Car { String brand; Engine engine; Car(String
brand, int hp) { [Link]=brand; [Link]=new Engine(hp); } void drive() { [Link]();
[Link](brand+" moving at "+[Link]+"hp"); } } // AGGREGATION — Employee exists
independently class Employee { String name; Employee(String n){name=n;} } class Dept { String
name; Employee manager; Dept(String n, Employee e){ name=n; manager=e; } } Employee e = new
Employee("Alice"); // created independently Dept d = new Dept("Engineering", e); // just a
reference
28. Packages
A package is a namespace (folder) that groups related classes. Prevents naming conflicts and provides access
control. Use import to bring classes from other packages.
[Link] String, Math, Object, System, Integer, Thread — auto-imported, no import needed
Modifier Same Class Same Package Subclass (any pkg) Any Class anywhere
private YES NO NO NO
Best practice: Apply the Principle of Least Privilege. Make everything private first, then expose only what is
truly needed via public methods.
final variable Value cannot be changed after the first assignment. Becomes a constant.
final class Class cannot be extended (subclassed). E.g., String, Integer are final.
blank final Declared final but assigned exactly once — inside the constructor only.
final double PI = 3.14159; // PI = 3.0; // ERROR — cannot reassign final variable class Parent {
final void show() { [Link]("Parent"); } } class Child extends Parent { // void
show() { } // ERROR — cannot override final method } final class Utility { static int square(int
n){ return n*n; } } // class BetterUtil extends Utility { } // ERROR — cannot extend final class
equals(Object o) Checks logical equality. Default checks reference (==). Override to compare content.
hashCode() Returns an int hash code. Must override together with equals() — HashMap depends on this.
public class Person { String name; int age; Person(String n, int a) { name=n; age=a; } @Override
public String toString() { return "Person{name='"+name+"', age="+age+"}"; } @Override public
boolean equals(Object o) { if (this==o) return true; if (!(o instanceof Person)) return false;
Person p = (Person)o; return age==[Link] && [Link]([Link]); } @Override public int
hashCode() { return [Link]()*31 + age; } } Person p1 = new Person("Alice", 25); Person p2
= new Person("Alice", 25); [Link](p1); // Person{name='Alice', age=25}
[Link]([Link](p2)); // true — same content [Link](p1==p2); // false
— different objects
Wrapper classes are object versions of the 8 primitives. Needed because Collections can only hold objects, not
primitives.
// Autoboxing — primitive → Wrapper (automatic) int prim = 42; Integer wrap = prim; //
auto-boxed ArrayList<Integer> list = new ArrayList<>(); [Link](10); // 10 auto-boxed to
Integer(10) // Unboxing — Wrapper → primitive (automatic) Integer w = [Link](99); int
p = w; // auto-unboxed // String conversions int num = [Link]("123"); // String → int
String s = [Link](456); // int → String String s2 = [Link](789); // int →
String [Link](Integer.MAX_VALUE); // 2147483647
[Link]([Link](10)); // 1010
[Link]([Link]('a')); // A
[Link]([Link]('5')); // true
Checked Exception Checked at COMPILE time. You must handle with try-catch OR declare with
throws. Examples: IOException, SQLException, ClassNotFoundException,
FileNotFoundException
Unchecked (Runtime) Exception Checked at RUNTIME only. Usually programming bugs. Not required to declare.
Examples: NullPointerException, ArrayIndexOutOfBoundsException,
ClassCastException, ArithmeticException
Error Serious JVM-level problems. You generally should NOT catch these. Examples:
StackOverflowError, OutOfMemoryError
File [Link] Represents a file/directory path. Create, delete, check existence, list contents.
FileWriter [Link] Write text to a file character by character. Slow without buffering.
BufferedWriter [Link] Wraps FileWriter for faster writing. Adds newLine() method.
BufferedReader [Link] Wraps FileReader for faster reading. readLine() returns one line at a time.
Files (NIO) [Link] Modern API. readAllLines(), write(), copy(), delete(), exists().
Part F — Multithreading
40. Multithreading
A thread is the smallest unit of execution. Multithreading lets a Java program run multiple tasks concurrently —
e.g., one thread downloads a file while another updates the UI. Java provides built-in threading support.
New Thread object created with new Thread(). start() not yet called.
Runnable start() called. Thread is ready, waiting for CPU to schedule it.
Map HashMap, LinkedHashMap, TreeMap Key-value pairs. Keys unique. Not a Collection technically.
ArrayList
Dynamic array. Fast index access O(1). Slow insert/delete in middle O(n). Default choice for lists.
ArrayList<String> fruits = new ArrayList<>(); [Link]("Apple"); [Link]("Banana");
[Link]("Cherry"); [Link](1, "Mango"); // insert at index 1 [Link](0, "Avocado"); //
replace at index 0 [Link]([Link](2)); // Cherry
[Link]([Link]()); // 4 [Link]([Link]("Banana")); // true
[Link]("Banana"); // remove by value [Link](0); // remove by index
[Link](fruits); // sort alphabetically [Link](fruits); // reverse order
LinkedList
Doubly-linked list. Fast O(1) add/remove at ends. Slow O(n) index access. Also implements Deque.
LinkedList<Integer> ll = new LinkedList<>(); [Link](10); [Link](20); [Link](30);
[Link](5); // [5, 10, 20, 30] [Link](40); // [5, 10, 20, 30, 40] [Link](); //
removes 5 [Link](); // removes 40 [Link]([Link]()); // 10 — view head
without removing [Link]([Link]()); // 10 — remove and return head
HashMap
Key-value pairs. Keys are unique. Order NOT guaranteed. get/put O(1) average.
HashMap<String, Integer> scores = new HashMap<>(); [Link]("Alice", 95); [Link]("Bob",
82); [Link]("Carol", 88); [Link]("Alice", 99); // overwrites Alice's score
[Link]([Link]("Bob")); // 82 [Link]([Link]("Dave",0));
// 0 (key missing) [Link]([Link]("Carol")); // true
[Link]("Carol"); for ([Link]<String,Integer> e : [Link]())
[Link]([Link]() + " -> " + [Link]());
A: JVM (Java Virtual Machine) executes bytecode. It is platform-specific — there are separate JVMs for
Windows, Linux, Mac. JRE (Java Runtime Environment) = JVM + core class libraries. Needed to RUN Java
programs. JDK (Java Development Kit) = JRE + development tools (javac compiler, debugger, javadoc).
Needed to WRITE and COMPILE Java programs. Relationship: JDK contains JRE, which contains JVM.
A: == compares references — it checks if both variables point to the exact same object in memory. .equals()
compares content (logical equality). For primitive types, == compares values. For objects (especially Strings),
always use .equals() for content comparison. Example: String a = new String('Hi'); String b = new String('Hi');
a==b is false (different objects), but [Link](b) is true (same content). Exception: String literals like 'Hi'=='Hi'
may be true due to the String Pool.
A: Using a BankAccount example: (1) Encapsulation — balance is private; only accessible via deposit() and
withdraw() methods that validate inputs. Data is protected. (2) Inheritance — SavingsAccount extends
BankAccount, inheriting all account operations and adding interest calculation. (3) Polymorphism —
calculateInterest() behaves differently in SavingsAccount vs CurrentAccount (method overriding). (4)
Abstraction — the user calls withdraw() without knowing if the bank uses SQL, NoSQL, or a blockchain ledger.
Complexity is hidden.
A: Overloading: same method name in the SAME class, different parameters (type/number/order). Resolved
at compile time — static/compile-time polymorphism. Return type alone cannot differentiate overloaded
methods. Overriding: child class provides a new implementation for a method already defined in the parent.
Same name + same parameters. Resolved at runtime — dynamic polymorphism. Use @Override. Cannot
make the method more private in the child. Static and final methods cannot be overridden.
A: Abstract class: can have abstract AND concrete methods, constructors, any access modifiers, instance
variables. A class can extend only ONE abstract class. Interface (Java 8+): can have abstract methods,
default methods (with body), and static methods. All variables are public static final (constants). No
constructors. A class can implement MULTIPLE interfaces. Use abstract class when related classes share
code (IS-A). Use interface for capabilities needed by unrelated classes (CAN-DO) or when multiple inheritance
is needed.
Q: 7. What is the 'static' keyword? Can static methods access instance variables?
A: static means the member belongs to the CLASS itself, not to any specific object. A static variable is shared
by ALL objects — one copy. A static method can be called without creating an object: [Link](). A
static block runs once when the class is loaded. NO — a static method CANNOT directly access instance
(non-static) variables, because instance members need an object to exist and static methods can be called
without any object. However, a static method CAN access static variables.
A: String is IMMUTABLE — every modification (concat, replace) creates a NEW String object. Slow for many
modifications. StringBuilder is MUTABLE — modifies the same object without creating new ones. Fast, but
NOT thread-safe. Use in single-threaded code. StringBuffer is MUTABLE and thread-safe (all methods are
synchronized). Slower than StringBuilder due to synchronisation overhead. Use in multi-threaded code. Rule:
fixed text → String. Many changes in one thread → StringBuilder. Many changes across threads →
StringBuffer.
A: ArrayList uses a dynamic array. get(i) is O(1) — very fast random access. add/remove in the middle is O(n)
— must shift elements. Best for frequent reads. LinkedList uses a doubly linked list. get(i) is O(n) — must
traverse. addFirst/addLast/removeFirst/removeLast are O(1) — very fast. Best for frequent
insertions/deletions at ends. Memory: LinkedList uses more memory per element (data + 2 pointers). For most
use cases ArrayList is preferred because modern iteration is still fast.
A: HashMap: no guaranteed order, uses hashing internally, get/put O(1) average, allows one null key and
multiple null values. TreeMap: keys are SORTED in natural or custom order, uses Red-Black tree, get/put
O(log n), does NOT allow null keys. Choose HashMap when you need fast unsorted lookup. Choose TreeMap
when you need entries in sorted key order (e.g., alphabetical menu, range queries). Both are NOT thread-safe
— use ConcurrentHashMap for thread safety.
A: 'throw' is used INSIDE a method body to ACTUALLY RAISE an exception at a specific line: throw new
IllegalArgumentException('Invalid age'). Only one exception at a time. 'throws' is used in the METHOD
SIGNATURE to DECLARE that the method MIGHT throw one or more checked exceptions, warning callers to
handle them: public void readFile() throws IOException, SQLException. It does not throw anything itself — it is
just a warning. Multiple exceptions can be declared with throws, separated by commas.
A: Garbage Collection (GC) is Java's automatic memory management. When an object in the heap is no
longer referenced by any variable (unreachable), the GC automatically reclaims its memory. Programmers do
NOT manually free memory (unlike C/C++). The GC runs as a background daemon thread. You can suggest it
runs with [Link]() but there is no guarantee of when it will run. Before collecting, GC calls finalize() on the
object (deprecated in Java 9+). Common algorithms: Mark-and-Sweep, Generational GC, G1 GC.
A: Applied to a variable: value cannot be changed after assignment (constant). Applied to a method: cannot be
overridden in any subclass. Applied to a class: cannot be extended — String, Integer, and Math are all final
classes. Blank final variable: declared final but assigned exactly once in the constructor. static final creates
class-level constants: public static final double PI = 3.14159. The final keyword is important in immutable
classes and Singleton patterns.
A: Singleton ensures only ONE instance of a class exists in the entire application and provides a global access
point. Steps: (1) Make the constructor private — no external new allowed. (2) Declare a private static variable
of the class type. (3) Provide a public static getInstance() method that creates the instance only the first time it
is called, then returns the same instance on all future calls. Common uses: DB connections, Logger, Config.
For thread safety, use synchronized getInstance() or double-checked locking pattern.
Q: 15. What is an Immutable class? Name examples and how to create one.
A: An immutable class cannot have its state changed after creation. Famous examples: String, Integer,
Double, LocalDate. To create: (1) Declare class as final. (2) Make all fields private and final. (3) Initialise all
fields in the constructor only. (4) Provide only getters — no setters. (5) For mutable fields, return a defensive
copy in the getter. Benefits: automatically thread-safe (no synchronisation needed), can be safely cached and
shared as HashMap keys, predictable state.
A: Comparable ([Link]): implemented BY the class itself. Has one method: compareTo(Object). Defines the
NATURAL ordering of the class. Example: String, Integer implement Comparable. Comparator ([Link]): a
SEPARATE class or lambda that defines CUSTOM ordering. Has one method: compare(T o1, T o2). Use it
when you need multiple sort orders, or cannot modify the class. Example: sort Employees by salary in one
place, by name in another. With Java 8, Comparator can be a concise lambda:
[Link](Employee::getSalary).
A: Autoboxing: Java automatically converts a primitive to its Wrapper object (int → Integer) when needed —
e.g., when adding to a Collection or assigning to a Wrapper variable. Unboxing: automatic conversion back
from Wrapper to primitive (Integer → int). Pitfalls: (1) Unboxing a null wrapper throws NullPointerException —
always check for null before unboxing. (2) Autoboxing in tight loops creates many temporary objects and can
hurt performance. (3) Integer uses a cache (-128 to 127), so [Link](100)==[Link](100) is
true, but [Link](200)==[Link](200) is false!
A: A lambda expression (Java 8+) is a concise anonymous function — no name, no class, no access modifier.
Syntax: (parameters) -> expression. A functional interface has exactly ONE abstract method (SAM — Single
Abstract Method). @FunctionalInterface annotation enforces this. Common ones: Predicate (test — returns
boolean), Function (apply — T to R), Consumer (accept — takes T, returns void), Supplier (get — produces
T). Lambdas make code dramatically shorter for sorting, event handling, stream operations, and threading.
Q: 19. What is the Stream API? What are intermediate and terminal operations?
A: Stream API ([Link], Java 8+) processes collections in a declarative pipeline style. Streams are
LAZY — processing only starts at the terminal operation. Pipeline: Source → Intermediate operations →
Terminal operation. Intermediate operations (return another Stream, lazy): filter(), map(), sorted(), distinct(),
limit(), skip(), flatMap(). Terminal operations (trigger processing, return result): collect(), count(), reduce(),
forEach(), min(), max(), findFirst(), anyMatch(), allMatch(). A stream can be consumed only ONCE — a
second terminal operation throws IllegalStateException. Use parallelStream() for parallel processing.
A: Checked exceptions are verified by the compiler at compile time. You MUST either surround with try-catch
or declare with 'throws' in the method signature. Examples: IOException, SQLException,
ClassNotFoundException, FileNotFoundException. Unchecked exceptions (subclasses of RuntimeException)
are NOT checked at compile time. They represent programming bugs — null access, invalid array index, etc.
You do NOT need to declare them. Examples: NullPointerException, ArrayIndexOutOfBoundsException,
ClassCastException, ArithmeticException. Errors (StackOverflowError, OutOfMemoryError) are also
unchecked and represent severe JVM problems.
Final advice: Don't just memorise answers — understand the WHY behind each concept. If an interviewer
asks a follow-up, you should explain it from first principles. Write code, make mistakes, debug, and learn. That
is how real Java developers are made!