Java Programming
Complete Exam-Ready Notes
All 8 Chapters | Full Theory | Clean Code Examples
JVM · OOP · Inheritance · Interfaces · Exception Handling · Threads · I/O · JavaFX
Chapter 1: Basics of Java
1.1 Java Platform and Architecture
Java is a high-level, object-oriented, platform-independent programming language developed by Sun
Microsystems in 1995. The guiding principle of Java is Write Once, Run Anywhere (WORA) — code
written and compiled on one platform can run on any other platform without modification. This is made
possible through the use of bytecode and the Java Virtual Machine (JVM).
The Three Pillars of the Java Platform
• JDK (Java Development Kit) — The complete toolkit for developing Java programs. It includes
the compiler (javac), debugger (jdb), documentation generator (javadoc), and everything inside
the JRE.
• JRE (Java Runtime Environment) — Used to run (not develop) Java programs. It includes the
JVM plus the standard Java Class Libraries (APIs).
• JVM (Java Virtual Machine) — The engine that actually executes Java bytecode. It is platform-
dependent, meaning there is a separate JVM for Windows, macOS, and Linux, but the bytecode
it runs is platform-independent.
📌 Key Point: Remember the hierarchy: JDK ⊃ JRE ⊃ JVM. For development, install the JDK. To
only run programs, the JRE is sufficient.
How the JVM Works
When you run a Java program, the JVM performs several key operations:
• Class Loading: The ClassLoader loads .class files into memory.
• Bytecode Verification: The bytecode is checked for security and correctness.
• Memory Management: The JVM manages allocation and cleanup (Garbage Collection).
• Execution: The Execution Engine converts bytecode to machine code using the JIT (Just-In-
Time) Compiler at runtime.
1.2 Java Program Structure, Compilation & Execution
Every Java program follows a specific structure. Here is the simplest Java program, with each part
explained:
// Every Java file must have a class whose name matches the filename
public class HelloWorld {
// main() is the entry point — JVM looks for this exact signature
public static void main(String[] args) {
[Link]("Hello, World!"); // prints text + newline
}
}
Breaking Down the main() Method
• public — Accessible from anywhere, so the JVM can call it.
• static — No object needs to be created; JVM calls it directly on the class.
• void — Returns nothing to the caller (the JVM).
• String[] args — Holds command-line arguments passed when running the program.
Compilation and Execution Steps
Step 1: Write code → [Link] (Source Code — human readable)
Step 2: Compile → javac [Link]
Creates [Link] (Bytecode — platform neutral)
Step 3: Run → java HelloWorld
JVM converts bytecode → machine code → Output on screen
📌 Key Point: The .class file contains bytecode, NOT machine code. The JVM's JIT compiler
converts bytecode to native machine code at runtime, which is why Java achieves near-native
performance.
1.3 Data Types in Java
Java is a strongly typed language — every variable must be declared with a specific type before use.
Data types are divided into two categories: Primitive (stored directly in memory) and Reference (store a
reference/address to an object).
Data Type Size Default Value Range / Notes
byte 1 byte (8 bits) 0 -128 to 127
short 2 bytes (16 bits) 0 -32,768 to 32,767
int 4 bytes (32 bits) 0 -2,147,483,648 to 2,147,483,647
long 8 bytes (64 bits) 0L -9.2×10¹⁸ to 9.2×10¹⁸ (needs L
suffix)
float 4 bytes (32 bits) 0.0f ~7 decimal digits precision (needs
f suffix)
double 8 bytes (64 bits) 0.0d ~15 decimal digits precision
char 2 bytes (16 bits) \u0000 0 to 65,535 (Unicode characters)
boolean 1 bit false true or false only
String Object (variable) null Sequence of characters
(Reference type)
Variable Examples
int age = 25; // integer
double salary = 50000.50; // decimal number
char grade = 'A'; // single character (use single quotes)
boolean isJavaFun = true; // true or false
String name = "Rahul"; // text (use double quotes)
long population = 1400000000L; // long — needs L suffix
float pi = 3.14f; // float — needs f suffix
Types of Variables
• Local Variable — Declared inside a method. No default value; must be initialized before use.
• Instance Variable — Declared inside the class but outside methods. Has a default value (0,
null, false).
• Static Variable — Declared with the static keyword. Shared across ALL objects of the class.
Arrays
An array stores multiple values of the same type in contiguous memory locations. Arrays are zero-
indexed (first element is at index 0).
// Single Dimensional Array
int[] marks = new int[5]; // declare & create (all zeros)
int[] marks = {90, 85, 78, 92, 88}; // declare, create & initialize
[Link](marks[0]); // access first element → 90
[Link]([Link]); // number of elements → 5
// Two Dimensional Array (matrix)
int[][] matrix = new int[3][3];
int[][] matrix = {{1,2,3}, {4,5,6}, {7,8,9}};
1.4 Operators in Java
Operators perform operations on variables and values. Java has several categories of operators:
Category Operators Example
Arithmetic + – * / % ++ -- a + b, a++, a % b
Relational == != > < >= <= a == b, a > b
Logical && || ! a && b, !a, a || b
Bitwise & | ^ ~ << >> >>> a & b, a << 2
Assignment = += -= *= /= %= a += 5 (same as a = a + 5)
Ternary condition ? val1 : val2 x > 0 ? "pos" : "neg"
instanceof obj instanceof ClassName obj instanceof String
Type Conversion
• Widening (Implicit): Smaller → Larger type, happens automatically. byte → short → int → long
→ float → double
• Narrowing (Explicit): Larger → Smaller type, requires manual casting. Data may be lost.
// Widening — automatic, no data loss
int i = 100;
double d = i; // int automatically becomes double
[Link](d); // Output: 100.0
// Narrowing — manual cast required, data loss possible
double pi = 3.99;
int x = (int) pi; // fractional part is dropped (truncated, not rounded)
[Link](x); // Output: 3 (not 4!)
Chapter 2: Conditional & Looping Statements
2.1 Conditional Statements
Conditional statements allow a program to make decisions and execute different code blocks based on
whether a condition evaluates to true or false.
if Statement
Executes the block only if the condition is true. If the condition is false, the block is skipped entirely.
int age = 18;
if (age >= 18) {
[Link]("You are eligible to vote.");
}
// If age were 15, nothing would be printed.
if-else Statement
Provides two paths: one for when the condition is true, and one for when it is false.
int marks = 45;
if (marks >= 50) {
[Link]("Pass");
} else {
[Link]("Fail"); // marks < 50, so this runs
}
else-if Ladder
Used when there are multiple mutually exclusive conditions to check in sequence. Only the first
matching block executes.
int marks = 75;
if (marks >= 90) {
[Link]("Grade: A+");
} else if (marks >= 80) {
[Link]("Grade: A");
} else if (marks >= 70) {
[Link]("Grade: B"); // This prints — 75 >= 70
} else if (marks >= 60) {
[Link]("Grade: C");
} else {
[Link]("Grade: F");
}
switch-case Statement
A cleaner alternative to an else-if ladder when comparing a single variable against many fixed values.
Works with int, char, String, and enum types.
int day = 3;
switch (day) {
case 1: [Link]("Monday"); break;
case 2: [Link]("Tuesday"); break;
case 3: [Link]("Wednesday"); break; // This runs
case 4: [Link]("Thursday"); break;
default: [Link]("Weekend"); // runs if no case matches
}
📌 Key Point: Always use 'break' in switch-case to prevent fall-through. Without break, execution
continues into the next case even if it doesn't match!
2.2 Looping Statements
Loops allow a block of code to execute repeatedly. Java provides four types of loops, each suited for
different scenarios.
for Loop
Best used when the number of iterations is known in advance. The three parts (initialization, condition,
update) are all written in one line.
// for(initialization; condition; update)
for (int i = 1; i <= 5; i++) {
[Link]("Count: " + i);
}
// Output: Count: 1 Count: 2 Count: 3 Count: 4 Count: 5
while Loop
Best used when the number of iterations is NOT known. The condition is checked BEFORE the loop
body runs, so if the condition is initially false, the body never executes.
int i = 1;
while (i <= 5) {
[Link](i);
i++; // increment — without this, the loop runs forever (infinite loop)
}
do-while Loop
Similar to while, but the condition is checked AFTER the loop body. This guarantees the body executes
at least once, even if the condition is false from the start.
int i = 1;
do {
[Link](i);
i++;
} while (i <= 5);
// Even if 'i' started at 10, the body would execute once before checking.
Enhanced for Loop (for-each)
A simplified loop designed for iterating over arrays and collections. It is cleaner and eliminates the risk
of index errors.
int[] numbers = {10, 20, 30, 40, 50};
// 'num' takes each value from 'numbers' in order
for (int num : numbers) {
[Link](num);
}
// Also works with ArrayList
ArrayList<String> names = new ArrayList<>();
[Link]("Alice");
[Link]("Bob");
for (String name : names) {
[Link](name);
}
Loop Type When to Use Condition Checked
for Number of iterations is known Before each iteration
while Number of iterations unknown; may not run Before each iteration
do-while Must execute at least once After each iteration
for-each Iterating over an array or collection Before each element
2.3 Jump Statements: break, continue, and Labels
break
Immediately exits the nearest enclosing loop or switch statement.
for (int i = 1; i <= 10; i++) {
if (i == 5) break; // loop stops when i reaches 5
[Link](i);
}
// Output: 1 2 3 4
continue
Skips the remaining statements in the current iteration and jumps to the next iteration of the loop.
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) continue; // skip even numbers
[Link](i);
}
// Output: 1 3 5 7 9 (only odd numbers)
Labelled break and continue
Labels allow break and continue to target an outer loop in nested loop situations. A label is simply a
name followed by a colon placed before the loop.
// Labelled break — exits the OUTER loop entirely
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) break outer; // breaks out of the outer loop
[Link](i + "," + j);
}
}
// Output: 1,1
// Labelled continue — continues to next iteration of the OUTER loop
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) continue outer; // skip to next i
[Link](i + "," + j + " ");
}
}
// Output: 1,1 2,1 3,1
Chapter 3: Basics of Object-Oriented Programming
3.1 Core OOP Concepts
Object-Oriented Programming (OOP) is a programming paradigm that organises software design
around objects (data + behaviour) rather than functions and logic. Java is a fully OOP language built on
four core principles:
Pillar Meaning Example in Java
Encapsulation Bundling data (variables) and methods into a private fields with public
class; hiding internal details using access getters/setters
modifiers.
Abstraction Showing only essential features to the user; Abstract classes and Interfaces
hiding the implementation complexity.
Inheritance A child class acquires properties and class Dog extends Animal
behaviours of a parent class, promoting code
reuse.
Polymorphism One interface, multiple implementations. The Method Overloading &
same method behaves differently depending on Overriding
the object.
3.2 Classes and Objects
A Class is a blueprint or template that defines what properties (variables) and behaviours (methods) an
object will have. An Object is a real, tangible instance created from that blueprint — it occupies actual
memory and has real values.
Analogy: Class vs Object
• Class = The blueprint/design of a house (no physical existence)
• Object = An actual house built from that blueprint (has physical existence)
// Class definition — the blueprint
class Student {
// Instance variables (each object has its own copies)
String name;
int rollNo;
double marks;
// Method — defines behaviour
void displayInfo() {
[Link]("Name: " + name);
[Link]("Roll No: " + rollNo);
[Link]("Marks: " + marks);
}
}
// Creating objects from the blueprint
class Main {
public static void main(String[] args) {
Student s1 = new Student(); // 'new' allocates memory for the object
[Link] = "Rahul";
[Link] = 101;
[Link] = 92.5;
[Link]();
Student s2 = new Student(); // a completely separate object
[Link] = "Priya";
[Link] = 102;
[Link] = 88.0;
[Link]();
}
}
Access Specifiers (Access Modifiers)
Access specifiers control which parts of your program can see and use a class member (variable or
method). They are fundamental to implementing encapsulation.
Specifier Same Class Same Package Subclass Outside
Package
private Yes No No No
(default / package) Yes Yes No No
protected Yes Yes Yes No
public Yes Yes Yes Yes
3.3 Constructors
A constructor is a special method that is automatically called when a new object is created using the
new keyword. Its purpose is to initialise the object's state. A constructor has the same name as the
class and has no return type (not even void).
Types of Constructors
• Default Constructor — No parameters. JVM provides one automatically if you don't define any
constructor.
• Parameterised Constructor — Accepts arguments so you can initialise the object with specific
values.
• Copy Constructor — Accepts an object of the same class and creates a new object with the
same values.
class Student {
String name;
int age;
// 1. Default Constructor
Student() {
name = "Unknown";
age = 0;
[Link]("Default constructor called");
}
// 2. Parameterised Constructor
Student(String n, int a) {
name = n;
age = a;
[Link]("Parameterised constructor called");
}
// 3. Copy Constructor
Student(Student s) {
name = [Link];
age = [Link];
}
void display() { [Link](name + " - " + age); }
}
class Main {
public static void main(String[] args) {
Student s1 = new Student(); // calls Default constructor
Student s2 = new Student("Priya", 20); // calls Parameterised constructor
Student s3 = new Student(s2); // calls Copy constructor
[Link](); // Unknown - 0
[Link](); // Priya - 20
[Link](); // Priya - 20 (copy of s2)
}
}
3.4 The 'this' Keyword
The this keyword is a reference to the current object — the object that is currently executing the method
or constructor. It is used in several important situations:
• To distinguish instance variables from local/parameter variables with the same name.
• To call another constructor of the same class — called constructor chaining (this() call must be
the first statement).
• To pass the current object as an argument to a method.
• To return the current object from a method (used in method chaining).
class Person {
String name;
int age;
Person(String name, int age) {
// Without 'this', the parameter 'name' would shadow the instance variable
[Link] = name; // '[Link]' = instance variable, 'name' = parameter
[Link] = age;
}
// Constructor chaining — calls the parameterised constructor above
Person() {
this("Default", 0); // MUST be the very first statement
}
}
3.5 Static Members, Blocks & Inner Classes
Static Members
A static member belongs to the class itself, not to any individual object. All objects share the same copy
of a static variable, and static methods can be called without creating an object.
class Counter {
static int count = 0; // ONE shared copy for all objects
Counter() {
count++; // incremented every time a new object is created
}
static void showCount() { // called without an object
[Link]("Total objects: " + count);
}
}
class Main {
public static void main(String[] args) {
new Counter();
new Counter();
new Counter();
[Link](); // Output: Total objects: 3
}
}
Static Block
A static block runs once when the class is first loaded into memory, even before main() or any
constructor executes. It is used to initialise static variables that require complex setup.
class Demo {
static int x;
static { // runs once at class loading time
x = 100;
[Link]("Static block executed. x = " + x);
}
public static void main(String[] args) {
[Link]("Main method. x = " + x);
}
}
// Output:
// Static block executed. x = 100
// Main method. x = 100
Inner Class
A class defined inside another class is called an inner class. It can access the outer class's members
(including private ones). Types: Non-static (Regular), Static, Local (inside a method), and Anonymous.
class Outer {
private int x = 10;
class Inner { // non-static inner class
void show() {
[Link]("x = " + x); // accesses outer's private field
}
}
}
class Main {
public static void main(String[] args) {
Outer o = new Outer();
[Link] i = [Link] Inner(); // inner object needs an outer object
[Link](); // Output: x = 10
}
}
Chapter 4: Inheritance, Polymorphism & Wrapper Classes
4.1 Inheritance
Inheritance is the mechanism by which one class (the child/subclass) acquires the properties (fields)
and behaviours (methods) of another class (the parent/superclass). This promotes code reusability —
you write common code once in the parent class and reuse it in all child classes. The keyword extends
establishes the inheritance relationship.
Type Description Supported in Java?
Single One child inherits from one parent Yes
Multilevel A → B → C (chain of inheritance) Yes
Hierarchical Multiple children inherit from one parent Yes
Multiple One child inherits from multiple parents No — causes Diamond Problem
(classes)
Hybrid Combination of multiple types Only via Interfaces
📌 Key Point: Java does NOT support multiple inheritance through classes to avoid the 'Diamond
Problem' (ambiguity when two parents have the same method). Multiple inheritance is achieved using
Interfaces instead.
// Single Inheritance
class Animal {
String name;
void eat() { [Link](name + " is eating."); }
}
class Dog extends Animal { // Dog IS-A Animal
void bark() { [Link](name + " is barking."); }
}
// Multilevel Inheritance
class Animal { void breathe() { [Link]("Breathing"); } }
class Mammal extends Animal { void walk() { [Link]("Walking"); } }
class Dog extends Mammal { void bark() { [Link]("Barking"); } }
class Main {
public static void main(String[] args) {
Dog d = new Dog();
[Link] = "Tommy";
[Link](); // inherited from Animal
[Link](); // inherited from Mammal
[Link](); // Dog's own method
}
}
4.2 super, this, and final Keywords
super Keyword
The super keyword refers to the immediate parent class. It is used to access parent class members that
are hidden by the child class, or to call the parent constructor.
• [Link] — Access parent's variable (when child has same name).
• [Link]() — Call parent's method (when child has overridden it).
• super() — Call parent's constructor. MUST be the first statement in child's constructor.
class Animal {
String type = "Animal";
void sound() { [Link]("Some sound"); }
Animal() { [Link]("Animal constructor"); }
}
class Dog extends Animal {
String type = "Dog"; // hides parent's 'type' variable
Dog() {
super(); // calls Animal's constructor — MUST be first line
[Link]("Dog constructor");
}
void sound() {
[Link](); // calls Animal's sound()
[Link]("Woof!");
}
void printType() {
[Link]([Link]); // prints "Animal"
[Link]([Link]); // prints "Dog"
}
}
final Keyword
Applied To Effect Example
Variable Value cannot be changed after final int MAX = 100;
assignment (constant)
Method Cannot be overridden in any subclass final void display() { }
Class Cannot be extended (subclassed) final class MyClass { }
4.3 Method Overloading and Method Overriding
Method Overloading — Compile-time Polymorphism
Overloading means having multiple methods in the same class with the same name but different
parameter lists (different number, type, or order of parameters). The compiler decides which method to
call at compile time based on the arguments provided.
class Calculator {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // different type
int add(int a, int b, int c) { return a + b + c; } // different count
}
// The compiler chooses the correct version at compile time
Calculator c = new Calculator();
[Link](5, 3); // calls version 1 → 8
[Link](2.5, 1.5); // calls version 2 → 4.0
[Link](1, 2, 3); // calls version 3 → 6
Method Overriding — Run-time Polymorphism
Overriding means a subclass provides its own specific implementation of a method that is already
defined in the parent class. Both methods must have the same name and same parameter list. The
JVM decides which version to run at runtime based on the actual object type — this is called Dynamic
Method Dispatch.
class Shape {
void draw() { [Link]("Drawing a shape"); }
}
class Circle extends Shape {
@Override
void draw() { [Link]("Drawing a Circle"); }
}
class Rectangle extends Shape {
@Override
void draw() { [Link]("Drawing a Rectangle"); }
}
class Main {
public static void main(String[] args) {
Shape s; // parent reference
s = new Circle();
[Link](); // Output: Drawing a Circle ← decided at
RUNTIME
s = new Rectangle();
[Link](); // Output: Drawing a Rectangle
}
}
Feature Overloading Overriding
Location Same class Parent & Child classes
Method Signature Must be different Must be exactly the same
Return Type Can differ Must match (or be covariant
subtype)
Binding Compile-time (Static) Run-time (Dynamic)
static/final methods Can be overloaded Cannot be overridden
4.4 String, StringBuffer, and StringBuilder
Java provides three classes for working with text, each with different characteristics suited for different
situations.
• String — Immutable. Once a String object is created, its value cannot change. Any modification
creates a new object in the String Pool.
• StringBuffer — Mutable and thread-safe (all methods are synchronised). Use in multi-threaded
environments.
• StringBuilder — Mutable but NOT thread-safe. Faster than StringBuffer. Use in single-threaded
environments.
// String — immutable
String s = "Hello";
s = s + " World"; // creates a NEW String object; old 'Hello' still in
pool
[Link](s); // Hello World
// StringBuffer — mutable, thread-safe
StringBuffer sb = new StringBuffer("Hello");
[Link](" World"); // modifies the SAME object in memory
[Link](5, ","); // Hello, World
[Link](); // dlroW ,olleH
[Link](0, 5); // ,olleH
[Link](sb);
// StringBuilder — mutable, faster, not thread-safe
StringBuilder sbl = new StringBuilder("Java");
[Link](" Programming");
[Link](sbl); // Java Programming
Feature String StringBuffer StringBuilder
Mutability Immutable Mutable Mutable
Thread Safety Yes (immutable) Yes (synchronised) No
Speed Slowest (new Slower Fastest
objects)
Storage String Pool Heap Heap
Best Used When Value rarely Multi-threaded access Single-threaded,
changes frequent changes
4.5 Wrapper Classes & Autoboxing
Wrapper classes convert Java's primitive types into objects. This is necessary because Java's
Collections Framework (ArrayList, HashMap, etc.) can only store objects, not primitives.
Primitive Wrapper Class Example
int Integer Integer i = [Link](5);
double Double Double d = [Link](3.14);
char Character Character c = [Link]('A');
boolean Boolean Boolean b = [Link](true);
long Long Long l = [Link](100L);
byte Byte Byte by = [Link]((byte) 10);
Autoboxing and Unboxing
Since Java 5, the compiler automatically handles conversion between primitives and their wrapper
objects.
// Autoboxing: primitive → Wrapper Object (automatic)
int x = 42;
Integer obj = x; // compiler converts this to [Link](42)
// Unboxing: Wrapper Object → primitive (automatic)
Integer obj2 = 100;
int y = obj2; // compiler converts this to [Link]()
// Useful Wrapper methods
int parsed = [Link]("123"); // String → int
String str = [Link](456); // int → String
int max = Integer.MAX_VALUE; // 2147483647
int min = Integer.MIN_VALUE; // -2147483648
Chapter 5: Interface, Abstract Class & Exception Handling
5.1 Interface
An interface is a completely abstract contract that defines WHAT a class must do, without specifying
HOW it should do it. It is the purest form of abstraction in Java. By default, all methods in an interface
are implicitly public and abstract, and all variables are implicitly public, static, and final.
A class uses the implements keyword to adopt an interface's contract, and it must provide
implementations for all abstract methods. Importantly, a class can implement multiple interfaces — this
is how Java achieves multiple inheritance.
interface Drawable {
void draw(); // implicitly public abstract
int MAX_SIZE = 100; // implicitly public static final
}
interface Colorable {
void setColor(String c);
}
// A class can implement MULTIPLE interfaces — multiple inheritance!
class Circle implements Drawable, Colorable {
@Override
public void draw() {
[Link]("Drawing Circle");
}
@Override
public void setColor(String c) {
[Link]("Color set to: " + c);
}
}
Default and Static Methods (Java 8+)
Before Java 8, interfaces could only have abstract methods. Java 8 introduced default methods (with a
body, optional to override) and static methods (called on the interface directly) to allow interfaces to
evolve without breaking existing code.
interface Vehicle {
void accelerate(); // abstract — MUST be implemented by the class
default void fuelType() { // default — optional to override
[Link]("Default fuel: Petrol");
}
static void info() { // static — called as [Link]()
[Link]("Vehicle Interface v1.0");
}
}
class Car implements Vehicle {
public void accelerate() { [Link]("Car accelerating"); }
// fuelType() is inherited as-is; no need to override unless desired
}
5.2 Abstract Class
An abstract class sits between a regular class (fully implemented) and an interface (fully abstract). It
can have both abstract methods (no body — must be implemented by subclasses) and concrete
methods (with a full body). An abstract class cannot be instantiated — you cannot create objects of it
directly. It is meant to be extended.
Use an abstract class when you want to share code among related classes but also enforce that
subclasses implement certain methods.
abstract class Shape {
String color; // instance variable — subclasses inherit this
// Abstract method — subclasses MUST provide implementation
abstract double area();
// Concrete method — already implemented; subclasses inherit it
void display() {
[Link]("Color: " + color + ", Area: " + area());
}
}
class Circle extends Shape {
double radius;
Circle(double r) { radius = r; }
@Override
double area() { return [Link] * radius * radius; } // must implement
}
class Main {
public static void main(String[] args) {
Shape s = new Circle(5); // OK — Circle is concrete
[Link] = "Red";
[Link]();
// Shape s2 = new Shape(); // ERROR! Cannot instantiate abstract class
}
}
Feature Interface Abstract Class
Instantiation Cannot be instantiated Cannot be instantiated
Methods Abstract by default (default/static Can have abstract + concrete
in Java 8+) methods
Variables Only public static final constants Any type (instance, static, final)
Constructor No constructor Can have constructor
Multiple inheritance Supported (implements multiple) Not supported (extends only one)
Keyword used implements extends
Use when Pure abstraction / multiple Partial shared implementation
inheritance
5.3 Exception Handling
An exception is an unexpected event (like dividing by zero, accessing a null object, or reading a
missing file) that disrupts the normal flow of a program. Java provides a structured exception handling
mechanism using try-catch-finally blocks so that programs can recover gracefully from errors.
Types of Exceptions
• Checked Exception — The compiler forces you to handle these. They are checked at compile
time. Example: IOException, SQLException, FileNotFoundException.
• Unchecked Exception (Runtime Exception) — Not checked by the compiler; occur at runtime.
Example: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException.
• Error — Serious system-level problems that cannot be caught or recovered from. Example:
OutOfMemoryError, StackOverflowError.
try-catch-finally
class ExceptionDemo {
public static void main(String[] args) {
try {
// Code that might throw an exception goes here
int a = 10, b = 0;
int result = a / b; // throws ArithmeticException
[Link](result); // this line is skipped
}
catch (ArithmeticException e) {
// Handles the specific exception
[Link]("Error: " + [Link]()); // / by zero
}
catch (NullPointerException e) {
// Multiple catch blocks allowed — catches different exception types
[Link]("Null pointer: " + [Link]());
}
finally {
// ALWAYS executes — whether or not an exception occurred
// Used to release resources (close files, DB connections, etc.)
[Link]("Finally block always runs");
}
}
}
// Output:
// Error: / by zero
// Finally block always runs
throw vs throws
Keyword Purpose Used In Example
throw Actually creates and throws Inside a method throw new
an exception object body ArithmeticException("msg");
throws Declares that a method might Method void method() throws
throw certain exceptions signature/declaration IOException { }
(warns the caller)
// Using throw — manually trigger an exception
void checkAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative: " + age);
}
[Link]("Valid age: " + age);
}
// Using throws — warn callers that this method may throw IOException
void readFile(String path) throws IOException {
FileReader fr = new FileReader(path); // might throw IOException
}
// The caller MUST handle the declared exception
try {
readFile("[Link]");
} catch (IOException e) {
[Link]("File error: " + [Link]());
}
User-Defined (Custom) Exceptions
You can create your own exception classes by extending Exception (for checked) or RuntimeException
(for unchecked). Custom exceptions make error messages more meaningful and specific to your
application's domain.
// Custom checked exception
class InsufficientFundsException extends Exception {
double shortfall;
InsufficientFundsException(double amount) {
super("Insufficient funds! Short by: " + amount);
[Link] = amount;
}
}
class BankAccount {
double balance = 500.0;
void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount - balance);
}
balance -= amount;
[Link]("Withdrawn: " + amount + ", Balance: " + balance);
}
}
class Main {
public static void main(String[] args) {
BankAccount acc = new BankAccount();
try {
[Link](800); // will throw since balance is only 500
} catch (InsufficientFundsException e) {
[Link]([Link]()); // Insufficient funds!
Short by: 300.0
[Link]("Need extra: " + [Link]);
}
}
}
Chapter 6: Concurrency Control – Threads
6.1 What is a Thread?
A thread is the smallest unit of execution within a program. Java supports multithreading, which allows
multiple threads to run concurrently within the same program. This improves performance, especially
for tasks like downloading files, processing data, and building responsive GUIs where one thread can
handle the UI while another does heavy computation.
6.2 Thread Life Cycle
Every thread goes through a series of states during its lifetime:
State Description
NEW Thread object is created but start() has not been called yet.
RUNNABLE start() has been called; the thread is ready to run and waiting for CPU time.
RUNNING The JVM's thread scheduler has given it CPU time and it is executing.
BLOCKED / WAITING Thread is waiting for a resource (e.g., a lock) or for another thread to
complete.
TERMINATED The run() method has completed. The thread is done and cannot be
restarted.
6.3 Creating Threads
Method 1: Extending the Thread Class
Override the run() method in a subclass of Thread. Call start() (not run() directly!) to begin execution in
a new thread.
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
[Link](getName() + " - Count: " + i);
try { [Link](500); } catch (InterruptedException e) {}
}
}
}
class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
[Link](); // ALWAYS call start(), NOT run() — run() won't create a new
thread
[Link](); // both threads run concurrently
}
}
Method 2: Implementing the Runnable Interface (Preferred)
This approach is preferred because Java does not support multiple class inheritance. If your class
already extends another class, you can still implement Runnable and pass it to a Thread object.
class MyTask implements Runnable {
String taskName;
MyTask(String name) { taskName = name; }
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
[Link](taskName + " - Step " + i);
try { [Link](300); } catch (InterruptedException e) {}
}
}
}
class Main {
public static void main(String[] args) {
MyTask task1 = new MyTask("Download");
MyTask task2 = new MyTask("Upload");
Thread t1 = new Thread(task1); // wrap Runnable in a Thread
Thread t2 = new Thread(task2);
[Link]();
[Link]();
}
}
6.4 Important Thread Methods
Method Description
start() Starts the thread by internally calling run() in a new thread context.
run() Contains the thread's task code. Do NOT call directly — use start().
sleep(ms) Pauses the thread for the given milliseconds. Throws
InterruptedException.
join() Waits for this thread to finish before the calling thread continues.
setPriority(n) Sets priority: MIN_PRIORITY=1, NORM_PRIORITY=5,
MAX_PRIORITY=10.
getName() / setName() Gets or sets the thread's name.
isAlive() Returns true if the thread has been started and not yet terminated.
interrupt() Sends an interrupt signal to a sleeping or waiting thread.
yield() Suggests to the scheduler to give other threads a chance to run.
6.5 Thread Synchronization
When multiple threads access and modify a shared resource (e.g., a bank account balance)
simultaneously, data inconsistency can occur — this is called a race condition. Synchronization
ensures that only ONE thread at a time can execute a critical section of code, using a monitor lock on
the object.
class BankAccount {
private int balance = 1000;
// synchronized method — only ONE thread can execute this at a time
synchronized void withdraw(int amount) {
if (balance >= amount) {
[Link]([Link]().getName() + " withdrawing "
+ amount);
balance -= amount;
[Link]("Remaining balance: " + balance);
} else {
[Link]("Insufficient balance for " +
[Link]().getName());
}
}
}
class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
// Lambda expressions create threads quickly
Thread t1 = new Thread(() -> [Link](700), "Thread-1");
Thread t2 = new Thread(() -> [Link](600), "Thread-2");
[Link]();
[Link]();
}
}
Synchronized Block (More Efficient)
Instead of synchronising the entire method, you can synchronise only the critical section. This improves
performance when most of the method does not need synchronisation.
class Counter {
private int count = 0;
void increment() {
// Only this specific block is locked — rest of the method is
unsynchronised
synchronized(this) {
count++;
}
}
}
📌 Key Point: Synchronisation prevents race conditions but can cause deadlock if two threads
permanently wait for each other's locks. Design carefully to avoid circular dependencies between
locks.
Chapter 7: I/O Management & Generics
7.1 The File Class
The File class in the [Link] package represents files and directories on the filesystem. It allows you to
check existence, get metadata, create, delete, and list files. Importantly, the File class does NOT
perform actual reading or writing — for that, you need streams.
import [Link];
import [Link];
class FileDemo {
public static void main(String[] args) {
File f = new File("[Link]");
// Query file metadata
[Link]("Exists: " + [Link]());
[Link]("Is File: " + [Link]());
[Link]("Is Directory: " + [Link]());
[Link]("Name: " + [Link]());
[Link]("Path: " + [Link]());
[Link]("Size: " + [Link]() + " bytes");
// Create file
try {
if ([Link]()) [Link]("File created!");
else [Link]("File already exists.");
} catch (IOException e) { [Link](); }
// Delete file
[Link]();
// List all files in current directory
File dir = new File(".");
for (String name : [Link]()) {
[Link](name);
}
}
}
7.2 Writing and Reading Files
Writing to a File
FileWriter writes characters directly to a file. BufferedWriter wraps FileWriter and uses an internal
buffer, making large writes significantly more efficient.
import [Link].*;
class WriteFile {
public static void main(String[] args) {
// FileWriter — direct character writing
// try-with-resources ensures the file is automatically closed
try (FileWriter fw = new FileWriter("[Link]")) {
[Link]("Hello, Java File I/O!\n");
[Link]("Second line of data.\n");
[Link]("File written successfully.");
} catch (IOException e) { [Link](); }
// BufferedWriter — efficient writing using an internal buffer
try (BufferedWriter bw = new BufferedWriter(new
FileWriter("[Link]"))) {
[Link]("Buffered writing is faster for large files.");
[Link](); // platform-independent newline (\n or \r\n)
[Link]("This is efficient for large writes.");
} catch (IOException e) { [Link](); }
}
}
Reading from a File
import [Link].*;
class ReadFile {
public static void main(String[] args) {
// FileReader — reads one character at a time
try (FileReader fr = new FileReader("[Link]")) {
int ch;
while ((ch = [Link]()) != -1) { // -1 means end of file
[Link]((char) ch);
}
} catch (IOException e) { [Link](); }
// BufferedReader — reads one line at a time (efficient)
try (BufferedReader br = new BufferedReader(new FileReader("[Link]")))
{
String line;
while ((line = [Link]()) != null) { // null means end of file
[Link](line);
}
} catch (IOException e) { [Link](); }
}
}
RandomAccessFile
RandomAccessFile allows you to read and write at any position in a file, like a database. You can use
seek() to move the file pointer to any byte position.
RandomAccessFile raf = new RandomAccessFile("[Link]", "rw"); // rw = read+write
// Write data sequentially
[Link]("Alice");
[Link](25);
[Link]("Bob");
[Link](30);
// Jump back to the beginning and read
[Link](0); // move file pointer to position 0 (start)
[Link]([Link]() + " - " + [Link]()); // Alice - 25
[Link]([Link]() + " - " + [Link]()); // Bob - 30
[Link]();
7.3 Generics
Generics allow you to write type-safe, reusable code. They enable a class, method, or interface to work
with different data types while providing compile-time type checking. Without generics, you would use
the Object type and risk ClassCastException at runtime. Generics were introduced in Java 5.
Why Generics?
// WITHOUT Generics — unsafe, error-prone
ArrayList list = new ArrayList();
[Link]("Hello");
[Link](100); // accidentally mixing types — no compile error!
String s = (String) [Link](1); // ClassCastException at RUNTIME!
// WITH Generics — type-safe
ArrayList<String> safeList = new ArrayList<String>();
[Link]("Hello");
// [Link](100); // COMPILE ERROR — type is enforced at compile time
String s = [Link](0); // no casting needed
Generic Class
Use a type parameter (like T) as a placeholder that gets replaced with an actual type when the class is
used.
// T is a type parameter — can be any name, but T (Type), E (Element), K, V are
conventions
class Box<T> {
T value;
Box(T val) { [Link] = val; }
T getValue() { return value; }
void setValue(T val) { [Link] = val; }
void display() { [Link]("Value: " + value); }
}
class Main {
public static void main(String[] args) {
Box<Integer> intBox = new Box<>(42); // T = Integer
Box<String> strBox = new Box<>("Hello"); // T = String
Box<Double> dblBox = new Box<>(3.14); // T = Double
[Link](); // Value: 42
[Link](); // Value: Hello
}
}
Generic Method
class Utils {
// <T> before return type declares this as a generic method
public static <T> void printArray(T[] arr) {
for (T element : arr) {
[Link](element + " ");
}
[Link]();
}
// Bounded type parameter — T must be Number or a subtype (Integer, Double,
etc.)
public static <T extends Number> double sum(List<T> list) {
double total = 0;
for (T item : list) total += [Link]();
return total;
}
}
class Main {
public static void main(String[] args) {
Integer[] ints = {1, 2, 3, 4, 5};
String[] strs = {"A", "B", "C"};
[Link](ints); // 1 2 3 4 5
[Link](strs); // A B C
}
}
Chapter 8: Designing GUI Applications using JavaFX
8.1 Introduction to JavaFX
JavaFX is a modern framework for building rich, visually appealing desktop GUI (Graphical User
Interface) applications in Java. It replaced the older Swing library and offers CSS styling, FXML (an
XML-based UI description language), built-in animations, and a clean scene graph architecture.
Concept Description
Stage The main application window — like a browser window or desktop frame.
Scene The content container placed inside a Stage. A Stage can show one Scene at a
time.
Node Every UI element (Button, Label, TextField, etc.) is a Node.
Scene Graph A hierarchical tree of all nodes in a scene (like the DOM in HTML).
Application The base class for all JavaFX apps. Extend it and implement start(Stage).
8.2 Basic Structure of a JavaFX Application
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public class MyFirstApp extends Application {
@Override
public void start(Stage primaryStage) {
// 1. Create UI components (Nodes)
Label label = new Label("Hello, JavaFX!");
Button button = new Button("Click Me");
// 2. Place nodes in a layout pane
VBox vbox = new VBox(10); // VBox arranges children top-to-bottom
[Link](new Insets(20)); // 20px padding on all sides
[Link]().addAll(label, button);
// 3. Create Scene — set preferred width and height
Scene scene = new Scene(vbox, 300, 200);
// 4. Configure and display the Stage
[Link]("My First JavaFX App");
[Link](scene);
[Link]();
}
public static void main(String[] args) {
launch(args); // required to start JavaFX runtime
}
}
8.3 Layout Panes
Layout panes automatically position and resize their child nodes. Choosing the right pane depends on
how you want components arranged.
Layout Pane Arrangement Best Used For
VBox Children arranged vertically Forms, menus, toolbars
(top to bottom)
HBox Children arranged horizontally Button rows, toolbars
(left to right)
GridPane Children in rows and columns Forms with labels and input fields
(table-like)
BorderPane 5 regions: Top, Bottom, Left, Main application layout
Right, Center
FlowPane Wraps children like flowing text Tag clouds, flexible layouts
StackPane Stacks children on top of each Overlapping elements, overlays
other
AnchorPane Anchors nodes to edges with Fixed-position layouts
pixel offsets
// GridPane — perfect for label + field forms
GridPane grid = new GridPane();
[Link](10); // horizontal gap between columns
[Link](10); // vertical gap between rows
[Link](new Insets(20));
Label nameLabel = new Label("Name:");
TextField nameField = new TextField();
Label ageLabel = new Label("Age:");
TextField ageField = new TextField();
[Link](nameLabel, 0, 0); // column 0, row 0
[Link](nameField, 1, 0); // column 1, row 0
[Link](ageLabel, 0, 1); // column 0, row 1
[Link](ageField, 1, 1); // column 1, row 1
8.4 UI Controls
Control Purpose Key Methods
Label Displays non-editable text setText(), getText(), setFont()
Button Clickable button setText(), setOnAction()
TextField Single-line text input getText(), setText(), setPromptText()
TextArea Multi-line text input getText(), setText(), setWrapText()
CheckBox On/off checkbox isSelected(), setSelected()
RadioButton Single-selection radio button isSelected(), ToggleGroup
ComboBox Drop-down list getValue(), getItems().add()
ListView Scrollable list of items getItems(), getSelectionModel()
Slider Range value selector getValue(), setMin(), setMax()
ProgressBar Shows progress (0.0 to 1.0) setProgress()
Label lbl = new Label("Enter Name:");
[Link]("-fx-font-size: 14px; -fx-text-fill: blue;");
TextField tf = new TextField();
[Link]("Type here..."); // placeholder/hint text
Button btn = new Button("Submit");
[Link](100);
CheckBox cb = new CheckBox("I agree to terms");
// RadioButton group — only one can be selected at a time
ToggleGroup tg = new ToggleGroup();
RadioButton rb1 = new RadioButton("Male");
RadioButton rb2 = new RadioButton("Female");
[Link](tg);
[Link](tg);
// ComboBox (drop-down)
ComboBox<String> combo = new ComboBox<>();
[Link]().addAll("India", "USA", "UK");
[Link]("India"); // default selection
8.5 Color and Font Classes
Color Class
The Color class represents RGBA colours and provides predefined constants, RGB construction, and
hex code support.
Color red = [Link]; // predefined constant
Color custom = [Link](128, 0, 255); // custom RGB (0–255 each)
Color hex = [Link]("#ff5733"); // hex colour code
Color trans = [Link](255, 0, 0, 0.5); // with 50% opacity (alpha)
// Applying colour to a node
Label lbl = new Label("Coloured Text");
[Link]([Link]);
Font Class
The Font class controls the typeface, size, weight, and style of text displayed in UI components.
import [Link];
import [Link];
import [Link];
Font f1 = [Link]("Arial", 16); // family +
size
Font f2 = [Link]("Times New Roman", [Link], 18); // bold
Font f3 = [Link]("Verdana", [Link], [Link], 14); // bold
italic
Font f4 = new Font(20); // default
family, size 20
Label label = new Label("Styled Text");
[Link](f2); // apply font to the label
8.6 Event Handling
Event handling allows the application to respond to user actions — clicks, key presses, mouse
movements, etc. JavaFX uses an event listener model with functional interfaces, making lambda
expressions particularly clean and concise for registering handlers.
Event Type Handler Method Triggered By
ActionEvent setOnAction() Button click, menu selection, Enter in
TextField
MouseEvent setOnMouseClicked(), Mouse click, hover, move
setOnMouseEntered()
KeyEvent setOnKeyPressed(), Keyboard input
setOnKeyReleased()
WindowEvent setOnCloseRequest() User clicking the window close button
import [Link];
import [Link];
Label resultLabel = new Label("Result will appear here");
TextField nameField = new TextField();
[Link]("Enter your name");
// Method 1: Anonymous inner class (verbose, Java 7 style)
Button btn1 = new Button("Greet");
[Link](new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
[Link]("Hello, " + [Link]() + "!");
}
});
// Method 2: Lambda expression (concise, preferred in Java 8+)
Button btn2 = new Button("Clear");
[Link](e -> {
[Link]();
[Link]("Cleared!");
});
// Mouse hover effects
[Link](e -> [Link]("-fx-background-color: lightblue;"));
[Link](e -> [Link](""));
// Key press detection
[Link](e -> [Link]("Key pressed: " + [Link]()));
8.7 Complete JavaFX Example — Simple Calculator
import [Link];
import [Link];
import [Link];
import [Link].*;
import [Link].*;
import [Link];
public class Calculator extends Application {
@Override
public void start(Stage stage) {
TextField num1 = new TextField(); [Link]("Number 1");
TextField num2 = new TextField(); [Link]("Number 2");
Label result = new Label("Result: ");
Button addBtn = new Button("+");
Button subBtn = new Button("-");
Button mulBtn = new Button("x");
Button divBtn = new Button("÷");
[Link](e -> {
double a = [Link]([Link]());
double b = [Link]([Link]());
[Link]("Result: " + (a + b));
});
[Link](e -> [Link]("Result: " +
([Link]([Link]()) -
[Link]([Link]()))));
[Link](e -> [Link]("Result: " +
([Link]([Link]()) *
[Link]([Link]()))));
[Link](e -> {
double b = [Link]([Link]());
if (b == 0) { [Link]("Cannot divide by zero!"); return; }
[Link]("Result: " + ([Link]([Link]()) / b));
});
HBox buttons = new HBox(10, addBtn, subBtn, mulBtn, divBtn);
VBox root = new VBox(10, num1, num2, buttons, result);
[Link](new Insets(20));
[Link](new Scene(root, 300, 200));
[Link]("JavaFX Calculator");
[Link]();
}
public static void main(String[] args) { launch(args); }
}
Quick Revision Summary
Chapter Key Points to Remember
Ch 1 – Java Basics JDK ⊃ JRE ⊃ JVM | WORA principle | .java → javac → .class (bytecode) →
java (JVM) | 8 primitive types + String | Widening=automatic, Narrowing=manual
cast
Ch 2 – Control if / if-else / else-if / switch | for / while / do-while / for-each | break=exit loop,
Flow continue=skip iteration | Labels for nested loop control
Ch 3 – OOP Basics Class=blueprint, Object=instance | 4 pillars: Encapsulation, Abstraction,
Inheritance, Polymorphism | Constructors: Default, Parameterised, Copy |
this=current object | static=shared | Inner classes, static/instance blocks
Ch 4 – Inheritance extends keyword | super accesses parent | final prevents
change/override/extend | Overloading=compile-time (different params) |
Overriding=runtime (same params) | String=immutable, StringBuffer=thread-
safe, StringBuilder=fast | Autoboxing/Unboxing
Ch 5 – Interface & Interface=100% abstract, implements, supports multiple inheritance | Abstract
Abstract class=partial implementation, extends, single inheritance | try-catch-finally |
throw=throw object, throws=declare | Custom exception extends
Exception/RuntimeException
Ch 6 – Threads Thread states: New→Runnable→Running→Blocked→Terminated | Extend
Thread or implement Runnable | ALWAYS call start(), not run() | synchronized
prevents race conditions | sleep(), join(), yield() are key methods
Ch 7 – I/O & File class=metadata only | FileWriter/BufferedWriter=writing |
Generics FileReader/BufferedReader=reading | RandomAccessFile=seek any position |
Generics=compile-time type safety, T placeholder
Ch 8 – JavaFX Stage(window)→Scene→Nodes hierarchy | Layouts: VBox, HBox, GridPane,
BorderPane | Controls: Label, Button, TextField, ComboBox, CheckBox |
[Link]()/[Link]() | [Link]() | setOnAction() for events | Lambda
expressions preferred
Best of Luck with Your Exam! Remember: Practice coding daily — reading theory alone
is not enough.