R
Rishtaara
Knowledge Hub
Technology & IT

Java Fundamentals: Complete Notes & Examples

By Rishtaara Editorial Team130 min read
#Java#OOP#Collections#Threads#Beginner

Full Java syllabus — syntax to OOP, collections, I/O, threads, lambdas, and projects. Every topic with a real-life example.

Intro — what is Java?

Java is a popular, strongly typed programming language. It runs on billions of devices — Android apps, banking systems, e-commerce backends, and large enterprise software.

Java follows the idea: write once, run anywhere. You compile source code to bytecode, and the JVM (Java Virtual Machine) runs it on Windows, Mac, or Linux.

Real-life example: Java is like a universal power adapter. You write your program once, and the JVM plugs it into any computer without rewriting for each OS.

  • Object-oriented — organize code with classes and objects
  • Huge ecosystem — Spring, Android, Hadoop, and more
  • Strong typing — catches many mistakes at compile time

Get Started — install JDK and first program

To run Java you need the JDK (Java Development Kit). It includes javac (compiler) and java (runner). Download from Oracle or use OpenJDK.

Create a file Main.java, compile with javac Main.java, run with java Main. Every Java app needs a class with public static void main.

Real-life example: JDK is your kitchen with stove and tools. javac cooks the recipe (source) into a ready meal (bytecode). java serves it to the guest (JVM).

Hello World
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Rishtaara!");
    }
}
Compile and run (terminal)
javac Main.java
java Main

Syntax — rules of Java code

Java is case-sensitive — Main and main are different. Every statement usually ends with a semicolon (;). Code lives inside classes and methods.

Curly braces { } define blocks. Indentation is for humans — the compiler cares about braces, not spaces.

Real-life example: Syntax is like grammar in English. Missing a semicolon is like forgetting a full stop — the sentence (program) may not make sense.

Basic syntax
public class Demo {
    public static void main(String[] args) {
        int count = 5;
        count = count + 1;
        System.out.println(count);
    }
}

Output — System.out.println

System.out.println() prints text to the console and moves to the next line. System.out.print() stays on the same line.

Use println for debugging and showing results while learning.

Real-life example: println is like writing one line on a whiteboard. print is writing without pressing Enter — the next text continues on the same line.

Print and println
System.out.print("Hello ");
System.out.println("World");
System.out.println(42);
System.out.println("Sum: " + (10 + 5));

Comments — notes for humans

Comments are ignored by the compiler. Use // for one line and /* ... */ for multiple lines.

Good comments explain why, not what obvious code already shows.

Real-life example: Comments are sticky notes on your homework — they help you and teammates, but the teacher (compiler) ignores them.

Single-line and multi-line comments
// This is a single-line comment
int price = 100;

/*
  Multi-line comment
  explains a bigger idea
*/
price = price + 50;

Variables — storing values

A variable is a named box that holds a value. Declare the type first, then the name: int age = 20;

Java variables must have a type. You cannot put a string into an int box without conversion.

Real-life example: A variable is a labeled jar in the kitchen. The label (type) says "only rice" or "only sugar" — you cannot mix them by accident.

Declaring variables
int age = 20;
double price = 99.50;
char grade = 'A';
boolean active = true;
String name = "Asha";

Data Types — primitives and String

Primitive types: byte, short, int, long, float, double, char, boolean. String is a class (reference type) for text.

int for whole numbers, double for decimals, boolean for true/false, char for one character.

Real-life example: Data types are different measuring cups — you use a cup for flour (int) and a jug for milk (double). Wrong cup = wrong recipe.

Common types
byte small = 127;
int count = 1000;
long big = 9_000_000_000L;
float ratio = 3.14f;
double pi = 3.14159;
char letter = 'Z';
boolean isStudent = true;
String city = "Mumbai";

Type Casting — converting between types

Widening (automatic): int to double. Narrowing (manual): double to int — you must cast: (int) 9.99 becomes 9.

Casting can lose data — 9.99 becomes 9 when cast to int.

Real-life example: Pouring a large bottle into a small cup — some water spills (data loss). Casting double to int drops the decimal part.

Widening and narrowing
int a = 10;
double b = a;        // automatic widening

double x = 9.99;
int y = (int) x;     // manual narrowing → 9
System.out.println(y);

Operators — arithmetic and assignment

+ - * / % for math. ++ and -- add or subtract 1. +=, -= combine assignment with operation.

Comparison: == != > < >= <=. Logical: && || ! for combining boolean conditions.

Real-life example: Operators are calculator buttons. + adds, == checks if two numbers are equal, && means both conditions must be true.

Arithmetic and logical operators
int a = 10, b = 3;
System.out.println(a + b);   // 13
System.out.println(a % b);   // 1 (remainder)

boolean ok = (a > 5) && (b < 10);
System.out.println(ok);      // true

Strings — working with text

String stores text. Use + to join strings (concatenation). length() returns character count.

Common methods: toUpperCase(), toLowerCase(), charAt(index), indexOf("text"), substring(start, end).

Real-life example: A String is a necklace of letter beads. length() counts beads. substring() takes beads from position 2 to 5.

String basics
String name = "Asha";
System.out.println(name.length());           // 4
System.out.println(name.toUpperCase());      // ASHA
System.out.println("Hello, " + name + "!");  // Hello, Asha!
System.out.println(name.charAt(0));          // A

Math — Math class

Java provides Math class with static methods — no need to create an object.

Math.max, Math.min, Math.sqrt, Math.pow, Math.abs, Math.round are used daily.

Real-life example: Math class is a pocket calculator built into Java — always ready, no install needed.

Math methods
System.out.println(Math.max(10, 20));    // 20
System.out.println(Math.min(10, 20));    // 10
System.out.println(Math.sqrt(16));       // 4.0
System.out.println(Math.pow(2, 3));      // 8.0
System.out.println(Math.abs(-7));        // 7
System.out.println(Math.round(3.6));     // 4

Booleans — true or false

boolean holds only true or false. Use it for yes/no decisions — login valid, age eligible, stock available.

Comparison and logical operators produce boolean results.

Real-life example: A light switch is boolean — ON (true) or OFF (false). No maybe, no half-on.

Boolean variables
boolean isLoggedIn = true;
boolean hasPermission = false;
System.out.println(isLoggedIn && hasPermission); // false
System.out.println(isLoggedIn || hasPermission); // true

If/Else — making decisions

if (condition) { ... } runs when condition is true. else if adds more checks. else runs when nothing matched.

Use curly braces even for one line — it prevents bugs when you add more lines later.

Real-life example: If it rains, take umbrella. Else if sunny, wear hat. Else, just go out. Your code picks one path.

if, else if, else
int marks = 85;

if (marks >= 90) {
    System.out.println("Grade A");
} else if (marks >= 75) {
    System.out.println("Grade B");
} else {
    System.out.println("Keep practicing");
}

Switch — many options, one value

switch checks one variable against many cases. break stops fall-through to the next case.

Good for menu choices, day-of-week, status codes. Java 14+ supports switch expressions too.

Real-life example: A vending machine — you press B3 (case), it gives chips (break). Without break, it might give chips AND soda.

Switch statement
int day = 3;
switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    default:
        System.out.println("Other day");
}

While — repeat while condition is true

while (condition) { ... } checks condition before each loop. If false at start, body never runs.

Watch for infinite loops — condition must eventually become false.

Real-life example: While there are dishes in the sink, keep washing. When sink is empty, stop.

While loop
int i = 1;
while (i <= 5) {
    System.out.println("Count: " + i);
    i++;
}
// Prints Count: 1 through 5

For — counted loops

for (init; condition; update) { ... } is best when you know how many times to repeat.

Common pattern: for (int i = 0; i < 10; i++) loops 10 times with i from 0 to 9.

Real-life example: For loop is like counting laps on a track — start at 0, run while under 10, add 1 each lap.

For loop
for (int i = 0; i < 5; i++) {
    System.out.println("Lap " + i);
}

// Sum 1 to 10
int sum = 0;
for (int n = 1; n <= 10; n++) {
    sum += n;
}
System.out.println(sum); // 55

Break & Continue — control the loop

break exits the loop immediately. continue skips the rest of this iteration and goes to the next one.

Use break to stop early when you find what you need. Use continue to skip unwanted items.

Real-life example: Searching a bag for keys — break when found. Skipping rotten apples in a basket — continue to next apple.

Break and continue
for (int i = 0; i < 10; i++) {
    if (i == 3) continue; // skip 3
    if (i == 7) break;    // stop at 7
    System.out.println(i);
}
// Prints 0, 1, 2, 4, 5, 6

Arrays — fixed-size lists

An array holds many values of the same type in one variable. Size is fixed after creation.

Index starts at 0. Last index is length - 1. Access with arr[i].

Real-life example: An array is a row of lockers — each locker has a number (index). You cannot add a 11th locker without a new row.

Create and use arrays
int[] scores = {90, 85, 78, 92};
System.out.println(scores[0]);  // 90
System.out.println(scores.length); // 4

String[] names = new String[3];
names[0] = "Asha";
names[1] = "Rohan";
names[2] = "Priya";

Loop through arrays

Use for loop with index or enhanced for (for-each) to visit every element.

for-each is simpler when you only need values, not index.

Real-life example: for-each is like reading every name on a attendance sheet top to bottom without caring about roll number.

For-each and indexed loop
int[] nums = {10, 20, 30};

for (int n : nums) {
    System.out.println(n);
}

for (int i = 0; i < nums.length; i++) {
    System.out.println("Index " + i + ": " + nums[i]);
}

Methods — reusable blocks of code

A method is a named function inside a class. It can take parameters and return a value.

Syntax: accessModifier returnType methodName(params) { body }. void means no return value.

Real-life example: A method is a recipe card — "makeChai()" always makes chai the same way when you call it.

Simple methods
public class Calculator {
    static int add(int a, int b) {
        return a + b;
    }

    static void greet(String name) {
        System.out.println("Hello, " + name);
    }

    public static void main(String[] args) {
        System.out.println(add(5, 3)); // 8
        greet("Asha");
    }
}

Challenge — practice methods

Try writing: maxOfThree(a,b,c), isEven(n), and printTable(n) that prints multiplication table.

Keep methods small — one job each. Test with main before moving on.

Real-life example: Challenge is like homework — you learn by doing, not just reading the recipe.

Challenge: Write a method celsiusToFahrenheit(double c) that returns (c * 9/5) + 32. Call it from main with 25.

Parameters — passing data into methods

Parameters are variables in the method signature. Arguments are the actual values you pass when calling.

Java passes primitives by value — the method gets a copy. Objects pass reference copy.

Real-life example: Parameters are order details on a food app — you tell the restaurant size (param), they make that size (argument).

Parameters and return
static double areaRectangle(double width, double height) {
    return width * height;
}

static void printLine(String text, int times) {
    for (int i = 0; i < times; i++) {
        System.out.println(text);
    }
}

Method Overloading — same name, different params

You can have multiple methods with the same name if parameter lists differ (count or types).

Compiler picks the right one based on arguments you pass.

Real-life example: "print" can mean print photo or print document — same word, different inputs, different action.

Overloaded methods
static int add(int a, int b) {
    return a + b;
}

static double add(double a, double b) {
    return a + b;
}

static int add(int a, int b, int c) {
    return a + b + c;
}

Scope — where variables live

Local variables exist only inside the block { } where declared. Method params are local to that method.

Instance variables belong to the object. Static variables belong to the class.

Real-life example: A name tag at a party (local) is thrown away when you leave the room. Your ID card (instance) stays with you everywhere.

Local vs instance scope
class Demo {
    int instanceVar = 10;

    void test() {
        int localVar = 5;
        System.out.println(instanceVar + localVar);
    }
    // localVar not visible here
}

Recursion — method calls itself

A recursive method solves a problem by calling itself on a smaller part. You need a base case to stop.

Without base case you get StackOverflowError — infinite calls.

Real-life example: Russian nesting dolls — open one doll, find smaller doll inside, repeat until the tiniest solid doll (base case).

Factorial with recursion
static int factorial(int n) {
    if (n <= 1) return 1;       // base case
    return n * factorial(n - 1);
}

public static void main(String[] args) {
    System.out.println(factorial(5)); // 120
}

When to use recursion

Good for trees, divide-and-conquer, and problems that mirror smaller versions (Fibonacci, directory walk).

For simple loops, a for or while is often clearer and faster.

Real-life example: Finding a file in nested folders — check this folder, if not found, recurse into subfolders.

Sum array with recursion
static int sumArray(int[] arr, int index) {
    if (index >= arr.length) return 0;
    return arr[index] + sumArray(arr, index + 1);
}

OOP — Object-Oriented Programming

OOP organizes code around objects — bundles of data (fields) and behavior (methods). Java is built for OOP.

Four pillars: Encapsulation, Inheritance, Polymorphism, Abstraction. You will learn each in this course.

Real-life example: A car object has data (color, speed) and behavior (accelerate, brake). You interact with the car, not raw engine wires.

  • Encapsulation — hide details, expose safe methods
  • Inheritance — reuse code from parent classes
  • Polymorphism — one interface, many forms
  • Abstraction — show only what matters

Why OOP matters in real projects

Large apps use classes to model users, orders, payments — each class owns its data and rules.

Teams work on different classes without breaking the whole system.

Real-life example: A school has Student, Teacher, Course classes — like departments in a company, each with clear duties.

Thinking in objects
// Instead of loose variables everywhere:
// String studentName; int studentMarks;

// Group related data in a class:
class Student {
    String name;
    int marks;
}

Classes & Objects

A class is a blueprint. An object is one instance built from that blueprint.

Create object with new: Student s = new Student();

Real-life example: Class = cookie cutter shape. Object = each actual cookie you bake from that cutter.

Class and object
class Car {
    String brand;
    int year;
}

public class Main {
    public static void main(String[] args) {
        Car myCar = new Car();
        myCar.brand = "Toyota";
        myCar.year = 2024;
        System.out.println(myCar.brand);
    }
}

Attributes — fields of a class

Attributes (fields) store object state — name, age, balance. Each object has its own copy of instance fields.

Access with dot: object.fieldName. Initialize in constructor or at declaration.

Real-life example: Attributes are labels on a student ID card — name, roll number, photo — unique per student.

Instance attributes
class Book {
    String title = "Unknown";
    String author;
    double price;

    void showInfo() {
        System.out.println(title + " by " + author + " — ₹" + price);
    }
}

Methods — behavior inside classes

Instance methods use object data: void deposit(double amount). Static methods belong to class: Math.max style.

Call instance method on object: acc.deposit(100). Call static on class: Demo.calculate().

Real-life example: deposit() is something one bank account does. calculateTax() might be a static tool for all accounts.

Instance and static methods
class BankAccount {
    double balance = 0;

    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    static double interestRate() {
        return 4.5;
    }
}

Challenge — build a Product class

Create Product with name, price, quantity. Add methods applyDiscount(percent), restock(amount), getTotalValue().

Create two Product objects in main and call each method.

Real-life example: Challenge is like building a mini shop shelf in code — one product class, many products on shelf.

Challenge: Add a method isLowStock() that returns true when quantity < 5.

Constructors — setup new objects

Constructor runs when you use new. Same name as class, no return type. Use it to set initial field values.

If you write no constructor, Java adds a default no-arg constructor.

Real-life example: Constructor is the factory setup line — when a new phone rolls out, it gets default settings before shipping.

Constructor example
class User {
    String name;
    int age;

    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

User u = new User("Asha", 20);

this — refer to current object

this refers to the current object. Common use: this.field = param when names clash.

this() calls another constructor in the same class — must be first line.

Real-life example: Saying "this phone" in a shop means the one in your hand, not every phone on the shelf.

this keyword
class Rectangle {
    int width, height;

    Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    int area() {
        return this.width * this.height;
    }
}

Modifiers — public, private, protected

public — visible everywhere. private — only inside same class. protected — class + subclasses + package.

Use private fields + public getters/setters for controlled access.

Real-life example: private is your diary lock — only you open it. public is a shop window — everyone can see.

Access modifiers
class Account {
    private double balance;

    public void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    public double getBalance() {
        return balance;
    }
}

Encapsulation — protect object state

Encapsulation hides internal data and exposes methods as the only way to change it.

Validation in setters prevents bad data — negative age, empty name.

Real-life example: ATM shows balance and buttons — you cannot reach inside and move wires. Methods are the buttons.

Getter and setter
class Student {
    private String name;

    public String getName() { return name; }

    public void setName(String name) {
        if (name != null && !name.isBlank()) {
            this.name = name;
        }
    }
}

Packages — organizing classes

Package groups related classes: com.rishtaara.models. First line of file: package com.rishtaara.models;

import brings classes from other packages: import java.util.ArrayList;

Real-life example: Packages are folders in a filing cabinet — billing, HR, inventory — each drawer holds related files.

Package and import
package com.knowvora.demo;

import java.util.ArrayList;
import java.util.List;

public class App {
    public static void main(String[] args) {
        List<String> items = new ArrayList<>();
        items.add("Java");
    }
}

API — using built-in libraries

Java API is the huge library of ready classes — java.lang, java.util, java.io, java.time.

You do not rewrite ArrayList or Scanner — you import and use them.

Real-life example: API is like a toolbox in a workshop — hammer, screwdriver ready. You pick the tool, not forge metal from scratch.

  • java.lang — String, Math, System (auto-imported)
  • java.util — collections, Scanner, Random
  • java.io — files and streams
  • java.time — modern date/time

Inheritance — extends parent class

Child class extends parent: class Dog extends Animal. Child inherits fields and methods (except private).

Use extends once per class in Java — single inheritance.

Real-life example: Dog inherits from Animal — all dogs eat and sleep (parent), but also bark (child extra).

Basic inheritance
class Animal {
    void eat() {
        System.out.println("Eating...");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Woof!");
    }
}

Dog d = new Dog();
d.eat();
d.bark();

super — call parent constructor or method

super() calls parent constructor — must be first line in child constructor if used.

super.methodName() calls parent version of an overridden method.

Real-life example: super is asking your parent for help — "use Dad's recipe step first, then add my spice."

super keyword
class Person {
    String name;
    Person(String name) { this.name = name; }
}

class Employee extends Person {
    int id;
    Employee(String name, int id) {
        super(name);
        this.id = id;
    }
}

Polymorphism — many forms

Same reference type can point to different object types: Animal a = new Dog();

Method overriding — child replaces parent method with same signature.

Real-life example: Remote control button "Play" — same button, different device (TV plays video, speaker plays music).

Method overriding
class Shape {
    void draw() {
        System.out.println("Drawing shape");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing circle");
    }
}

Shape s = new Circle();
s.draw(); // Drawing circle

Runtime polymorphism

Java picks the actual object type at runtime for overridden methods.

Useful for arrays or lists of mixed subclasses: Shape[] shapes = { new Circle(), new Square() };

Real-life example: A school announcement says "all students line up" — each student walks differently, but one command fits all.

Polymorphic array
class Square extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing square");
    }
}

Shape[] shapes = { new Circle(), new Square() };
for (Shape sh : shapes) {
    sh.draw();
}

Inner Classes — class inside a class

Inner class lives inside outer class. Useful for helpers tightly tied to outer class.

Static nested class does not need outer instance. Non-static inner class needs outer object.

Real-life example: Inner class is a pocket inside a jacket — belongs to that jacket, not worn alone on the street.

Inner class
class Outer {
    private int x = 10;

    class Inner {
        void show() {
            System.out.println("x = " + x);
        }
    }
}

Outer o = new Outer();
Outer.Inner inner = o.new Inner();
inner.show();

Abstraction — abstract classes

abstract class cannot be instantiated. It may have abstract methods (no body) and concrete methods.

Subclass must implement all abstract methods unless subclass is also abstract.

Real-life example: "Vehicle" abstract — no single vehicle exists, but Car and Bike fill in startEngine() their own way.

Abstract class
abstract class Vehicle {
    abstract void start();

    void stop() {
        System.out.println("Stopped");
    }
}

class Bike extends Vehicle {
    @Override
    void start() {
        System.out.println("Kick start");
    }
}

Interface — contract of methods

interface defines what a class must do — method signatures without implementation (before Java 8).

class implements Interface. A class can implement many interfaces.

Real-life example: USB port is an interface — any device with USB plug must fit the same shape and rules.

Interface and implements
interface Printable {
    void print();
}

class Report implements Printable {
    @Override
    public void print() {
        System.out.println("Printing report...");
    }
}

Anonymous Classes — one-time implementation

Anonymous class implements an interface or extends a class inline, without a separate file.

Common with old event listeners; lambdas often replace them today.

Real-life example: A custom stamp order — one special design, no need to manufacture a whole stamp factory line.

Anonymous class
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running anonymously");
    }
};
new Thread(task).start();

Enum — fixed set of constants

enum defines named constants: enum Day { MON, TUE, WED }. Type-safe alternative to magic strings.

Use in switch, compare with ==.

Real-life example: Enum is a menu with only 3 combo options — you cannot order "combo 99" if it is not on the list.

Enum example
enum Status {
    PENDING, APPROVED, REJECTED
}

Status s = Status.PENDING;
if (s == Status.APPROVED) {
    System.out.println("Done");
}

User Input — Scanner

Scanner reads keyboard input. import java.util.Scanner; wrap System.in.

Methods: nextLine() for full line, nextInt() for integer, nextDouble() for decimal.

Real-life example: Scanner is a waiter taking your order — you type, program reads and remembers.

Scanner input
import java.util.Scanner;

Scanner sc = new Scanner(System.in);
System.out.print("Enter name: ");
String name = sc.nextLine();
System.out.print("Enter age: ");
int age = sc.nextInt();
System.out.println("Hi " + name + ", age " + age);
sc.close();

Date — java.time (modern API)

Prefer java.time over old Date. LocalDate for date, LocalDateTime for date+time.

Use DateTimeFormatter for formatting strings.

Real-life example: LocalDate is a calendar page (2026-07-24). LocalDateTime adds the clock time (14:30).

LocalDate and formatting
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd-MM-yyyy");
System.out.println(today.format(fmt));

Errors — compile time vs runtime

Compile errors — typos, wrong types — javac catches before run. Fix syntax and types.

Runtime errors — crash while running: divide by zero, null pointer, array out of bounds.

Real-life example: Compile error = wrong address on envelope before posting. Runtime error = letter returned after delivery attempt fails.

  • Syntax error — missing semicolon, brace
  • Logic error — program runs but wrong answer
  • Exception — runtime problem with stack trace

Debugging — finding bugs

Read error messages — line number and exception type. Use System.out.println to trace values.

IDE debuggers let you pause, step line by line, and inspect variables.

Real-life example: Debugging is detective work — follow clues (logs) from crime scene (crash line) to culprit (bug).

Debug with println
int total = 0;
for (int i = 0; i < 5; i++) {
    total += i;
    System.out.println("i=" + i + " total=" + total);
}

Exceptions — try, catch, finally

try { risky code } catch (Exception e) { handle } finally { always runs }.

Checked exceptions must be handled or declared. Unchecked extend RuntimeException.

Real-life example: try is attempting payment. catch is "card declined — show message." finally is "close checkout screen either way."

try-catch-finally
try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Cannot divide by zero");
} finally {
    System.out.println("Cleanup done");
}

Multiple Exceptions — catch several types

Use multiple catch blocks or one catch with types separated by | : catch (IOException | SQLException e).

Order catches from specific to general — IOException before Exception.

Real-life example: One fire drill handles smoke (IOException) and power cut (SQLException) — different fixes for each alarm.

Multi-catch
try {
    String s = null;
    System.out.println(s.length());
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
    System.out.println("Bad access: " + e.getMessage());
}

try-with-resources — auto-close

try (Resource r = ...) { } closes resources automatically — files, streams, connections.

Resource must implement AutoCloseable. Cleaner than manual finally close.

Real-life example: try-with-resources is a library that lends you a book and takes it back when you leave — no forgotten returns.

try-with-resources
import java.io.*;

try (FileWriter writer = new FileWriter("notes.txt")) {
    writer.write("Hello, Rishtaara!");
} catch (IOException e) {
    System.out.println("Write failed: " + e.getMessage());
}
// writer closed automatically

Files — create, read, update, delete

java.nio.file.Files and java.io.File handle file operations. CRUD = Create, Read, Update, Delete.

Always check if file exists before read. Handle IOException for missing files or permission errors.

Real-life example: Files CRUD is managing a notebook — write new page (create), read page (read), edit text (update), tear page (delete).

File CRUD with java.io.File
import java.io.File;
import java.io.IOException;

File file = new File("data.txt");

// Create
if (file.createNewFile()) {
    System.out.println("Created");
}

// Read exists?
System.out.println("Exists: " + file.exists());
System.out.println("Size: " + file.length());

// Delete
// file.delete();

Files utility class (NIO)

Files.write, Files.readString, Files.delete — modern shortcuts in Java 11+.

Paths.get("folder", "file.txt") builds cross-platform paths.

Real-life example: Files utility is express delivery — one method call instead of many manual steps.

Files.readString and write
import java.nio.file.*;

Path path = Paths.get("hello.txt");
Files.writeString(path, "Welcome to Java!");
String content = Files.readString(path);
System.out.println(content);

FileInputStream & FileOutputStream — byte streams

Streams read/write raw bytes — good for images, PDFs, binary files.

FileInputStream reads; FileOutputStream writes. Always close or use try-with-resources.

Real-life example: Byte stream is a pipe carrying water drops (bytes) — works for any liquid, not just text.

Byte streams
import java.io.*;

try (FileOutputStream out = new FileOutputStream("out.bin")) {
    out.write(65); // byte 'A'
}

try (FileInputStream in = new FileInputStream("out.bin")) {
    System.out.println(in.read()); // 65
}

BufferedReader & BufferedWriter — text lines

Wrap streams for efficient text: BufferedReader reads line by line. BufferedWriter writes with buffer.

Use InputStreamReader to bridge bytes to chars: new BufferedReader(new FileReader("file.txt")).

Real-life example: BufferedReader is reading a book line by line instead of one letter at a time — faster and easier.

Buffered text I/O
import java.io.*;

try (BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"))) {
    bw.write("Line 1");
    bw.newLine();
    bw.write("Line 2");
}

try (BufferedReader br = new BufferedReader(new FileReader("log.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

Collections Framework — overview

java.util collections hold groups of objects — List, Set, Map, Queue. Use interfaces for flexibility: List<String> list = new ArrayList<>();

Generics <Type> keep type safety — no casting from raw types.

Real-life example: Collections are different containers — list = numbered queue, set = unique badge set, map = name-to-desk lookup.

  • List — ordered, allows duplicates
  • Set — unique elements only
  • Map — key → value pairs

List, ArrayList & LinkedList

List interface — add, get, remove by index. ArrayList — fast random access. LinkedList — fast insert/remove at ends.

Pick ArrayList by default unless you insert/delete heavily in the middle.

Real-life example: ArrayList is a theater row — jump to seat 5 quickly. LinkedList is a chain of people holding hands — easy to add in middle if you know neighbors.

ArrayList and LinkedList
import java.util.*;

List<String> array = new ArrayList<>();
array.add("Java");
array.add("Spring");
System.out.println(array.get(0));

List<Integer> linked = new LinkedList<>();
linked.add(10);
linked.add(20);

List Sorting

Collections.sort(list) or list.sort(null) for natural order. Custom order with Comparator.

Strings sort alphabetically. Numbers sort numerically.

Real-life example: Sorting a class by marks is Comparator — you define "higher marks first" rule.

Sort list
import java.util.*;

List<Integer> nums = new ArrayList<>(List.of(30, 10, 20));
Collections.sort(nums);
System.out.println(nums); // [10, 20, 30]

nums.sort((a, b) -> b - a); // descending
System.out.println(nums); // [30, 20, 10]

Set, HashSet, TreeSet & LinkedHashSet

Set stores unique values. HashSet — fast, no order. LinkedHashSet — insertion order. TreeSet — sorted order.

add duplicate returns false — Set ignores duplicate.

Real-life example: HashSet is a bag of unique marbles — shake it, order random. TreeSet is marbles sorted by size automatically.

Set implementations
Set<String> hash = new HashSet<>();
hash.add("java");
hash.add("java"); // ignored
System.out.println(hash.size()); // 1

Set<String> tree = new TreeSet<>();
tree.add("banana");
tree.add("apple");
System.out.println(tree); // [apple, banana]

Map — key-value pairs

Map stores keys mapped to values. Keys must be unique. put, get, remove, containsKey.

Use when you need fast lookup by ID, email, or name.

Real-life example: Map is a phone contact book — name (key) → number (value). Search name, get number instantly.

HashMap basics
import java.util.*;

Map<String, Integer> scores = new HashMap<>();
scores.put("Asha", 91);
scores.put("Rohan", 88);
System.out.println(scores.get("Asha")); // 91

for (Map.Entry<String, Integer> e : scores.entrySet()) {
    System.out.println(e.getKey() + ": " + e.getValue());
}

HashMap, TreeMap & LinkedHashMap

HashMap — fastest, no order. TreeMap — keys sorted. LinkedHashMap — insertion order of keys.

Choose based on whether you need sorted keys or stable iteration order.

Real-life example: TreeMap is a dictionary sorted A–Z. LinkedHashMap remembers the order you added words.

TreeMap and LinkedHashMap
Map<String, String> tree = new TreeMap<>();
tree.put("zebra", "Z");
tree.put("apple", "A");
System.out.println(tree.keySet()); // [apple, zebra]

Map<String, String> linked = new LinkedHashMap<>();
linked.put("first", "1");
linked.put("second", "2");

Iterator — walk a collection

Iterator has hasNext() and next() to visit elements safely during removal.

Enhanced for-loop uses iterator internally. Use explicit iterator when removing while looping.

Real-life example: Iterator is a ticket checker at cinema — hasNext "any seats left?", next "here is seat 7".

Iterator example
import java.util.*;

List<String> fruits = new ArrayList<>(List.of("Apple", "Banana", "Cherry"));
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

Algorithms — Collections utility methods

Collections.max, min, frequency, reverse, shuffle, binarySearch work on lists.

Arrays class has similar helpers for arrays: Arrays.sort, Arrays.toString.

Real-life example: Algorithms are quick tools in a Swiss knife — sort, find max, count duplicates without rewriting loops.

Collections algorithms
import java.util.*;

List<Integer> list = new ArrayList<>(List.of(5, 2, 8, 2));
System.out.println(Collections.max(list));      // 8
System.out.println(Collections.frequency(list, 2)); // 2
Collections.sort(list);
System.out.println(list); // [2, 2, 5, 8]

Wrapper Classes — primitives as objects

Integer, Double, Boolean wrap primitives for collections and null support.

Autoboxing converts int → Integer automatically. Unboxing goes back.

Real-life example: Wrapper is putting a small gem (int) in a gift box (Integer) so it fits on a shelf that only holds boxes.

Wrappers and autoboxing
Integer num = 42;        // autoboxing
int value = num;         // unboxing

List<Integer> scores = new ArrayList<>();
scores.add(90);          // int boxed to Integer

System.out.println(Integer.parseInt("100"));

Generics — type-safe collections

Generics <T> let classes and methods work with any type while keeping compile-time checks.

List<String> cannot accidentally hold integers.

Real-life example: Generics are labeled boxes — "Strings only" sticker stops you from putting screws inside.

Generic class
class Box<T> {
    private T value;
    Box(T value) { this.value = value; }
    T get() { return value; }
}

Box<String> nameBox = new Box<>("Asha");
Box<Integer> ageBox = new Box<>(20);

Annotations — metadata in code

@Override, @Deprecated, @SuppressWarnings are built-in. Frameworks use @Test, @Entity, etc.

Annotations do not change logic by themselves — tools read them at compile or runtime.

Real-life example: Annotations are sticky labels on luggage — "Fragile", "This side up" — handlers know what to do.

Common annotations
class Parent {
    void greet() {}
}

class Child extends Parent {
    @Override
    void greet() {
        System.out.println("Hello");
    }

    @Deprecated
    void oldMethod() {}
}

RegEx — pattern matching

Regular expressions match text patterns — emails, phone numbers, passwords.

Pattern and Matcher classes: Pattern.compile("\\d+").matcher(text).find().

String matches(), replaceAll(), split() also use regex.

Real-life example: RegEx is a bouncer with a rule — "only names starting with A" — checks each guest automatically.

Regex basics
String email = "asha@mail.com";
boolean valid = email.matches("[\\w.-]+@[\\w.-]+\\.\\w+");

String text = "Order 123 done";
String digits = text.replaceAll("[^0-9]", "");
System.out.println(digits); // 123

Threads — parallel execution

Thread class or Runnable interface runs code in parallel. thread.start() begins execution.

Main thread and worker threads run concurrently — watch shared data (synchronization later).

Real-life example: Threads are multiple chefs in one kitchen — each cooks a dish at the same time, faster total service.

Thread with Runnable
class Worker implements Runnable {
    public void run() {
        System.out.println("Worker: " + Thread.currentThread().getName());
    }
}

Thread t = new Thread(new Worker());
t.start();
System.out.println("Main continues...");

Lambda — short anonymous functions

Lambda syntax: (params) -> expression or { statements }. Used with functional interfaces.

Replaces verbose anonymous classes for simple callbacks.

Real-life example: Lambda is a quick note instead of full letter — "(x) -> x * 2" says double the number, nothing more.

Lambda expressions
import java.util.*;

List<String> names = new ArrayList<>(List.of("Rohan", "Asha", "Priya"));
names.sort((a, b) -> a.compareTo(b));
names.forEach(name -> System.out.println(name));

Advanced Sorting — Comparator and streams

Comparator.comparing(Person::getName) sorts objects by field. thenComparing chains rules.

Streams: list.stream().sorted().filter().map() — declarative pipeline.

Real-life example: Advanced sorting is ranking students — first by grade, then by name if grades tie.

Comparator and stream sort
import java.util.*;

record Student(String name, int marks) {}

List<Student> students = List.of(
    new Student("Asha", 90),
    new Student("Rohan", 85)
);

students.stream()
    .sorted(Comparator.comparingInt(Student::marks).reversed())
    .forEach(s -> System.out.println(s.name() + " " + s.marks()));

Projects — put it all together

Build small console apps: Student Result Manager, Library System, Todo CLI, Bank Account simulator.

Use classes, collections, file I/O, and exception handling in each project.

Real-life example: Projects are mini internships — you practice real tasks, not just read theory.

  • Student Result — OOP + ArrayList + file save
  • Library — HashMap books by ID + Scanner input
  • Todo CLI — List + file persistence
  • Bank — encapsulation + validation + exceptions
Project starter — Student
import java.util.*;

class Student {
    String name;
    int marks;
    Student(String name, int marks) {
        this.name = name;
        this.marks = marks;
    }
}

public class ResultApp {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("Asha", 91));
        list.add(new Student("Rohan", 88));
        for (Student s : list) {
            System.out.println(s.name + ": " + s.marks);
        }
    }
}

Certification — next steps

Oracle offers Java certifications (OCA/OCP). Practice with our Java MCQs after you finish the lessons.

Certifications prove basics — employers also want projects on GitHub.

Real-life example: Certificate is a driving license — proves you passed test. Projects prove you can actually drive on roads.

After this course, practice daily on Rishtaara MCQs and build one GitHub project before applying for Java internships.

How Tos — common tasks quick guide

Read file: Files.readString(path). Write file: Files.writeString(path, text). Sort list: Collections.sort(list).

Parse int safely: Integer.parseInt in try-catch. Compare strings: equals(), not ==.

Real-life example: How Tos are recipe cards on the fridge — fastest way to make chai without opening the full cookbook.

  • Compare strings with .equals()
  • Use try-with-resources for files
  • Prefer ArrayList and HashMap by default
  • Check null before calling methods

Reference notes — keep handy

Official docs: docs.oracle.com/javase. Use the official Java API docs for quick lookup.

Main method signature: public static void main(String[] args). Package first line, then imports, then class.

Real-life example: Reference is a dictionary — you do not memorize every word, but you know where to look.

Bookmark Oracle Java API docs and this Rishtaara course for guided learning in simple English.

Examples & Interview path

Interview topics: OOP pillars, equals vs ==, String immutability, ArrayList vs LinkedList, HashMap internals basics, exception types, multithreading intro.

Practice coding: reverse string, find duplicates, two-sum, fibonacci, singleton pattern basics.

Real-life example: Interview path is exam prep — revise notes (this course), solve past papers (LeetCode easy), mock interview with friend.

  • Core: OOP, collections, exceptions
  • Medium: threads, generics, streams intro
  • Practice: 20 easy Java programs from scratch
  • Next course: Spring Boot on Rishtaara

Key Takeaways

  • Java runs on the JVM — write once, run anywhere.
  • Master syntax, loops, arrays, and methods before OOP.
  • Classes, inheritance, interfaces, and encapsulation model real-world systems.
  • Handle errors with try-catch; use try-with-resources for files and streams.
  • Collections (List, Set, Map) are daily tools — know ArrayList, HashSet, HashMap.
  • Generics, lambdas, and threads unlock modern Java style.
  • Build console projects and review interview topics to solidify skills.

Frequently Asked Questions

JDK vs JRE vs JVM — what do I need to code?
Install the JDK (Java Development Kit). It includes the compiler (javac), runtime (JRE), and JVM to run your programs.
ArrayList or LinkedList?
Use ArrayList by default — fast random access. LinkedList only when you insert/delete often in the middle of a large list.
== vs equals() for Strings?
Always use equals() to compare text content. == checks if two references point to the same object in memory.
Checked vs unchecked exceptions?
Checked exceptions (IOException) must be handled or declared. Unchecked (NullPointerException, ArithmeticException) extend RuntimeException and are optional to catch.
What after this Java course?
Build 1–2 console projects, practice MCQs, then move to Spring Boot or Android depending on your career goal.