Java is a statically-typed, object-oriented, platform-independent programming language first released by Sun Microsystems in 1995. It follows the principle of 'Write Once, Run Anywhere' — code compiled on Windows runs unchanged on Linux or macOS. Java is the backbone of Android development, enterprise backend systems, and large-scale distributed applications at companies like Google, Amazon, LinkedIn, and Netflix.
Step 1 — JDK vs JRE vs JVM: Understanding the Ecosystem
Before writing a single line of Java, you must understand the three components that make Java work.
JVM — Java Virtual Machine
- An abstract computing machine that executes Java bytecode.
- It is NOT a physical machine — it is a software layer.
- Handles memory management, garbage collection, and security.
- Makes Java platform-independent: JVM is the only platform-dependent piece.
JRE — Java Runtime Environment
- JRE = JVM + Java standard class libraries (java.lang, java.util, etc.).
- Used to RUN Java programs. End-users need the JRE.
- Does NOT include development tools like the compiler (javac).
JDK — Java Development Kit
- JDK = JRE + development tools (javac compiler, debugger, javadoc, jar).
- Used to DEVELOP and compile Java programs. Developers need the JDK.
- Download: JDK 21 LTS from https://adoptium.net (recommended).
Step 2 — Your First Java Program
Every Java program starts with a class. The entry point for any Java application is the main method with an exact signature.
// Every Java file must have a public class matching the filename
public class HelloWorld {
// The entry point: JVM calls this method to start your program
// 'public' — accessible from anywhere
// 'static' — belongs to the class, not an instance
// 'void' — returns nothing
// 'String[] args' — command-line arguments array
public static void main(String[] args) {
System.out.println("Hello, World!"); // prints to stdout with newline
System.out.print("No newline here"); // prints without newline
System.out.printf("Name: %s, Age: %d%n", "Kuldeep", 25); // formatted
}
}# Compile: generates HelloWorld.class in the same directory
javac HelloWorld.java
# Run: JVM loads HelloWorld.class and calls main()
java HelloWorld
# Output:
# Hello, World!
# No newline hereName: Kuldeep, Age: 25Step 3 — Data Types: Primitives vs Reference Types
Java has two categories of data types. Primitive types store raw values directly in memory (stack). Reference types store a memory address (pointer) to an object on the heap.
8 Primitive Data Types
- byte — 8-bit integer. Range: -128 to 127. Use for raw binary data.
- short — 16-bit integer. Range: -32,768 to 32,767.
- int — 32-bit integer. Range: ~-2.1 billion to 2.1 billion. Default for integers.
- long — 64-bit integer. Range: ±9.2 × 10^18. Suffix with L: 100L.
- float — 32-bit floating point. Suffix with f: 3.14f. Imprecise.
- double — 64-bit floating point. Default for decimals. More precise than float.
- boolean — true or false. 1 bit of information.
- char — 16-bit Unicode character. Single quotes: 'A', '\n', '\u0041'.
public class DataTypes {
public static void main(String[] args) {
// --- Primitive Types ---
byte b = 127; // max byte value
short s = 32000;
int i = 2_000_000; // underscores for readability (Java 7+)
long l = 9_000_000_000L; // L suffix required for long literals
float f = 3.14f; // f suffix required for float literals
double d = 3.14159265358979;
boolean flag = true;
char c = 'A'; // Unicode: c = '\u0041' is also 'A'
System.out.println("int max: " + Integer.MAX_VALUE); // 2147483647
System.out.println("long max: " + Long.MAX_VALUE); // 9223372036854775807
System.out.println("double max: " + Double.MAX_VALUE); // 1.7976931348623157E308
// --- Reference Types ---
String name = "Kuldeep"; // String is a class, not a primitive
int[] nums = {1, 2, 3}; // Array is a reference type
String nullStr = null; // Reference types can be null; primitives CANNOT
// --- Autoboxing: primitive <-> Wrapper class ---
Integer boxed = 42; // int autoboxed to Integer object
int unboxed = boxed; // Integer unboxed back to int
// Wrapper classes: Integer, Long, Double, Float, Boolean, Character, Byte, Short
}
}Step 4 — Variables: Declaration, Initialization, and Scope
public class Variables {
// --- Class-level (field) variables ---
static int classVar = 10; // belongs to the class
int instanceVar = 20; // belongs to each object instance
public static void main(String[] args) {
// --- Local variables: declared inside a method ---
int x; // declared but NOT initialized
x = 5; // must initialize before use — compiler error otherwise
int y = 10; // declare + initialize in one step
// --- var (local type inference, Java 10+) ---
var message = "Hello"; // compiler infers type as String
var count = 42; // compiler infers type as int
// var can ONLY be used for local variables, not fields or parameters
// --- final: makes a variable a constant (cannot be reassigned) ---
final double PI = 3.14159;
// PI = 3.0; // COMPILE ERROR: cannot assign to final variable
// --- Scope: variable lives within the block {} it was declared in ---
{
int inner = 100; // only accessible inside this block
System.out.println(inner); // OK
}
// System.out.println(inner); // COMPILE ERROR: inner is out of scope
System.out.println("x=" + x + ", y=" + y + ", message=" + message);
}
}Step 5 — Operators
Java operators are divided into groups. Understanding operator precedence prevents subtle bugs.
public class Operators {
public static void main(String[] args) {
// --- Arithmetic Operators ---
int a = 10, b = 3;
System.out.println(a + b); // 13 — addition
System.out.println(a - b); // 7 — subtraction
System.out.println(a * b); // 30 — multiplication
System.out.println(a / b); // 3 — integer division (truncates decimal)
System.out.println(a % b); // 1 — modulo (remainder)
System.out.println((double) a / b); // 3.3333 — cast to double first!
// --- Increment / Decrement ---
int x = 5;
System.out.println(x++); // 5 — post-increment: returns THEN increments
System.out.println(x); // 6
System.out.println(++x); // 7 — pre-increment: increments THEN returns
// --- Assignment Operators ---
int n = 10;
n += 5; // n = n + 5 = 15
n -= 3; // n = n - 3 = 12
n *= 2; // n = n * 2 = 24
n /= 4; // n = n / 4 = 6
n %= 4; // n = n % 4 = 2
// --- Comparison Operators (return boolean) ---
System.out.println(10 == 10); // true
System.out.println(10 != 5); // true
System.out.println(10 > 5); // true
System.out.println(10 < 5); // false
System.out.println(10 >= 10); // true
System.out.println(10 <= 9); // false
// --- Logical Operators ---
boolean p = true, q = false;
System.out.println(p && q); // false — AND: both must be true
System.out.println(p || q); // true — OR: at least one must be true
System.out.println(!p); // false — NOT: inverts
// Short-circuit evaluation:
// In (a && b): if a is false, b is NEVER evaluated
// In (a || b): if a is true, b is NEVER evaluated
int[] arr = null;
if (arr != null && arr.length > 0) { // safe: arr.length not evaluated if null
System.out.println(arr[0]);
}
// --- Bitwise Operators ---
System.out.println(5 & 3); // 1 — AND per bit: 101 & 011 = 001
System.out.println(5 | 3); // 7 — OR per bit: 101 | 011 = 111
System.out.println(5 ^ 3); // 6 — XOR per bit: 101 ^ 011 = 110
System.out.println(~5); // -6 — NOT per bit (inverts all bits)
System.out.println(5 << 1); // 10 — left shift = multiply by 2
System.out.println(20 >> 2);// 5 — right shift = divide by 4
// --- Ternary Operator ---
int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // Adult
// --- instanceof Operator ---
Object obj = "Hello";
System.out.println(obj instanceof String); // true
// Java 16+ pattern matching:
if (obj instanceof String str) { // casts and binds in one step
System.out.println(str.toUpperCase()); // HELLO
}
}
}Step 6 — Control Flow: if, switch, loops
public class ControlFlow {
public static void main(String[] args) {
// =========================================
// IF / ELSE IF / ELSE
// =========================================
int score = 85;
if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B"); // prints this
} else if (score >= 70) {
System.out.println("Grade: C");
} else {
System.out.println("Grade: F");
}
// =========================================
// SWITCH STATEMENT (traditional)
// =========================================
int day = 3;
switch (day) {
case 1:
System.out.println("Monday");
break; // break prevents fall-through to next case
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday"); // prints this
break;
default:
System.out.println("Other day");
}
// =========================================
// SWITCH EXPRESSION (Java 14+, preferred modern style)
// =========================================
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
default -> "Weekend";
};
System.out.println(dayName); // Wednesday
// =========================================
// FOR LOOP
// =========================================
for (int i = 0; i < 5; i++) {
System.out.print(i + " "); // 0 1 2 3 4
}
System.out.println();
// =========================================
// ENHANCED FOR LOOP (for-each) — for arrays/collections
// =========================================
int[] numbers = {10, 20, 30, 40, 50};
for (int num : numbers) {
System.out.print(num + " "); // 10 20 30 40 50
}
System.out.println();
// =========================================
// WHILE LOOP
// =========================================
int count = 0;
while (count < 3) {
System.out.println("count = " + count);
count++;
}
// =========================================
// DO-WHILE LOOP — executes body at least once
// =========================================
int x = 10;
do {
System.out.println("do-while: x = " + x); // prints even though x >= 10
x++;
} while (x < 10); // condition is false, but body ran once
// =========================================
// BREAK and CONTINUE
// =========================================
for (int i = 0; i < 10; i++) {
if (i == 3) continue; // skip 3
if (i == 6) break; // stop at 6
System.out.print(i + " "); // 0 1 2 4 5
}
System.out.println();
// Labeled break (break out of outer loop from inner loop)
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) break outer; // exits both loops
System.out.print("(" + i + "," + j + ") ");
}
}
// Output: (0,0) (0,1) (0,2) (1,0)
}
}Step 7 — Arrays
Arrays in Java are fixed-size, ordered collections of elements of the same type. Once created, the size cannot change.
import java.util.Arrays; // for Arrays.sort(), Arrays.toString()
public class ArraysExample {
public static void main(String[] args) {
// --- 1D Array Declaration and Initialization ---
int[] arr1 = new int[5]; // creates array of 5 zeros
int[] arr2 = {1, 2, 3, 4, 5}; // array literal (inline init)
int[] arr3 = new int[]{10, 20}; // explicit new with values
// Access by index (0-based)
arr1[0] = 100;
arr1[1] = 200;
System.out.println(arr1[0]); // 100
System.out.println(arr2.length); // 5 (not a method! it's a field)
// ArrayIndexOutOfBoundsException if index >= length or < 0
// arr2[5] = 99; // RUNTIME ERROR
// --- Iterating ---
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] + " "); // 1 2 3 4 5
}
// --- Sorting ---
int[] unsorted = {5, 3, 8, 1, 9, 2};
Arrays.sort(unsorted); // sorts in-place
System.out.println(Arrays.toString(unsorted)); // [1, 2, 3, 5, 8, 9]
// --- Binary Search (array must be sorted first!) ---
int idx = Arrays.binarySearch(unsorted, 5); // returns index of element
System.out.println("Index of 5: " + idx); // 3
// --- Copying ---
int[] copy = Arrays.copyOf(arr2, arr2.length); // full copy
int[] partial = Arrays.copyOfRange(arr2, 1, 4); // indices 1,2,3 -> {2,3,4}
// --- 2D Arrays (matrix) ---
int[][] matrix = new int[3][3]; // 3 rows, 3 columns
int[][] grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(grid[1][2]); // 6 (row 1, col 2)
// Iterating 2D array
for (int[] row : grid) {
for (int val : row) {
System.out.printf("%3d", val);
}
System.out.println();
}
// --- Jagged arrays (rows of different lengths) ---
int[][] jagged = new int[3][];
jagged[0] = new int[]{1};
jagged[1] = new int[]{2, 3};
jagged[2] = new int[]{4, 5, 6};
}
}Step 8 — Methods
A method is a named block of reusable code. Methods define behavior. In Java, every method must live inside a class.
public class Methods {
// --- Basic method: no params, no return ---
static void greet() {
System.out.println("Hello!");
}
// --- Method with parameters and return value ---
static int add(int a, int b) {
return a + b; // 'return' exits the method and sends a value back
}
// --- Method overloading: same name, different parameter lists ---
static double add(double a, double b) {
return a + b;
}
static int add(int a, int b, int c) {
return a + b + c;
}
// --- Varargs: variable number of arguments ---
static int sum(int... numbers) { // 'numbers' is treated as int[]
int total = 0;
for (int n : numbers) total += n;
return total;
}
// --- Pass by value: primitives ---
static void tryToChange(int x) {
x = 999; // only modifies the LOCAL copy
}
// --- Pass by reference (object reference by value) ---
static void modifyArray(int[] arr) {
arr[0] = 999; // modifies the ORIGINAL array through the reference
}
// --- Recursive method ---
static int factorial(int n) {
if (n <= 1) return 1; // base case: MUST have one or you get StackOverflowError
return n * factorial(n - 1); // recursive call
}
// factorial(5) = 5 * factorial(4) = 5 * 4 * 3 * 2 * 1 = 120
public static void main(String[] args) {
greet(); // Hello!
System.out.println(add(3, 4)); // 7 (int version)
System.out.println(add(1.5, 2.5)); // 4.0 (double version)
System.out.println(add(1, 2, 3)); // 6 (three-arg version)
System.out.println(sum(1,2,3,4,5));// 15
int val = 10;
tryToChange(val);
System.out.println(val); // still 10 — primitive was copied
int[] arr = {1, 2, 3};
modifyArray(arr);
System.out.println(arr[0]); // 999 — array was modified!
System.out.println(factorial(5)); // 120
}
}Step 9 — Strings: The Most-Used Reference Type
String in Java is immutable — once created, its characters cannot change. Every string operation that appears to modify a string actually creates a new String object. Strings are stored in the String Pool (a special area of the heap) for memory efficiency.
public class Strings {
public static void main(String[] args) {
// --- String creation ---
String s1 = "Hello"; // String literal — goes into String Pool
String s2 = new String("Hello"); // new object on heap (NOT in pool)
// == compares REFERENCES, not content!
System.out.println(s1 == s2); // false (different objects)
System.out.println(s1.equals(s2)); // true (same content) — ALWAYS use .equals()
// --- Common String methods ---
String str = " Hello, World! ";
System.out.println(str.length()); // 17
System.out.println(str.trim()); // "Hello, World!" (removes leading/trailing spaces)
System.out.println(str.strip()); // "Hello, World!" (Unicode-aware, Java 11+)
System.out.println(str.toUpperCase()); // " HELLO, WORLD! "
System.out.println(str.toLowerCase()); // " hello, world! "
System.out.println(str.contains("World")); // true
System.out.println(str.startsWith(" ")); // true
System.out.println(str.indexOf("World")); // 8 (or -1 if not found)
System.out.println(str.substring(7, 12)); // "Hello" — indices 7 to 11
System.out.println(str.replace("World", "Java")); // " Hello, Java! "
System.out.println(str.isEmpty()); // false
System.out.println("".isBlank()); // true (Java 11+)
// --- Splitting ---
String csv = "apple,banana,cherry";
String[] fruits = csv.split(",");
System.out.println(fruits[1]); // banana
// --- Joining ---
String joined = String.join(" - ", "one", "two", "three");
System.out.println(joined); // one - two - three
// --- String.format and formatted() ---
String msg = String.format("Name: %s, Age: %d, GPA: %.2f", "Kuldeep", 22, 3.856);
System.out.println(msg); // Name: Kuldeep, Age: 22, GPA: 3.86
// --- char operations ---
String word = "Java";
char ch = word.charAt(0); // 'J'
char[] chars = word.toCharArray();
String back = new String(chars); // back to String
// --- Immutability: why it matters ---
String original = "Hello";
String upper = original.toUpperCase(); // creates a NEW String object
System.out.println(original); // "Hello" — unchanged!
System.out.println(upper); // "HELLO"
// --- StringBuilder: mutable string for performance ---
// Never concatenate strings in a loop with +! Use StringBuilder.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
sb.append("item").append(i).append(", ");
}
sb.delete(sb.length() - 2, sb.length()); // remove trailing ", "
System.out.println(sb.toString()); // item0, item1, item2, item3, item4
// StringBuilder methods
StringBuilder sb2 = new StringBuilder("Hello");
sb2.insert(5, " World"); // "Hello World"
sb2.reverse(); // "dlroW olleH"
sb2.replace(0, 5, "Java"); // "Java olleH"
System.out.println(sb2); // Java olleH
}
}Step 10 — Type Casting
public class TypeCasting {
public static void main(String[] args) {
// --- Widening (implicit/automatic) casting ---
// Goes from smaller type to larger type. Safe, no data loss.
// byte -> short -> int -> long -> float -> double
int myInt = 9;
double myDouble = myInt; // automatic: int widened to double
System.out.println(myDouble); // 9.0
// --- Narrowing (explicit) casting ---
// Goes from larger type to smaller type. MUST be explicit. Risk of data loss.
double pi = 3.99;
int truncated = (int) pi; // explicit cast: decimal part LOST
System.out.println(truncated); // 3 (NOT rounded, truncated)
long big = 1234567890123L;
int overflow = (int) big; // data loss! value wraps around
System.out.println(overflow); // 1912276171 (garbage value)
// --- Numeric type promotion in expressions ---
byte x = 10, y = 20;
// byte result = x + y; // COMPILE ERROR: x+y is promoted to int
byte result = (byte)(x + y); // must cast back
// --- char and int interop ---
char ch = 'A';
int ascii = ch; // char widened to int
System.out.println(ascii); // 65
char back = (char)(ascii + 1); // 66 -> 'B'
System.out.println(back); // B
// --- String to number conversion ---
int parsed = Integer.parseInt("42"); // String -> int
double parsedD = Double.parseDouble("3.14"); // String -> double
// --- Number to String ---
String s1 = String.valueOf(100); // "100"
String s2 = Integer.toString(100); // "100"
String s3 = "" + 100; // "100" (less efficient)
}
}Quick Reference: Java Keywords
Most Important Java Keywords
- public / private / protected — access modifiers controlling visibility.
- static — belongs to the class itself, not to instances.
- final — for constants (variables), non-overridable methods, non-inheritable classes.
- void — method return type meaning 'returns nothing'.
- new — allocates a new object on the heap.
- this — refers to the current object instance.
- super — refers to the parent class.
- return — exits a method and optionally returns a value.
- null — a literal meaning 'no object reference'.
- instanceof — tests whether an object is an instance of a class/interface.
- try / catch / finally / throw / throws — exception handling.
- abstract / interface / implements / extends — OOP building blocks.
- synchronized — makes a method/block thread-safe.
- import — brings a class or package into scope.