Skip to main content

Java

Welcome to Java

Java is a powerful, object-oriented programming language known for its "write once, run anywhere" capability. Developed by Sun Microsystems in 1995, Java is designed to be platform-independent through the Java Virtual Machine (JVM). It's widely used for building enterprise-scale applications, Android apps, web services, and large systems. Java's strong typing, automatic memory management (garbage collection), and extensive standard library make it a robust choice for mission-critical applications across industries.

Introduction to Java

How to install Java and write your first "Hello, World!" program.

Step 1: Download and Install Java

  1. Visit the Official Website
    Go to Oracle Java Downloads or OpenJDK and download the latest LTS version of Java for your operating system.
  2. Run the Installer
    • For Windows: Run the downloaded .exe file and follow the installation wizard.
    • For Mac: Use the .dmg installer or install via Homebrew with brew install openjdk
    • For Linux: Use your package manager (e.g., for Ubuntu: sudo apt install openjdk-17-jdk)
  3. Verify the Installation
    Open a terminal or command prompt and type:
    java -version
    This should display the installed Java version.

Step 2: Choose an IDE or Code Editor

While you can write Java in any text editor, IDEs provide powerful features:

  • IntelliJ IDEA – Most popular Java IDE with excellent features
  • Eclipse – Free, open-source IDE with strong community
  • VS Code – Lightweight with Java extensions
  • NetBeans – Another free, full-featured IDE

Step 3: Write and Run Your First Java Program

  1. Create a Java File
    Create a file named HelloWorld.java with the following content:
    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello, World!");
        }
    }
  2. Compile the Program
    Open terminal/command prompt, navigate to the file location, and run:
    javac HelloWorld.java
  3. Run the Program
    Execute the compiled class file:
    java HelloWorld
    You should see the output: Hello, World!

Congratulations! You have successfully installed Java and run your first program. 🎉

Java Syntax Basics

1. Class Structure

Every Java program must have at least one class, and the filename must match the class name.

public class MyClass {
    // Class content goes here
}

2. Main Method

The main method is the entry point of any Java application.

public static void main(String[] args) {
    // Program execution starts here
}

3. Semicolons and Braces

Java requires semicolons at the end of statements and uses curly braces {} to define code blocks.

if (condition) {
    // Code block
    statement;
}

4. Case Sensitivity

Java is case-sensitive. MyVariable and myvariable are different identifiers.

int count = 5;
int Count = 10;  // Different variable

5. Comments

Java supports single-line and multi-line comments.

// This is a single-line comment

/*
 This is a multi-line comment
 that spans multiple lines
*/

/**
 * This is a Javadoc comment used for documentation
 */

Conclusion

Understanding Java's basic syntax is essential for writing correct programs. Key points include proper class structure, the main method requirement, and consistent use of semicolons and braces.

System.out.println

The System.out.println() method in Java is used to display output to the console. It's one of the most commonly used methods for debugging and user interaction.

1. Basic Usage

Print a string or variable value:

System.out.println("Hello, World!");  // Outputs: Hello, World!
System.out.println(42);                // Outputs: 42

2. Printing Multiple Values

Use string concatenation with the + operator:

String name = "Alice";
int age = 25;
System.out.println("Name: " + name + ", Age: " + age);
// Outputs: Name: Alice, Age: 25

3. System.out.print() vs System.out.println()

println() adds a newline at the end, while print() does not:

System.out.print("Hello ");  // No newline
System.out.print("World");    // Continues on same line
System.out.println();         // Adds newline
// Outputs: Hello World

4. Formatting Output

Use System.out.printf() for formatted output:

double price = 19.99;
System.out.printf("Price: $%.2f", price);
// Outputs: Price: $19.99

Conclusion

Java provides multiple ways to output data to the console. System.out.println() is the most common for simple output, while printf() offers formatting capabilities.

Arithmetic Operators

Java provides arithmetic operators for basic mathematical operations.

Basic Arithmetic Operators

int a = 15;
int b = 4;

System.out.println(a + b);   // 19 (addition)
System.out.println(a - b);   // 11 (subtraction)
System.out.println(a * b);   // 60 (multiplication)
System.out.println(a / b);   // 3 (integer division)
System.out.println(a % b);   // 3 (modulus)

double x = 15.0;
double y = 4.0;
System.out.println(x / y);   // 3.75 (floating-point division)

Increment and Decrement

int count = 5;
count++;    // post-increment: count becomes 6
++count;    // pre-increment: count becomes 7
count--;    // post-decrement: count becomes 6
--count;    // pre-decrement: count becomes 5

Comparison Operators

Comparison operators compare values and return boolean results.

int x = 7;
int y = 10;

System.out.println(x == y);   // false (equal to)
System.out.println(x != y);   // true (not equal to)
System.out.println(x > y);    // false
System.out.println(x < y);    // true
System.out.println(x >= 7);   // true
System.out.println(y <= 7);   // false

Object Comparison

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");

System.out.println(s1 == s2);     // true (string pool)
System.out.println(s1 == s3);     // false (different objects)
System.out.println(s1.equals(s3)); // true (content comparison)

Logical Operators

Logical operators combine boolean expressions.

boolean isSunny = true;
boolean isWarm = false;

System.out.println(isSunny && isWarm);   // false (AND)
System.out.println(isSunny || isWarm);    // true (OR)
System.out.println(!isWarm);             // true (NOT)

Short-Circuit Evaluation

// && and || are short-circuit operators
int value = 10;
if (value != 0 && 100 / value > 5) {
    // Safe from division by zero
    System.out.println("Condition met");
}

Bitwise Operators

Bitwise operators work on individual bits of integer types.

int a = 6;   // binary: 110
int b = 3;   // binary: 011

System.out.println(a & b);   // 2 (binary 010) - AND
System.out.println(a | b);   // 7 (binary 111) - OR
System.out.println(a ^ b);   // 5 (binary 101) - XOR
System.out.println(~a);      // -7 (complement)
System.out.println(a << 1);  // 12 (binary 1100) - left shift
System.out.println(a >> 1);  // 3 (binary 011) - right shift
System.out.println(a >>> 1); // 3 (unsigned right shift)

Assignment Operators

Assignment operators assign values to variables, often with operations.

int x = 10;   // simple assignment
x += 5;       // equivalent to x = x + 5 → 15
x -= 3;       // equivalent to x = x - 3 → 12
x *= 2;       // equivalent to x = x * 2 → 24
x /= 4;       // equivalent to x = x / 4 → 6
x %= 4;       // equivalent to x = x % 4 → 2

// Also works with other operators
x &= 3;       // bitwise AND assignment
x |= 8;       // bitwise OR assignment
x <<= 1;      // left shift assignment

Primitive Data Types

Java has 8 primitive data types that store simple values directly in memory.

1. Integer Types

byte smallNumber = 100;        // 8-bit, range: -128 to 127
short mediumNumber = 10000;    // 16-bit, range: -32,768 to 32,767
int number = 100000;           // 32-bit, most commonly used
long bigNumber = 1000000000L;  // 64-bit, note the 'L' suffix

2. Floating-Point Types

float price = 19.99f;         // 32-bit, note the 'f' suffix
double precisePrice = 19.999; // 64-bit, default for decimals

3. Character and Boolean

char letter = 'A';            // 16-bit Unicode character
boolean isJavaFun = true;     // true or false

4. Default Values

Primitives have default values when declared as instance variables:

int defaultInt;        // 0
double defaultDouble;  // 0.0
boolean defaultBool;   // false
char defaultChar;      // '\u0000' (null character)

Strings in Java

Strings in Java are objects that represent sequences of characters. They are immutable.

Creating Strings

// String literals (go to string pool)
String s1 = "Hello";
String s2 = "World";

// Using constructor
String s3 = new String("Hello World");

// Concatenation
String greeting = s1 + " " + s2;
System.out.println(greeting);  // Hello World

String Immutability

String str = "hello";
str.concat(" world");      // Returns new string, original unchanged
System.out.println(str);   // Still "hello"

str = str.concat(" world"); // Reassign to see change
System.out.println(str);   // "hello world"

String Methods

Java String class provides many useful methods for string manipulation.

Common String Methods

String text = " Hello World ";

System.out.println(text.length());        // 13
System.out.println(text.trim());          // "Hello World"
System.out.println(text.toUpperCase());   // " HELLO WORLD "
System.out.println(text.toLowerCase());   // " hello world "
System.out.println(text.charAt(1));       // 'H'
System.out.println(text.substring(1, 6)); // "Hello"
System.out.println(text.replace("World", "Java")); // " Hello Java "

Searching and Checking

String str = "Java Programming";

System.out.println(str.contains("Pro"));    // true
System.out.println(str.startsWith("Java")); // true
System.out.println(str.endsWith("ing"));    // true
System.out.println(str.indexOf("a"));       // 1
System.out.println(str.lastIndexOf("a"));   // 3
System.out.println(str.isEmpty());          // false

String Formatting

Java provides several ways to format strings.

String.format()

String name = "Alice";
int age = 25;
double salary = 50000.50;

String message = String.format("Name: %s, Age: %d, Salary: $%,.2f", 
                              name, age, salary);
System.out.println(message);
// Output: Name: Alice, Age: 25, Salary: $50,000.50

StringBuilder for Efficient Concatenation

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
String result = sb.toString();
System.out.println(result);  // Hello World

Text Blocks (Java 15+)

String html = """
    <html>
        <body>
            <h1>Hello World</h1>
        </body>
    </html>
    """;
System.out.println(html);

Arrays in Java

Arrays in Java are fixed-size containers that store multiple values of the same type.

1. Declaring and Initializing Arrays

// Declaration
int[] numbers;

// Initialization with size
numbers = new int[5];

// Declaration and initialization in one line
int[] scores = new int[3];

// Initialization with values
int[] primes = {2, 3, 5, 7, 11};

2. Accessing Array Elements

int[] numbers = {10, 20, 30, 40, 50};
System.out.println(numbers[0]);  // Outputs: 10
numbers[2] = 35;                 // Modify third element

3. Array Length

int[] numbers = {1, 2, 3, 4, 5};
System.out.println(numbers.length);  // Outputs: 5

4. Multi-dimensional Arrays

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
System.out.println(matrix[1][2]);  // Outputs: 6

Array Indexing

Arrays in Java use zero-based indexing to access elements.

Accessing Array Elements

int[] numbers = {10, 20, 30, 40, 50};

System.out.println(numbers[0]);   // First element: 10
System.out.println(numbers[2]);   // Third element: 30
System.out.println(numbers[4]);   // Last element: 50

numbers[1] = 25;  // Modify second element
System.out.println(numbers[1]);   // Now 25

Array Index Out of Bounds

int[] arr = {1, 2, 3};

// This will throw ArrayIndexOutOfBoundsException
// System.out.println(arr[5]);

Multi-dimensional Array Indexing

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

System.out.println(matrix[0][0]);  // 1
System.out.println(matrix[1][2]);  // 6
System.out.println(matrix[2][1]);  // 8

Array Methods and Utilities

Java provides utility methods for working with arrays through the Arrays class.

Arrays Class Utilities

import java.util.Arrays;

int[] numbers = {5, 2, 8, 1, 9};

// Sorting
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));  // [1, 2, 5, 8, 9]

// Searching (array must be sorted)
int index = Arrays.binarySearch(numbers, 5);
System.out.println(index);  // 2

// Comparing arrays
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
System.out.println(Arrays.equals(arr1, arr2));  // true

// Filling arrays
int[] filled = new int[5];
Arrays.fill(filled, 10);
System.out.println(Arrays.toString(filled));  // [10, 10, 10, 10, 10]

Copying Arrays

int[] original = {1, 2, 3, 4, 5};

// Using Arrays.copyOf
int[] copy1 = Arrays.copyOf(original, 3);  // [1, 2, 3]
int[] copy2 = Arrays.copyOf(original, 7);  // [1, 2, 3, 4, 5, 0, 0]

// Using System.arraycopy
int[] copy3 = new int[5];
System.arraycopy(original, 0, copy3, 0, 5);

ArrayList

ArrayList is a resizable array implementation of the List interface.

Basic ArrayList Operations

import java.util.ArrayList;

ArrayList<String> list = new ArrayList<>();

// Adding elements
list.add("Apple");
list.add("Banana");
list.add("Cherry");

// Accessing elements
System.out.println(list.get(0));  // Apple
System.out.println(list.size());  // 3

// Modifying elements
list.set(1, "Blueberry");
System.out.println(list);  // [Apple, Blueberry, Cherry]

// Removing elements
list.remove(0);
list.remove("Cherry");
System.out.println(list);  // [Blueberry]

Iterating through ArrayList

ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);

// Enhanced for loop
for (int num : numbers) {
    System.out.println(num);
}

// Traditional for loop
for (int i = 0; i < numbers.size(); i++) {
    System.out.println(numbers.get(i));
}

// Using forEach (Java 8+)
numbers.forEach(System.out::println);

LinkedList

LinkedList is a doubly-linked list implementation that allows efficient insertions and deletions.

LinkedList Operations

import java.util.LinkedList;

LinkedList<String> linkedList = new LinkedList<>();

// Adding elements
linkedList.add("First");
linkedList.add("Second");
linkedList.addFirst("New First");  // Add at beginning
linkedList.addLast("Last");        // Add at end

System.out.println(linkedList);  // [New First, First, Second, Last]

// Accessing elements
System.out.println(linkedList.getFirst());  // New First
System.out.println(linkedList.getLast());   // Last

// Removing elements
linkedList.removeFirst();
linkedList.removeLast();
System.out.println(linkedList);  // [First, Second]

LinkedList as Queue/Deque

LinkedList<Integer> queue = new LinkedList<>();

// Queue operations
queue.offer(10);  // add to end
queue.offer(20);
queue.offer(30);

System.out.println(queue.poll());  // remove from front: 10
System.out.println(queue.peek());  // view front: 20

// Stack operations
queue.push(5);     // push to front
System.out.println(queue.pop());   // pop from front: 5

HashMap

HashMap stores key-value pairs and provides constant-time performance for basic operations.

Basic HashMap Operations

import java.util.HashMap;

HashMap<String, Integer> map = new HashMap<>();

// Adding key-value pairs
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

// Accessing values
System.out.println(map.get("Alice"));  // 25
System.out.println(map.get("David"));  // null

// Checking existence
System.out.println(map.containsKey("Bob"));     // true
System.out.println(map.containsValue(40));      // false

// Removing entries
map.remove("Charlie");
System.out.println(map.size());  // 2

Iterating through HashMap

HashMap<String, String> countries = new HashMap<>();
countries.put("US", "United States");
countries.put("UK", "United Kingdom");
countries.put("IN", "India");

// Iterate through keys
for (String code : countries.keySet()) {
    System.out.println(code + ": " + countries.get(code));
}

// Iterate through entries
for (HashMap.Entry<String, String> entry : countries.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
}

// Using forEach (Java 8+)
countries.forEach((k, v) -> System.out.println(k + ": " + v));

HashSet

HashSet stores unique elements and provides constant-time performance for basic operations.

Basic HashSet Operations

import java.util.HashSet;

HashSet<String> set = new HashSet<>();

// Adding elements
set.add("Apple");
set.add("Banana");
set.add("Apple");  // Duplicate - won't be added

System.out.println(set);        // [Apple, Banana]
System.out.println(set.size()); // 2

// Checking existence
System.out.println(set.contains("Banana"));  // true
System.out.println(set.contains("Cherry"));  // false

// Removing elements
set.remove("Apple");
System.out.println(set);  // [Banana]

Set Operations

HashSet<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);

HashSet<Integer> set2 = new HashSet<>();
set2.add(2);
set2.add(3);
set2.add(4);

// Union
HashSet<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("Union: " + union);  // [1, 2, 3, 4]

// Intersection
HashSet<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("Intersection: " + intersection);  // [2, 3]

// Difference
HashSet<Integer> difference = new HashSet<>(set1);
difference.removeAll(set2);
System.out.println("Difference: " + difference);  // [1]

Variables in Java

Variables in Java are strongly typed and must be declared before use.

Variable Declaration and Initialization

// Primitive variables
int age = 25;
double salary = 50000.50;
char grade = 'A';
boolean isActive = true;

// Reference variables
String name = "John Doe";
int[] numbers = {1, 2, 3};

// Multiple declaration
int x = 5, y = 10, z = 15;

// Constants (final variables)
final double PI = 3.14159;
final int MAX_USERS = 100;

Variable Scope

public class ScopeExample {
    int instanceVar = 10;  // Instance variable
    
    public void method() {
        int localVar = 20;  // Local variable
        System.out.println(instanceVar);  // Can access instance variable
        System.out.println(localVar);     // Can access local variable
    }
    
    public void anotherMethod() {
        // System.out.println(localVar);  // Error: localVar not accessible here
        System.out.println(instanceVar);  // Can access instance variable
    }
}

Conditional Statements: if, else, switch

Java provides if-else and switch statements for conditional execution.

if-else Statements

int score = 85;

if (score >= 90) {
    System.out.println("Grade: A");
} else if (score >= 80) {
    System.out.println("Grade: B");  // This executes
} else if (score >= 70) {
    System.out.println("Grade: C");
} else {
    System.out.println("Grade: F");
}

Ternary Operator

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

Switch Statement

int day = 3;
String dayName;

switch (day) {
    case 1:
        dayName = "Monday";
        break;
    case 2:
        dayName = "Tuesday";
        break;
    case 3:
        dayName = "Wednesday";  // This executes
        break;
    default:
        dayName = "Invalid day";
}
System.out.println(dayName);  // Wednesday

Switch Expressions (Java 14+)

int day = 3;
String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    default -> "Invalid day";
};
System.out.println(dayName);  // Wednesday

For Loops in Java

For loops in Java repeat a block of code a specific number of times.

1. Basic For Loop

for (int i = 0; i < 5; i++) {
    System.out.println("Count: " + i);
}
// Outputs: Count: 0, Count: 1, ..., Count: 4

2. Enhanced For Loop (For-Each)

int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
    System.out.println(number);
}
// Outputs each number in the array

3. Nested For Loops

for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 2; j++) {
        System.out.println("i=" + i + ", j=" + j);
    }
}

4. Loop Control with break and continue

for (int i = 0; i < 10; i++) {
    if (i == 3) {
        continue;  // Skip iteration when i is 3
    }
    if (i == 7) {
        break;     // Exit loop when i is 7
    }
    System.out.println(i);
}
// Outputs: 0, 1, 2, 4, 5, 6

While Loops

While loops execute a block of code as long as a condition is true.

Basic While Loop

int count = 1;

while (count <= 5) {
    System.out.println("Count: " + count);
    count++;
}
// Output: Count: 1, Count: 2, ..., Count: 5

Do-While Loop

int number;
do {
    System.out.println("Enter a positive number: ");
    // Simulating user input
    number = -1;  // This would typically come from Scanner
} while (number <= 0);

// Do-while guarantees at least one execution

Infinite Loop with Break

int value = 0;
while (true) {
    System.out.println("Value: " + value);
    value++;
    
    if (value >= 5) {
        break;  // Exit the loop
    }
}

Do-While Loop

The do-while loop executes the code block first, then checks the condition.

Basic Do-While

int i = 1;

do {
    System.out.println("Number: " + i);
    i++;
} while (i <= 3);
// Output: Number: 1, Number: 2, Number: 3

Do-While vs While

// While loop - may not execute if condition is false initially
int x = 10;
while (x < 5) {
    System.out.println("This won't execute");
}

// Do-while loop - executes at least once
int y = 10;
do {
    System.out.println("This will execute once");
} while (y < 5);

Practical Example

import java.util.Scanner;

Scanner scanner = new Scanner(System.in);
int number;

do {
    System.out.print("Enter a number between 1 and 10: ");
    number = scanner.nextInt();
} while (number < 1 || number > 10);

System.out.println("Thank you! You entered: " + number);

Enhanced For Loop

The enhanced for loop (for-each loop) simplifies iterating over arrays and collections.

Iterating over Arrays

int[] numbers = {1, 2, 3, 4, 5};

// Enhanced for loop
for (int num : numbers) {
    System.out.println(num);
}
// Output: 1, 2, 3, 4, 5

Iterating over Collections

import java.util.ArrayList;

ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");

for (String fruit : fruits) {
    System.out.println(fruit);
}
// Output: Apple, Banana, Cherry

Limitations

int[] numbers = {10, 20, 30, 40, 50};

// Enhanced for loop - read-only access
for (int num : numbers) {
    num = num * 2;  // This doesn't modify the array
    System.out.println(num);
}
System.out.println(numbers[0]);  // Still 10

// Traditional for loop for modification
for (int i = 0; i < numbers.length; i++) {
    numbers[i] = numbers[i] * 2;  // Actually modifies the array
}
System.out.println(numbers[0]);  // Now 20

Loop Control Statements

Java provides break, continue, and labels for loop control.

Break Statement

for (int i = 1; i <= 10; i++) {
    if (i == 5) {
        break;  // Exit loop when i is 5
    }
    System.out.println(i);
}
// Output: 1, 2, 3, 4

Continue Statement

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue;  // Skip iteration when i is 3
    }
    System.out.println(i);
}
// Output: 1, 2, 4, 5

Labeled Break and Continue

outerLoop:
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (i == 2 && j == 2) {
            break outerLoop;  // Break out of both loops
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}
// Output: i=1,j=1, i=1,j=2, i=1,j=3, i=2,j=1

Nested Loops

Nested loops are loops within loops, useful for multi-dimensional data.

Basic Nested Loops

// Multiplication table
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        System.out.print(i * j + "\t");
    }
    System.out.println();  // New line after each row
}
// Output:
// 1   2   3
// 2   4   6
// 3   6   9

Pattern Printing

// Right triangle pattern
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= i; j++) {
        System.out.print("* ");
    }
    System.out.println();
}
// Output:
// *
// * *
// * * *
// * * * *
// * * * * *

Nested Enhanced For Loops

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

for (int[] row : matrix) {
    for (int value : row) {
        System.out.print(value + " ");
    }
    System.out.println();
}
// Output:
// 1 2 3
// 4 5 6
// 7 8 9

Classes and Objects

Java is an object-oriented language where classes define blueprints for objects.

1. Defining a Class

public class Car {
    // Fields (attributes)
    String brand;
    String color;
    int year;
    
    // Constructor
    public Car(String brand, String color, int year) {
        this.brand = brand;
        this.color = color;
        this.year = year;
    }
    
    // Method
    public void displayInfo() {
        System.out.println(brand + " " + color + " " + year);
    }
}

2. Creating Objects

// Create Car objects
Car car1 = new Car("Toyota", "Red", 2020);
Car car2 = new Car("Honda", "Blue", 2022);

// Use object methods
car1.displayInfo();  // Outputs: Toyota Red 2020
car2.displayInfo();  // Outputs: Honda Blue 2022

3. Getters and Setters

public class Person {
    private String name;
    private int age;
    
    // Getter
    public String getName() {
        return name;
    }
    
    // Setter
    public void setName(String name) {
        this.name = name;
    }
    
    // Similarly for age
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
}

Defining Methods

Methods in Java define reusable blocks of code with specific functionality.

Method Syntax

// Method definition
public static int add(int a, int b) {
    return a + b;
}

// Method usage
int result = add(5, 3);
System.out.println(result);  // 8

Method Parameters and Return Types

// Void method (no return)
public static void greet(String name) {
    System.out.println("Hello, " + name + "!");
}

// Method with return value
public static double calculateArea(double radius) {
    return Math.PI * radius * radius;
}

// Method with multiple parameters
public static String createEmail(String firstName, String lastName, String domain) {
    return firstName.toLowerCase() + "." + lastName.toLowerCase() + "@" + domain;
}

// Usage
greet("Alice");  // Hello, Alice!
double area = calculateArea(5.0);
String email = createEmail("John", "Doe", "company.com");

Method Overloading

public class Calculator {
    
    // Same method name, different parameters
    public static int add(int a, int b) {
        return a + b;
    }
    
    public static double add(double a, double b) {
        return a + b;
    }
    
    public static int add(int a, int b, int c) {
        return a + b + c;
    }
}

// Usage - Java selects appropriate method based on parameters
System.out.println(Calculator.add(2, 3));      // 5 (uses first method)
System.out.println(Calculator.add(2.5, 3.5));  // 6.0 (uses second method)
System.out.println(Calculator.add(1, 2, 3));   // 6 (uses third method)

Importing Packages

Java packages organize classes, and imports make them accessible.

Basic Import Syntax

// Import single class
import java.util.ArrayList;
import java.util.Scanner;

// Import entire package
import java.util.*;
import java.io.*;

// Static import (for static members)
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

Commonly Used Packages

import java.util.*;        // Collections, Scanner, Date
import java.io.*;          // File I/O
import java.net.*;         // Networking
import java.sql.*;         // Database
import java.time.*;        // Date and Time API
import java.util.regex.*;  // Regular Expressions
import java.util.stream.*; // Stream API

Custom Packages

// File: com/example/utils/StringUtils.java
package com.example.utils;

public class StringUtils {
    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }
    
    public static String reverse(String str) {
        return new StringBuilder(str).reverse().toString();
    }
}

// Usage in another file
import com.example.utils.StringUtils;

public class Main {
    public static void main(String[] args) {
        System.out.println(StringUtils.isEmpty(""));    // true
        System.out.println(StringUtils.reverse("hello")); // olleh
    }
}

File I/O

Java provides comprehensive file input/output operations.

Reading Files

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

// Using BufferedReader
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// Using Scanner
import java.util.Scanner;
import java.io.File;

try (Scanner scanner = new Scanner(new File("file.txt"))) {
    while (scanner.hasNextLine()) {
        System.out.println(scanner.nextLine());
    }
} catch (IOException e) {
    e.printStackTrace();
}

Writing Files

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

// Using BufferedWriter
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
    bw.write("Hello, World!");
    bw.newLine();
    bw.write("This is a new line");
} catch (IOException e) {
    e.printStackTrace();
}

// Using PrintWriter
import java.io.PrintWriter;

try (PrintWriter pw = new PrintWriter(new FileWriter("data.txt"))) {
    pw.println("Name: John Doe");
    pw.println("Age: 30");
    pw.printf("Salary: $%,.2f", 50000.50);
} catch (IOException e) {
    e.printStackTrace();
}

File Operations

import java.io.File;

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

System.out.println("Exists: " + file.exists());
System.out.println("Is file: " + file.isFile());
System.out.println("Is directory: " + file.isDirectory());
System.out.println("Size: " + file.length() + " bytes");
System.out.println("Path: " + file.getAbsolutePath());

// Create directory
File dir = new File("myFolder");
dir.mkdir();

// List files in directory
File folder = new File(".");
String[] files = folder.list();
for (String f : files) {
    System.out.println(f);
}

Inheritance

Inheritance allows a class to inherit fields and methods from another class.

Basic Inheritance

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

// Child class inheriting from Animal
class Dog extends Animal {
    String breed;
    
    public Dog(String name, String breed) {
        super(name);  // Call parent constructor
        this.breed = breed;
    }
    
    public void bark() {
        System.out.println(name + " is barking.");
    }
    
    // Overriding parent method
    @Override
    public void eat() {
        System.out.println(name + " the " + breed + " is eating dog food.");
    }
}

// Usage
Dog dog = new Dog("Buddy", "Golden Retriever");
dog.eat();    // Overridden method
dog.sleep();  // Inherited method
dog.bark();   // Dog-specific method

Method Overriding

class BankAccount {
    protected double balance;
    
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
    
    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            System.out.println("Withdrew: " + amount);
        } else {
            System.out.println("Insufficient funds");
        }
    }
}

class SavingsAccount extends BankAccount {
    private double interestRate;
    
    public SavingsAccount(double initialBalance, double interestRate) {
        super(initialBalance);
        this.interestRate = interestRate;
    }
    
    @Override
    public void withdraw(double amount) {
        if (amount <= balance - 100) {  // Maintain minimum balance
            balance -= amount;
            System.out.println("Withdrew: " + amount);
        } else {
            System.out.println("Cannot withdraw - minimum balance requirement");
        }
    }
    
    public void applyInterest() {
        balance += balance * interestRate;
        System.out.println("Interest applied. New balance: " + balance);
    }
}

Polymorphism

Polymorphism allows objects of different classes to be treated as objects of a common superclass.

Runtime Polymorphism

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

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing a circle with radius " + radius);
    }
    
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle " + width + "x" + height);
    }
    
    @Override
    public double area() {
        return width * height;
    }
}

// Polymorphic behavior
Shape[] shapes = new Shape[3];
shapes[0] = new Shape();
shapes[1] = new Circle(5);
shapes[2] = new Rectangle(4, 6);

for (Shape shape : shapes) {
    shape.draw();      // Calls appropriate version based on actual type
    System.out.println("Area: " + shape.area());
}

Interface Polymorphism

interface Payment {
    void processPayment(double amount);
    boolean isSuccessful();
}

class CreditCardPayment implements Payment {
    private boolean success;
    
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing credit card payment: $" + amount);
        success = true;
    }
    
    @Override
    public boolean isSuccessful() {
        return success;
    }
}

class PayPalPayment implements Payment {
    private boolean success;
    
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing PayPal payment: $" + amount);
        success = true;
    }
    
    @Override
    public boolean isSuccessful() {
        return success;
    }
}

// Using interface polymorphism
Payment payment1 = new CreditCardPayment();
Payment payment2 = new PayPalPayment();

payment1.processPayment(100.0);
payment2.processPayment(50.0);

Encapsulation

Encapsulation bundles data and methods together and restricts direct access to internal state.

Basic Encapsulation

public class BankAccount {
    // Private fields - hidden from outside
    private String accountNumber;
    private double balance;
    private String ownerName;
    
    // Public constructor
    public BankAccount(String accountNumber, String ownerName, double initialBalance) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = initialBalance;
    }
    
    // Public getters and setters
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public double getBalance() {
        return balance;
    }
    
    public String getOwnerName() {
        return ownerName;
    }
    
    public void setOwnerName(String ownerName) {
        if (ownerName != null && !ownerName.trim().isEmpty()) {
            this.ownerName = ownerName;
        }
    }
    
    // Public methods for operations
    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("Withdrew: $" + amount);
        } else {
            System.out.println("Invalid withdrawal amount");
        }
    }
}

Benefits of Encapsulation

public class Student {
    private String name;
    private int age;
    private double gpa;
    
    public Student(String name, int age) {
        this.name = name;
        setAge(age);  // Use setter for validation
        this.gpa = 0.0;
    }
    
    // Validation in setters
    public void setAge(int age) {
        if (age >= 0 && age <= 120) {
            this.age = age;
        } else {
            throw new IllegalArgumentException("Invalid age");
        }
    }
    
    public void setGpa(double gpa) {
        if (gpa >= 0.0 && gpa <= 4.0) {
            this.gpa = gpa;
        } else {
            throw new IllegalArgumentException("GPA must be between 0.0 and 4.0");
        }
    }
    
    // Read-only properties (no setters)
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    public double getGpa() {
        return gpa;
    }
}

Abstraction

Abstraction focuses on essential features while hiding implementation details.

Abstract Classes

// Abstract class - cannot be instantiated
abstract class Vehicle {
    protected String brand;
    protected int speed;
    
    public Vehicle(String brand) {
        this.brand = brand;
        this.speed = 0;
    }
    
    // Abstract method - must be implemented by subclasses
    public abstract void start();
    public abstract void stop();
    
    // Concrete method
    public void displayInfo() {
        System.out.println("Brand: " + brand + ", Speed: " + speed + " km/h");
    }
    
    // Can have concrete methods with implementation
    public void accelerate(int increment) {
        speed += increment;
        System.out.println("Accelerating to " + speed + " km/h");
    }
}

class Car extends Vehicle {
    private int doors;
    
    public Car(String brand, int doors) {
        super(brand);
        this.doors = doors;
    }
    
    @Override
    public void start() {
        System.out.println("Car is starting with key ignition");
        speed = 10;
    }
    
    @Override
    public void stop() {
        System.out.println("Car is stopping with brakes");
        speed = 0;
    }
}

class Motorcycle extends Vehicle {
    public Motorcycle(String brand) {
        super(brand);
    }
    
    @Override
    public void start() {
        System.out.println("Motorcycle is starting with kick start");
        speed = 5;
    }
    
    @Override
    public void stop() {
        System.out.println("Motorcycle is stopping");
        speed = 0;
    }
}

Interfaces

// Interface - defines contract without implementation
interface Drawable {
    void draw();           // Abstract method
    double getArea();      // Abstract method
    
    // Default method (Java 8+)
    default String getDescription() {
        return "This is a drawable object";
    }
    
    // Static method (Java 8+)
    static boolean isValidSize(double size) {
        return size > 0;
    }
}

interface Resizable {
    void resize(double factor);
}

// Class implementing multiple interfaces
class Circle implements Drawable, Resizable {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing a circle with radius " + radius);
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public void resize(double factor) {
        radius *= factor;
        System.out.println("Circle resized to radius " + radius);
    }
}

Exceptions Introduction

Exceptions handle runtime errors and exceptional conditions in a structured way.

Exception Hierarchy

// Checked exceptions - must be handled or declared
IOException, SQLException, ClassNotFoundException

// Unchecked exceptions (RuntimeException)
NullPointerException, ArrayIndexOutOfBoundsException, 
IllegalArgumentException, ArithmeticException

// Error - serious system problems
OutOfMemoryError, StackOverflowError

Common Exceptions

// NullPointerException
String str = null;
// System.out.println(str.length());  // Throws NullPointerException

// ArrayIndexOutOfBoundsException
int[] arr = {1, 2, 3};
// System.out.println(arr[5]);        // Throws ArrayIndexOutOfBoundsException

// ArithmeticException
int a = 10;
// int b = a / 0;                     // Throws ArithmeticException

// NumberFormatException
String numStr = "abc";
// int num = Integer.parseInt(numStr); // Throws NumberFormatException

// ClassCastException
Object obj = "Hello";
// Integer num = (Integer) obj;       // Throws ClassCastException

Throwing Exceptions

public class Calculator {
    public static double divide(double a, double b) {
        if (b == 0) {
            throw new ArithmeticException("Division by zero is not allowed");
        }
        return a / b;
    }
    
    public static int factorial(int n) {
        if (n < 0) {
            throw new IllegalArgumentException("Factorial is not defined for negative numbers");
        }
        if (n == 0) return 1;
        return n * factorial(n - 1);
    }
}

// Usage
try {
    double result = Calculator.divide(10, 0);
} catch (ArithmeticException e) {
    System.out.println("Error: " + e.getMessage());
}

Try-Catch Blocks

Try-catch blocks handle exceptions gracefully without terminating the program.

Basic Try-Catch

try {
    int[] numbers = {1, 2, 3};
    System.out.println(numbers[5]);  // This will throw exception
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Array index is out of bounds!");
    System.out.println("Error message: " + e.getMessage());
}

Multiple Catch Blocks

try {
    String str = null;
    int num = Integer.parseInt("abc");
    System.out.println(str.length());
} catch (NullPointerException e) {
    System.out.println("String is null!");
} catch (NumberFormatException e) {
    System.out.println("Invalid number format!");
} catch (Exception e) {
    System.out.println("Some other error occurred: " + e.getMessage());
}

Try-With-Resources

// Automatically closes resources
try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
    
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.out.println("Error reading file: " + e.getMessage());
}

Finally Block

FileInputStream fis = null;
try {
    fis = new FileInputStream("file.txt");
    // Read file operations
} catch (IOException e) {
    System.out.println("Error: " + e.getMessage());
} finally {
    // Always executes, used for cleanup
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            System.out.println("Error closing file: " + e.getMessage());
        }
    }
    System.out.println("Finally block executed");
}

Custom Exceptions

Custom exceptions allow you to create application-specific exception types.

Creating Custom Exceptions

// Checked exception
class InsufficientFundsException extends Exception {
    private double amount;
    private double balance;
    
    public InsufficientFundsException(double amount, double balance) {
        super("Insufficient funds: Attempted to withdraw " + amount + 
              " but balance is " + balance);
        this.amount = amount;
        this.balance = balance;
    }
    
    public double getAmount() {
        return amount;
    }
    
    public double getBalance() {
        return balance;
    }
}

// Unchecked exception
class InvalidAgeException extends RuntimeException {
    private int invalidAge;
    
    public InvalidAgeException(int invalidAge) {
        super("Invalid age: " + invalidAge + ". Age must be between 0 and 120.");
        this.invalidAge = invalidAge;
    }
    
    public int getInvalidAge() {
        return invalidAge;
    }
}

Using Custom Exceptions

class BankAccount {
    private double balance;
    
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
    
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException(amount, balance);
        }
        balance -= amount;
        System.out.println("Withdrew: " + amount);
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        if (age < 0 || age > 120) {
            throw new InvalidAgeException(age);
        }
        this.name = name;
        this.age = age;
    }
}

// Usage
try {
    BankAccount account = new BankAccount(1000);
    account.withdraw(1500);  // Throws InsufficientFundsException
} catch (InsufficientFundsException e) {
    System.out.println(e.getMessage());
    System.out.println("Shortfall: " + (e.getAmount() - e.getBalance()));
}

// Unchecked exception doesn't require try-catch
Person person = new Person("John", 150);  // Throws InvalidAgeException

Advanced Java Concepts

Advanced Java features for writing efficient, modern code.

Generics

// Generic class
class Box<T> {
    private T content;
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

// Generic methods
class Utilities {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    
    public static <T extends Comparable<T>> T findMax(T[] array) {
        if (array == null || array.length == 0) return null;
        
        T max = array[0];
        for (T element : array) {
            if (element.compareTo(max) > 0) {
                max = element;
            }
        }
        return max;
    }
}

// Usage
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");

Box<Integer> intBox = new Box<>();
intBox.setContent(42);

Integer[] numbers = {1, 5, 3, 9, 2};
System.out.println("Max: " + Utilities.findMax(numbers));

Lambda Expressions

import java.util.Arrays;
import java.util.List;
import java.util.function.*;

// Functional interfaces with lambdas
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");

// Using lambda with forEach
names.forEach(name -> System.out.println(name));

// Using method reference
names.forEach(System.out::println);

// Filtering with lambda
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println);

// Custom functional interface
@FunctionalInterface
interface StringProcessor {
    String process(String input);
}

StringProcessor toUpper = s -> s.toUpperCase();
StringProcessor addExclamation = s -> s + "!";

System.out.println(toUpper.process("hello"));       // HELLO
System.out.println(addExclamation.process("hello")); // hello!

Stream API

import java.util.*;
import java.util.stream.*;

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Filter even numbers, square them, and collect to list
List<Integer> result = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .collect(Collectors.toList());

System.out.println(result);  // [4, 16, 36, 64, 100]

// Reduction operations
int sum = numbers.stream().reduce(0, Integer::sum);
Optional<Integer> max = numbers.stream().max(Integer::compare);

System.out.println("Sum: " + sum);        // 55
System.out.println("Max: " + max.get());  // 10

// Grouping
List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
Map<Integer, List<String>> groupedByLength = words.stream()
    .collect(Collectors.groupingBy(String::length));

System.out.println(groupedByLength);  // {5=[apple], 6=[banana, cherry], 4=[date]}