Java Fundamentals: Complete Notes & Examples
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).
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Rishtaara!");
}
}javac Main.java
java MainSyntax — 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.
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.
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.
// 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.
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.
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.
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.
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); // trueStrings — 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 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)); // AMath — 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.
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)); // 4Booleans — 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 isLoggedIn = true;
boolean hasPermission = false;
System.out.println(isLoggedIn && hasPermission); // false
System.out.println(isLoggedIn || hasPermission); // trueIf/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.
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.
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.
int i = 1;
while (i <= 5) {
System.out.println("Count: " + i);
i++;
}
// Prints Count: 1 through 5For — 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 (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); // 55Break & 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.
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, 6Arrays — 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.
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.
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.
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.
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).
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.
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.
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).
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.
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.
// 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 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.
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.
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.
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.
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.
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.
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.
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 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).
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."
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).
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 circleRuntime 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.
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.
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 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 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.
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 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.
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).
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).
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 {
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.
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.
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 automaticallyFiles — 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).
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.
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.
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.
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.
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.
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<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.
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.
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".
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.
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.
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.
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.
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.
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); // 123Threads — 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.
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.
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.
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
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.
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.
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.