Unit I: Java Fundamentals

Learn the core concepts of Java programming language

Why Java and History of Java

Definition

Java is a high-level, class-based, object-oriented programming language designed to have as few implementation dependencies as possible. It follows the principle of "Write Once, Run Anywhere" (WORA).

History

Key Features of Java

Your First Java Program

HelloWorld.java
// This is your first Java program
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
        
        // Print numbers
        System.out.println(42);
        
        // Perform calculations
        System.out.println(10 + 20);
    }
}

Understanding the Program Structure

More Examples - Working with Variables

VariablesDemo.java
public class VariablesDemo {
    public static void main(String[] args) {
        // Declaring and initializing variables
        int age = 25;
        double salary = 50000.50;
        String name = "John Doe";
        boolean isEmployed = true;
        
        // Printing variables
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
        System.out.println("Salary: $" + salary);
        System.out.println("Employed: " + isEmployed);
        
        // Performing operations
        int yearsToRetirement = 60 - age;
        System.out.println("Years to retirement: " + yearsToRetirement);
    }
}

Example: User Input and Processing

UserInputDemo.java
import java.util.Scanner;

public class UserInputDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Reading user input
        System.out.print("Enter your name: ");
        String name = scanner.nextLine();
        
        System.out.print("Enter your age: ");
        int age = scanner.nextInt();
        
        // Processing and displaying
        System.out.println("\\nWelcome, " + name + "!");
        System.out.println("You are " + age + " years old.");
        
        if (age >= 18) {
            System.out.println("You are an adult.");
        } else {
            System.out.println("You are a minor.");
        }
        
        scanner.close();
    }
}

Important Notes

  • The filename must match the public class name exactly (case-sensitive) and have the .java extension.
  • Java is case-sensitive: HelloWorld and helloworld are different.
  • Every statement must end with a semicolon (;).
  • Strings must be enclosed in double quotes ("text"), while characters use single quotes ('a').

Practice Questions

Q1: Write a program that prints your name, age, and favorite programming language.

Q2: Create a program that calculates and displays the area of a rectangle (length × width).

Q3: Write a program that takes two numbers as input and prints their sum, difference, and product.

JVM, JRE, and JDK

JDK (Java Development Kit)

Definition: JDK is a software development environment used for developing Java applications. It includes the JRE plus development tools.

JRE (Java Runtime Environment)

Definition: JRE provides the runtime environment to execute Java applications. It contains the JVM and standard class libraries.

JVM (Java Virtual Machine)

Definition: JVM is an abstract machine that provides the runtime environment in which Java bytecode can be executed. It is platform-dependent.

Relationship

Component Contains Purpose
JDK JRE + Development Tools (javac, jar, javadoc) Develop and Run Java programs
JRE JVM + Library Classes Run Java programs
JVM Class Loader, Bytecode Verifier, Interpreter Execute bytecode

Java Compilation Process

  1. Source Code (.java): Write Java source code
  2. Compilation: javac compiles source to bytecode (.class)
  3. Bytecode (.class): Platform-independent intermediate code
  4. JVM Execution: JVM interprets/JIT compiles bytecode to machine code
  5. Output: Program executes on the host machine
Terminal Commands
# Compile Java source file
javac HelloWorld.java

# Run the compiled program
java HelloWorld

# Output: Hello, World!

JVM Architecture (Detailed)

The JVM consists of three main subsystems:

1. Class Loader Subsystem

2. Runtime Data Areas

3. Execution Engine

Real-World Example: How Java Code Executes

Example.java
public class Example {
    static int count = 0;  // Stored in Method Area
    
    public static void main(String[] args) {  // Stack frame created
        int x = 10;  // x stored in Stack
        Example obj = new Example();  // obj reference in Stack, object in Heap
        obj.display(x);
    }
    
    void display(int num) {  // New stack frame created
        System.out.println("Number: " + num);
        // Stack frame destroyed after method completes
    }
}

Compilation and Execution Process Example

Terminal Commands with Explanation
# Step 1: Create Java source file
# Write your code in HelloWorld.java

# Step 2: Compile Java source file
javac HelloWorld.java
# Creates HelloWorld.class (bytecode file)

# Step 3: View bytecode (optional)
javap -c HelloWorld
# Shows the bytecode instructions

# Step 4: Run the compiled program
java HelloWorld
# JVM loads, verifies, and executes bytecode
# Output: Hello, World!

# The .class file can run on any platform with JVM
# Windows, Linux, Mac - all use the same .class file!

Advantages of This Architecture

Exam Tips

  • JDK = JRE + Development Tools (javac, javadoc, jar, etc.)
  • JRE = JVM + Core Libraries (java.lang, java.util, etc.)
  • JVM is platform-dependent, but bytecode is platform-independent
  • Bytecode verification happens during the linking phase
  • Heap stores objects, Stack stores method calls and local variables
  • Static variables are stored in Method Area, not Heap

Practice Questions

Q1: What is the difference between JIT compiler and interpreter in JVM?

Q2: Where are static variables stored in JVM memory?

Q3: Why is bytecode called platform-independent but JVM is platform-dependent?

Q4: What happens during the "linking" phase of class loading?

Java Source File Structure

Definition

A Java source file has a specific structure that must be followed. The file can contain package declaration, import statements, and class definitions in a specific order.

Structure Order (Must Follow)

  1. Package Statement (optional, must be first if present)
  2. Import Statements (optional)
  3. Class/Interface Declarations
SourceFileStructure.java
// 1. Package declaration (optional, must be first)
package com.example.myapp;

// 2. Import statements (optional)
import java.util.Scanner;
import java.util.ArrayList;

// 3. Class declaration
public class SourceFileStructure {
    // Fields (instance variables)
    private String name;
    private int age;
    
    // Constructor
    public SourceFileStructure(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Methods
    public void display() {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
    }
    
    // Main method - entry point
    public static void main(String[] args) {
        SourceFileStructure obj = new SourceFileStructure("John", 25);
        obj.display();
    }
}

Rules for Java Source Files

Access Specifiers

Modifier Class Package Subclass World
public Yes Yes Yes Yes
protected Yes Yes Yes No
default (no modifier) Yes Yes No No
private Yes No No No

Common Mistake

Placing import statements before the package declaration will cause a compilation error. Always follow the order: package, imports, class.

Data Types in Java

Definition

Data types specify the type of data that can be stored in a variable. Java is a statically-typed language, meaning all variables must be declared with a data type before use.

Categories of Data Types

Java has two categories of data types:

Primitive Data Types

Type Size Range Default
byte 1 byte -128 to 127 0
short 2 bytes -32,768 to 32,767 0
int 4 bytes -2^31 to 2^31-1 0
long 8 bytes -2^63 to 2^63-1 0L
float 4 bytes ~7 decimal digits 0.0f
double 8 bytes ~15 decimal digits 0.0d
char 2 bytes 0 to 65,535 (Unicode) '\u0000'
boolean 1 bit true or false false
DataTypesDemo.java
public class DataTypesDemo {
    public static void main(String[] args) {
        // Integer types
        byte b = 100;
        short s = 10000;
        int i = 100000;
        long l = 10000000000L;  // Note: L suffix for long
        
        // Floating-point types
        float f = 3.14f;      // Note: f suffix for float
        double d = 3.14159265359;
        
        // Character type
        char c = 'A';
        char unicode = '\u0041';  // Also 'A'
        
        // Boolean type
        boolean isJavaFun = true;
        
        // Reference type (String is a class)
        String name = "Java";
        
        System.out.println("byte: " + b);
        System.out.println("int: " + i);
        System.out.println("double: " + d);
        System.out.println("char: " + c);
        System.out.println("boolean: " + isJavaFun);
    }
}

Type Casting

TypeCasting.java
public class TypeCasting {
    public static void main(String[] args) {
        // Widening (Implicit) - automatic
        int intVal = 100;
        double doubleVal = intVal;  // int to double
        System.out.println("Widening: " + doubleVal);  // 100.0
        
        // Narrowing (Explicit) - requires cast
        double d = 9.78;
        int i = (int) d;  // double to int (truncates)
        System.out.println("Narrowing: " + i);  // 9
        
        // String to int
        String str = "123";
        int num = Integer.parseInt(str);
        
        // int to String
        int value = 456;
        String s = String.valueOf(value);
    }
}

Variables

Exam Tip

Remember the size hierarchy: byte (1) < short (2) < int (4) < long (8) and float (4) < double (8). char is 2 bytes because Java uses Unicode.

Operators in Java

Definition

Operators are special symbols that perform operations on operands (variables and values). Java provides a rich set of operators classified into several categories.

1. Arithmetic Operators

Operator Name Example Result
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division5 / 31 (integer division)
%Modulus5 % 32

2. Relational Operators

Operator Name Example
==Equal toa == b
!=Not equal toa != b
>Greater thana > b
<Less thana < b
>=Greater than or equala >= b
<=Less than or equala <= b

3. Logical Operators

LogicalOperators.java
public class LogicalOperators {
    public static void main(String[] args) {
        boolean a = true;
        boolean b = false;
        
        // AND - both must be true
        System.out.println("a && b: " + (a && b));  // false
        
        // OR - at least one must be true
        System.out.println("a || b: " + (a || b));  // true
        
        // NOT - reverses the value
        System.out.println("!a: " + !a);  // false
        
        // Practical example
        int age = 25;
        boolean hasLicense = true;
        boolean canDrive = (age >= 18) && hasLicense;
        System.out.println("Can drive: " + canDrive);  // true
    }
}

4. Assignment Operators

AssignmentOperators.java
int x = 10;
x += 5;   // x = x + 5;  x = 15
x -= 3;   // x = x - 3;  x = 12
x *= 2;   // x = x * 2;  x = 24
x /= 4;   // x = x / 4;  x = 6
x %= 4;   // x = x % 4;  x = 2

5. Unary Operators

UnaryOperators.java
int a = 5;

// Pre-increment: increment first, then use
System.out.println(++a);  // Output: 6

// Post-increment: use first, then increment
a = 5;
System.out.println(a++);  // Output: 5
System.out.println(a);    // Output: 6

// Pre-decrement and Post-decrement work similarly
int b = 10;
System.out.println(--b);  // Output: 9
System.out.println(b--);  // Output: 9
System.out.println(b);    // Output: 8

6. Ternary Operator

TernaryOperator.java
// Syntax: condition ? value_if_true : value_if_false

int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status);  // Output: Adult

// Finding maximum
int a = 10, b = 20;
int max = (a > b) ? a : b;
System.out.println("Max: " + max);  // Output: Max: 20

7. Bitwise Operators

Operator Name Description
&ANDSets bit to 1 if both bits are 1
|ORSets bit to 1 if at least one bit is 1
^XORSets bit to 1 if only one bit is 1
~NOTInverts all the bits
<<Left ShiftShifts bits left, fills with 0
>>Right ShiftShifts bits right, preserves sign

Bitwise Operators Example

BitwiseDemo.java
public class BitwiseDemo {
    public static void main(String[] args) {
        int a = 5;   // Binary: 0101
        int b = 3;   // Binary: 0011
        
        System.out.println("a & b: " + (a & b));   // 1 (0001)
        System.out.println("a | b: " + (a | b));   // 7 (0111)
        System.out.println("a ^ b: " + (a ^ b));   // 6 (0110)
        System.out.println("~a: " + (~a));         // -6 (inverts all bits)
        
        // Shift operators
        System.out.println("a << 1: " + (a << 1));  // 10 (multiply by 2)
        System.out.println("a >> 1: " + (a >> 1));  // 2 (divide by 2)
    }
}

8. instanceof Operator

InstanceOfDemo.java
public class InstanceOfDemo {
    public static void main(String[] args) {
        String str = "Hello";
        Integer num = 100;
        Object obj = "Test";
        
        // Check object types
        System.out.println(str instanceof String);    // true
        System.out.println(num instanceof Integer);   // true
        System.out.println(obj instanceof String);    // true
        System.out.println(str instanceof Object);    // true (String is subclass of Object)
    }
}

Operator Precedence

Operators are evaluated in a specific order when multiple operators appear in an expression:

Priority Operators Associativity
1 (Highest)Postfix (++, --)Left to Right
2Unary (++, --, +, -, !, ~)Right to Left
3Multiplicative (*, /, %)Left to Right
4Additive (+, -)Left to Right
5Shift (<<, >>, >>>)Left to Right
6Relational (<, >, <=, >=, instanceof)Left to Right
7Equality (==, !=)Left to Right
8Bitwise AND (&)Left to Right
9Bitwise XOR (^)Left to Right
10Bitwise OR (|)Left to Right
11Logical AND (&&)Left to Right
12Logical OR (||)Left to Right
13Ternary (?:)Right to Left
14 (Lowest)Assignment (=, +=, -=, etc.)Right to Left

Complex Expressions Example

OperatorPrecedence.java
public class OperatorPrecedence {
    public static void main(String[] args) {
        // Understanding precedence
        int result1 = 10 + 5 * 2;
        System.out.println(result1);  // 20, not 30 (* has higher precedence)
        
        // Using parentheses to change order
        int result2 = (10 + 5) * 2;
        System.out.println(result2);  // 30
        
        // Complex expression
        int a = 5, b = 10, c = 15;
        int result3 = a + b * c / 3 - 2;
        System.out.println(result3);  // 5 + (10*15/3) - 2 = 5 + 50 - 2 = 53
        
        // Combining logical operators
        boolean flag = (a < b) && (b < c) || (a > c);
        System.out.println(flag);  // true
    }
}

Common Pitfalls and Best Practices

Important Points

  • Division by Zero: Integer division by zero throws ArithmeticException; floating-point returns Infinity or NaN
  • Integer Division: 5/2 returns 2, not 2.5 (use 5.0/2 for decimal result)
  • Increment Operators: Use parentheses for clarity: (++a) vs (a++)
  • Comparing Objects: Use .equals() for content comparison, not == (which compares references)
  • Short-Circuit Evaluation: && and || stop evaluating once result is determined

Short-Circuit Evaluation Example

ShortCircuit.java
public class ShortCircuit {
    public static void main(String[] args) {
        int x = 5;
        
        // && stops if first condition is false
        if (x < 3 && ++x > 4) {
            System.out.println("True");
        }
        System.out.println("x = " + x);  // x = 5 (++x was not executed)
        
        // || stops if first condition is true
        if (x > 3 || ++x > 6) {
            System.out.println("True");
        }
        System.out.println("x = " + x);  // x = 5 (++x was not executed)
    }
}

Practice Questions

Q1: What is the output of: int x = 5; System.out.println(x++ + ++x);

Q2: Calculate the result: int result = 10 + 5 * 2 / 4 - 1;

Q3: What's the difference between & and && operators?

Q4: Write a program to swap two numbers without using a third variable (use XOR).

Q5: What is the output of: 5 % -3 and -5 % 3?

Exam Tip

Understand the difference between ++i (pre-increment) and i++ (post-increment). In expressions, pre-increment increments first and then uses the value, while post-increment uses the value first and then increments.

Control Statements

Definition

Control statements determine the flow of execution of a program. They allow decision making, looping, and branching in code.

1. if-else Statement

IfElseDemo.java
public class IfElseDemo {
    public static void main(String[] args) {
        int age = 20;
        
        // Simple if
        if (age >= 18) {
            System.out.println("You are an adult");
        }
        
        // if-else
        if (age >= 18) {
            System.out.println("Can vote");
        } else {
            System.out.println("Cannot vote");
        }
        
        // if-else-if ladder
        int score = 85;
        if (score >= 90) {
            System.out.println("Grade: A");
        } else if (score >= 80) {
            System.out.println("Grade: B");
        } else if (score >= 70) {
            System.out.println("Grade: C");
        } else {
            System.out.println("Grade: F");
        }
    }
}

2. switch Statement

SwitchDemo.java
public class SwitchDemo {
    public static void main(String[] args) {
        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;
            case 4:
                System.out.println("Thursday");
                break;
            case 5:
                System.out.println("Friday");
                break;
            default:
                System.out.println("Weekend");
        }
        
        // Switch with String (Java 7+)
        String fruit = "Apple";
        switch (fruit) {
            case "Apple":
                System.out.println("Red fruit");
                break;
            case "Banana":
                System.out.println("Yellow fruit");
                break;
            default:
                System.out.println("Unknown fruit");
        }
    }
}

3. for Loop

ForLoopDemo.java
public class ForLoopDemo {
    public static void main(String[] args) {
        // Basic for loop
        for (int i = 1; i <= 5; i++) {
            System.out.println("Count: " + i);
        }
        
        // Enhanced for loop (for-each)
        int[] numbers = {10, 20, 30, 40, 50};
        for (int num : numbers) {
            System.out.println("Number: " + num);
        }
        
        // Nested for loop
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                System.out.print(i * j + " ");
            }
            System.out.println();
        }
    }
}

4. while Loop

WhileLoopDemo.java
public class WhileLoopDemo {
    public static void main(String[] args) {
        // while loop
        int count = 1;
        while (count <= 5) {
            System.out.println("Count: " + count);
            count++;
        }
        
        // do-while loop (executes at least once)
        int num = 1;
        do {
            System.out.println("Number: " + num);
            num++;
        } while (num <= 5);
    }
}

5. break and continue

BreakContinueDemo.java
public class BreakContinueDemo {
    public static void main(String[] args) {
        // break - exits the loop
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                break;  // Exits loop when i is 5
            }
            System.out.println(i);  // Prints 1, 2, 3, 4
        }
        
        // continue - skips current iteration
        for (int i = 1; i <= 5; i++) {
            if (i == 3) {
                continue;  // Skips when i is 3
            }
            System.out.println(i);  // Prints 1, 2, 4, 5
        }
    }
}

Warning

Unlike Python, Java does not have an else clause for loops. The do-while loop is guaranteed to execute at least once, unlike the while loop which may not execute at all if the condition is initially false.

Arrays in Java

Definition

An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created and cannot be changed after creation.

Key Points

Array Declaration and Initialization

ArrayDemo.java
public class ArrayDemo {
    public static void main(String[] args) {
        // Declaration and initialization
        int[] numbers = new int[5];  // Array of 5 integers
        
        // Assigning values
        numbers[0] = 10;
        numbers[1] = 20;
        numbers[2] = 30;
        numbers[3] = 40;
        numbers[4] = 50;
        
        // Declaration with values (array literal)
        int[] scores = {85, 90, 78, 92, 88};
        
        // String array
        String[] fruits = {"Apple", "Banana", "Cherry"};
        
        // Accessing elements
        System.out.println("First fruit: " + fruits[0]);
        System.out.println("Array length: " + fruits.length);
        
        // Iterating with for loop
        for (int i = 0; i < scores.length; i++) {
            System.out.println("Score " + i + ": " + scores[i]);
        }
        
        // Iterating with enhanced for loop
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
    }
}

Multidimensional Arrays

MultiDimensionalArray.java
public class MultiDimensionalArray {
    public static void main(String[] args) {
        // 2D array declaration
        int[][] matrix = new int[3][3];
        
        // 2D array with values
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        
        // Accessing elements
        System.out.println("Element at [1][2]: " + grid[1][2]);  // 6
        
        // Iterating 2D array
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                System.out.print(grid[i][j] + " ");
            }
            System.out.println();
        }
        
        // Jagged array (rows with different lengths)
        int[][] jagged = new int[3][];
        jagged[0] = new int[2];
        jagged[1] = new int[4];
        jagged[2] = new int[3];
    }
}

Common Mistake

Accessing an array index that is out of bounds (negative or >= length) throws ArrayIndexOutOfBoundsException. Always check the array length before accessing elements.

Strings in Java

Definition

A String in Java is an object that represents a sequence of characters. Strings are immutable, meaning once created, their values cannot be changed.

Key Points

String Creation

StringDemo.java
public class StringDemo {
    public static void main(String[] args) {
        // String literal (stored in String Pool)
        String s1 = "Hello";
        
        // Using new keyword (creates new object in heap)
        String s2 = new String("Hello");
        
        // Concatenation
        String fullName = "John" + " " + "Doe";
        
        // String length
        System.out.println("Length: " + s1.length());  // 5
        
        // Comparison
        System.out.println(s1 == s2);           // false (different objects)
        System.out.println(s1.equals(s2));      // true (same content)
        System.out.println(s1.equalsIgnoreCase("HELLO"));  // true
    }
}

Common String Methods

StringMethods.java
public class StringMethods {
    public static void main(String[] args) {
        String str = "Hello World";
        
        // Character access
        char ch = str.charAt(0);  // 'H'
        
        // Substring
        String sub = str.substring(0, 5);  // "Hello"
        
        // Case conversion
        String upper = str.toUpperCase();  // "HELLO WORLD"
        String lower = str.toLowerCase();  // "hello world"
        
        // Trim whitespace
        String padded = "  Java  ";
        String trimmed = padded.trim();  // "Java"
        
        // Replace
        String replaced = str.replace('l', 'x');  // "Hexxo Worxd"
        
        // Split
        String csv = "apple,banana,cherry";
        String[] parts = csv.split(",");  // ["apple", "banana", "cherry"]
        
        // Contains and indexOf
        boolean contains = str.contains("World");  // true
        int index = str.indexOf("World");  // 6
        
        // StartsWith and EndsWith
        boolean starts = str.startsWith("Hello");  // true
        boolean ends = str.endsWith("World");  // true
        
        // isEmpty and isBlank (Java 11+)
        boolean empty = "".isEmpty();  // true
    }
}

StringBuilder and StringBuffer

StringBuilderDemo.java
public class StringBuilderDemo {
    public static void main(String[] args) {
        // StringBuilder - mutable, not thread-safe, faster
        StringBuilder sb = new StringBuilder();
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        System.out.println(sb.toString());  // "Hello World"
        
        sb.insert(5, ",");  // "Hello, World"
        sb.reverse();  // "dlroW ,olleH"
        sb.delete(0, 5);  // ", olleH"
        
        // StringBuffer - mutable, thread-safe, slower
        StringBuffer buffer = new StringBuffer("Thread Safe");
    }
}
Feature String StringBuilder StringBuffer
Mutability Immutable Mutable Mutable
Thread Safety Yes (immutable) No Yes (synchronized)
Performance Slow for modifications Fast Slower than StringBuilder

Exam Tip

Use StringBuilder for single-threaded string manipulation and StringBuffer for multi-threaded environments. Never use == to compare String content; always use equals().

Object-Oriented Programming Concepts

Definition

Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects" which contain data (fields) and code (methods). Java is fundamentally an object-oriented language.

Four Pillars of OOP

  1. Encapsulation: Bundling data and methods that operate on the data within a single unit (class)
  2. Inheritance: Mechanism where one class acquires properties and behaviors of another class
  3. Polymorphism: Ability of an object to take many forms
  4. Abstraction: Hiding implementation details and showing only functionality

Classes and Objects

Student.java
public class Student {
    // Instance variables (fields)
    private String name;
    private int age;
    private String rollNo;
    
    // Constructor
    public Student(String name, int age, String rollNo) {
        this.name = name;
        this.age = age;
        this.rollNo = rollNo;
    }
    
    // Default constructor
    public Student() {
        this.name = "Unknown";
        this.age = 0;
    }
    
    // Getter methods
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    // Setter methods
    public void setName(String name) {
        this.name = name;
    }
    
    public void setAge(int age) {
        if (age > 0) {
            this.age = age;
        }
    }
    
    // Method
    public void display() {
        System.out.println("Name: " + name + ", Age: " + age);
    }
    
    // Main method for testing
    public static void main(String[] args) {
        // Creating objects
        Student s1 = new Student("John", 20, "CS101");
        Student s2 = new Student();
        
        s1.display();
        s2.setName("Jane");
        s2.display();
    }
}

Inheritance

InheritanceDemo.java
// Parent class (Superclass)
class Animal {
    protected String name;
    
    public void eat() {
        System.out.println(name + " is eating");
    }
    
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

// Child class (Subclass)
class Dog extends Animal {
    public Dog(String name) {
        this.name = name;
    }
    
    // Additional method
    public void bark() {
        System.out.println(name + " is barking");
    }
    
    // Method overriding
    @Override
    public void eat() {
        System.out.println(name + " is eating dog food");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("Buddy");
        dog.eat();    // Overridden method
        dog.sleep();  // Inherited method
        dog.bark();   // Own method
    }
}

Polymorphism

PolymorphismDemo.java
class Shape {
    public void draw() {
        System.out.println("Drawing a shape");
    }
}

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

class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        // Runtime polymorphism
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();
        
        shape1.draw();  // Drawing a circle
        shape2.draw();  // Drawing a rectangle
        
        // Method overloading (compile-time polymorphism)
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 3));
        System.out.println(calc.add(5.0, 3.0));
    }
}

class Calculator {
    // Method overloading
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
}

Abstract Classes and Interfaces

AbstractDemo.java
// Abstract class
abstract class Vehicle {
    protected String brand;
    
    public Vehicle(String brand) {
        this.brand = brand;
    }
    
    // Abstract method (no implementation)
    public abstract void start();
    
    // Concrete method
    public void stop() {
        System.out.println(brand + " stopped");
    }
}

// Interface
interface Electric {
    void charge();  // implicitly public abstract
    int VOLTAGE = 220;  // implicitly public static final
}

// Concrete class implementing abstract class and interface
class Tesla extends Vehicle implements Electric {
    public Tesla() {
        super("Tesla");
    }
    
    @Override
    public void start() {
        System.out.println("Tesla starting silently");
    }
    
    @Override
    public void charge() {
        System.out.println("Charging at " + VOLTAGE + "V");
    }
}
Feature Abstract Class Interface
Methods Can have abstract and concrete methods Only abstract methods (before Java 8)
Variables Can have any type of variables Only public static final (constants)
Inheritance Single inheritance only Multiple interfaces can be implemented
Constructor Can have constructors Cannot have constructors
Keyword extends implements

Detailed Example: Bank Account System

BankAccountSystem.java
// Demonstrating all OOP principles

// Abstraction using abstract class
abstract class Account {
    protected String accountNumber;
    protected String holderName;
    protected double balance;
    
    public Account(String accountNumber, String holderName, double initialBalance) {
        this.accountNumber = accountNumber;
        this.holderName = holderName;
        this.balance = initialBalance;
    }
    
    // Abstract method (must be implemented by subclasses)
    public abstract void calculateInterest();
    
    // Concrete method
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: $" + amount);
        }
    }
    
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
        } else {
            System.out.println("Insufficient balance");
        }
    }
    
    public double getBalance() {
        return balance;
    }
}

// Inheritance and Polymorphism
class SavingsAccount extends Account {
    private double interestRate;
    
    public SavingsAccount(String accountNumber, String holderName, double balance, double interestRate) {
        super(accountNumber, holderName, balance);
        this.interestRate = interestRate;
    }
    
    @Override
    public void calculateInterest() {
        double interest = balance * interestRate / 100;
        balance += interest;
        System.out.println("Interest added: $" + interest);
    }
}

class CurrentAccount extends Account {
    private double overdraftLimit;
    
    public CurrentAccount(String accountNumber, String holderName, double balance, double overdraftLimit) {
        super(accountNumber, holderName, balance);
        this.overdraftLimit = overdraftLimit;
    }
    
    @Override
    public void withdraw(double amount) {
        if (amount > 0 && amount <= (balance + overdraftLimit)) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
        } else {
            System.out.println("Exceeds overdraft limit");
        }
    }
    
    @Override
    public void calculateInterest() {
        // No interest for current accounts
        System.out.println("No interest for current accounts");
    }
}

public class BankAccountSystem {
    public static void main(String[] args) {
        // Polymorphism: Parent reference to child object
        Account savings = new SavingsAccount("SAV001", "John Doe", 1000, 5.0);
        Account current = new CurrentAccount("CUR001", "Jane Smith", 500, 200);
        
        // Demonstrating polymorphism
        savings.deposit(500);
        savings.calculateInterest();
        System.out.println("Savings Balance: $" + savings.getBalance());
        
        current.withdraw(600);  // Uses overdraft
        System.out.println("Current Balance: $" + current.getBalance());
    }
}

Access Modifiers in Detail

Modifier Class Package Subclass World
public
protected
default (no modifier)
private

Method Overloading vs Overriding

PolymorphismDemo.java
class Calculator {
    // Method Overloading (Compile-time Polymorphism)
    public int add(int a, int b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
}

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

class Circle extends Shape {
    // Method Overriding (Runtime Polymorphism)
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        // Overloading example
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 3));           // 8
        System.out.println(calc.add(5, 3, 2));        // 10
        System.out.println(calc.add(5.5, 3.2));      // 8.7
        
        // Overriding example (Runtime Polymorphism)
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();
        
        shape1.draw();  // Drawing a circle
        shape2.draw();  // Drawing a rectangle
    }
}

this and super Keywords

ThisSuperDemo.java
class Parent {
    int value = 100;
    
    public Parent() {
        System.out.println("Parent constructor");
    }
    
    public void display() {
        System.out.println("Parent display: " + value);
    }
}

class Child extends Parent {
    int value = 200;
    
    public Child() {
        super();  // Calls parent constructor
        System.out.println("Child constructor");
    }
    
    public void display() {
        System.out.println("Child value: " + this.value);      // 200
        System.out.println("Parent value: " + super.value);   // 100
        super.display();  // Calls parent method
    }
}

public class ThisSuperDemo {
    public static void main(String[] args) {
        Child child = new Child();
        child.display();
    }
}

Static vs Instance Members

StaticDemo.java
class Counter {
    // Static variable (shared by all instances)
    static int count = 0;
    
    // Instance variable (unique to each instance)
    int id;
    
    public Counter() {
        count++;  // Increment shared counter
        id = count;  // Assign unique id
    }
    
    // Static method
    public static int getCount() {
        return count;
    }
    
    // Instance method
    public void display() {
        System.out.println("Object ID: " + id + ", Total objects: " + count);
    }
}

public class StaticDemo {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();
        
        c1.display();  // Object ID: 1, Total objects: 3
        c2.display();  // Object ID: 2, Total objects: 3
        c3.display();  // Object ID: 3, Total objects: 3
        
        System.out.println("Total created: " + Counter.getCount());  // 3
    }
}

final Keyword

FinalDemo.java
final class FinalClass {  // Cannot be extended
    final int MAX_VALUE = 100;  // Constant (cannot be changed)
    
    final void display() {  // Cannot be overridden
        System.out.println("Final method");
    }
}

class Example {
    final int x;  // Blank final variable
    
    public Example(int x) {
        this.x = x;  // Must be initialized in constructor
    }
}

Practice Questions

Q1: Create a class hierarchy for Vehicle → Car, Bike with appropriate properties and methods.

Q2: Implement a Shape class with calculateArea() method, then create Circle and Rectangle subclasses.

Q3: What's the difference between method overloading and method overriding? Provide examples.

Q4: Explain the use of 'this' and 'super' keywords with examples.

Q5: When would you use an abstract class vs an interface?

Q6: Create an Employee class with encapsulation (private fields, public getters/setters).

Q7: What is the output if you call a static method using an object reference? Is it allowed?

Exam Tip

Remember: Java supports single inheritance for classes but multiple inheritance through interfaces. From Java 8, interfaces can have default and static methods with implementations.

Packages, CLASSPATH, and JAR Files

Definition

A package in Java is a namespace that organizes classes and interfaces. Packages prevent naming conflicts and provide access control.

Types of Packages

Creating and Using Packages

com/example/util/MathHelper.java
// Package declaration (must match directory structure)
package com.example.util;

public class MathHelper {
    public static int add(int a, int b) {
        return a + b;
    }
    
    public static int multiply(int a, int b) {
        return a * b;
    }
}
Main.java
// Import single class
import com.example.util.MathHelper;

// Import all classes from package
import java.util.*;

// Static import - import static members
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

public class Main {
    public static void main(String[] args) {
        // Using imported class
        int sum = MathHelper.add(5, 3);
        
        // Using static imports (no class name needed)
        double area = PI * 5 * 5;
        double root = sqrt(16);
        
        // Using fully qualified name (no import needed)
        java.util.Date date = new java.util.Date();
    }
}

CLASSPATH

Definition: CLASSPATH is an environment variable that tells the JVM where to look for user-defined classes and packages.

Setting CLASSPATH
# Windows
set CLASSPATH=.;C:\myproject\classes;C:\libs\mylib.jar

# Linux/Mac
export CLASSPATH=.:/home/user/classes:/home/user/libs/mylib.jar

# Using -cp or -classpath option
java -cp .;lib/mylib.jar Main

JAR Files

Definition: JAR (Java Archive) is a package file format used to aggregate many Java class files and associated resources into one file for distribution.

JAR Commands
# Create a JAR file
jar cf myapp.jar *.class

# Create JAR with manifest
jar cfm myapp.jar manifest.txt *.class

# View contents of JAR
jar tf myapp.jar

# Extract JAR
jar xf myapp.jar

# Run executable JAR
java -jar myapp.jar

Common Built-in Packages

Package Description
java.lang Core classes (String, Math, System) - auto-imported
java.util Collections, Date, Scanner, Random
java.io Input/Output classes
java.nio New I/O classes
java.net Networking classes
java.sql Database connectivity (JDBC)

Note

The java.lang package is automatically imported in every Java program. You do not need to explicitly import classes like String, System, or Math.