JAVA

FUNDAMENTAL SKILLS

Tutorial Details

Table of Contents

Basic guides And Resources

JAVA FOUNDATIONAL MATERIAL TUTORIAL

Module 1: Introduction to Java

Objective: Understand what Java is, set up the development environment, and write your first Java program while learning the basic syntax and structure of a Java application.


1. What is Java?

Java is a high-level, class-based, object-oriented programming language. It is widely used for building platform-independent applications, ranging from desktop apps to web and mobile solutions.

Key Characteristics:

  • Platform Independence: “Write Once, Run Anywhere” (WORA). Java code compiles to bytecode, which can run on any device with a Java Virtual Machine (JVM).
  • Object-Oriented: Everything in Java revolves around objects and classes.
  • Rich Ecosystem: Includes extensive standard libraries and frameworks for almost every need.
  • Used For:
    • Web development (using Spring, Hibernate).
    • Android app development.
    • Enterprise applications.

2. Features of Java

  1. Platform Independence: Java programs can run on different operating systems without modification.
  2. Object-Oriented: Encapsulation, inheritance, and polymorphism are key principles.
  3. Robust: Built-in exception handling and memory management.
  4. Secure: Security features like bytecode verification and a secure runtime environment.
  5. Multi-threaded: Supports concurrent programming with threads.
  6. High Performance: The JVM optimizes performance using Just-In-Time (JIT) compilation.

3. Installing JDK and Setting Up an IDE

A. Installing the Java Development Kit (JDK)

  1. Download JDK:
  2. Install JDK:
    • Follow the installation instructions for your OS (Windows, macOS, or Linux).
  3. Verify Installation: Open a terminal/command prompt and type:
    java -version
    
    This should display the installed Java version.

B. Setting Up an IDE

  1. Recommended IDEs:
    • IntelliJ IDEA (Community Edition for free).
    • Eclipse IDE.
    • Visual Studio Code (with Java extensions).
  2. Install IntelliJ IDEA:
    • Download and install IntelliJ IDEA from JetBrains.
  3. Set Up Your First Java Project:
    • Open IntelliJ IDEA.
    • Click on File > New > Project.
    • Select Java as the project type.
    • Set the JDK path and project name.
    • Click Finish to create the project.

4. Writing and Running Your First Java Program

A. Writing “Hello, World!” Program

  1. Open IntelliJ IDEA or your preferred IDE.
  2. Create a new Java class file named HelloWorld.java.
  3. Write the following code:
    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello, World!");
        }
    }
    

B. Explanation of Code

  1. public class HelloWorld:

    • Defines a public class named HelloWorld.
    • A Java program must have at least one class.
  2. public static void main(String[] args):

    • Entry point of a Java application.
    • public: Accessible from anywhere.
    • static: Belongs to the class rather than an instance.
    • void: Returns no value.
    • String[] args: Array of command-line arguments.
  3. System.out.println("Hello, World!");:

    • Prints the message "Hello, World!" to the console.

C. Running the Program

  1. In IntelliJ IDEA:
    • Right-click on the file and select Run HelloWorld.main().
  2. In the terminal:
    • Compile the program:
      javac HelloWorld.java
      
    • Run the compiled program:
      java HelloWorld
      

5. Understanding Java Syntax and Program Structure

A. Structure of a Java Program

// This is a single-line comment
/*
   This is a multi-line comment
*/
public class MyClass {  // Class declaration
    public static void main(String[] args) {  // Main method
        // Code goes here
    }
}

B. Key Components

  1. Classes:
    • Everything in Java is encapsulated in a class.
  2. Methods:
    • Functions inside classes, e.g., main() method.
  3. Statements:
    • Instructions executed sequentially, e.g., System.out.println();.
  4. Comments:
    • Used to document code.

6. Practical Exercises

Exercise 1: Print Your Name

Modify the “Hello, World!” program to print your name instead.

public class MyName {
    public static void main(String[] args) {
        System.out.println("Hello, my name is [Your Name]!");
    }
}

Exercise 2: Simple Math Operations

Write a program to calculate and print the sum of two numbers.

public class SimpleMath {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int sum = a + b;
        System.out.println("The sum is: " + sum);
    }
}

Exercise 3: Command-Line Arguments

Write a program to accept two numbers as command-line arguments and print their product.

public class CommandLineArgs {
    public static void main(String[] args) {
        int a = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);
        System.out.println("The product is: " + (a * b));
    }
}

7. Summary

Key Takeaways:

  1. Java Basics:
    • Platform-independent, object-oriented programming language.
  2. Development Setup:
    • Install JDK and set up an IDE like IntelliJ IDEA.
  3. Basic Syntax:
    • Programs are written in classes, with the main method serving as the entry point.
  4. First Program:
    • Successfully write and run a “Hello, World!” program.

 


Module 2: Java Basics

Objective: Learn the foundational concepts of Java programming, including data types, variables, operators, user input/output, type conversion, and best practices.


1. Data Types and Variables

A. Variables

Variables are containers for storing data values. In Java, variables must be declared with a specific data type.

Syntax:

dataType variableName = value;

Example:

int age = 25;
double salary = 50000.50;
char grade = 'A';
boolean isStudent = false;

B. Primitive Data Types

TypeDescriptionSizeExample
byteInteger1 byte-128 to 127
shortInteger2 bytes-32,768 to 32,767
intInteger4 bytes-2^31 to 2^31-1
longLarge integer8 bytes-2^63 to 2^63-1
floatDecimal numbers4 bytes3.14f
doubleLarge decimals8 bytes3.14159
charSingle character2 bytes'A'
booleanLogical values1 bittrue or false

C. Constants

To declare constants, use the final keyword.

Example:

final double PI = 3.14159;

2. Operators

A. Arithmetic Operators

Used to perform basic mathematical operations.

OperatorDescriptionExample
+Additiona + b
-Subtractiona - b
*Multiplicationa * b
/Divisiona / b
%Modulusa % b

Example:

int a = 10, b = 3;
System.out.println("Addition: " + (a + b));
System.out.println("Modulus: " + (a % b));

B. Relational Operators

Used to compare values.

OperatorDescriptionExample
==Equal toa == b
!=Not equal toa != b
>Greater thana > b
<Less thana < b
>=Greater than or equal toa >= b
<=Less than or equal toa <= b

C. Logical Operators

Used for combining multiple conditions.

OperatorDescriptionExample
&&Logical ANDa > b && a < c
` `
!Logical NOT!(a > b)

3. Input and Output in Java

A. Output with System.out.println

Used to print messages to the console.

Example:

System.out.println("Welcome to Java!");

B. Input with Scanner

The Scanner class is used to get input from the user.

Steps:

  1. Import the Scanner class:
    import java.util.Scanner;
    
  2. Create a Scanner object:
    Scanner scanner = new Scanner(System.in);
    
  3. Read input:
    • nextInt(): Reads an integer.
    • nextDouble(): Reads a double.
    • nextLine(): Reads a line of text.

Example:

import java.util.Scanner;

public class UserInput {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter your name: ");
        String name = scanner.nextLine();

        System.out.print("Enter your age: ");
        int age = scanner.nextInt();

        System.out.println("Hello, " + name + "! You are " + age + " years old.");
    }
}

4. Type Conversion and Casting

A. Implicit Conversion

Java automatically converts smaller data types to larger types (e.g., int to double).

Example:

int a = 10;
double b = a;  // Implicit conversion
System.out.println(b);

B. Explicit Casting

Used to convert larger types to smaller types manually.

Syntax:

dataType variable = (dataType) value;

Example:

double a = 10.5;
int b = (int) a;  // Explicit casting
System.out.println(b);

5. Comments and Coding Best Practices

A. Comments

  1. Single-line comment:
    // This is a single-line comment.
    
  2. Multi-line comment:
    /*
       This is a
       multi-line comment.
    */
    

B. Best Practices

  1. Use meaningful variable names:
    int numberOfStudents = 25;
    
  2. Follow proper indentation.
  3. Add comments to explain complex logic.
  4. Use constants for fixed values:
    final double TAX_RATE = 0.05;
    

6. Practical Exercises

Exercise 1: Basic Calculator

Write a program to take two numbers as input and perform basic arithmetic operations.

Solution:

import java.util.Scanner;

public class Calculator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter first number: ");
        double num1 = scanner.nextDouble();

        System.out.print("Enter second number: ");
        double num2 = scanner.nextDouble();

        System.out.println("Addition: " + (num1 + num2));
        System.out.println("Subtraction: " + (num1 - num2));
        System.out.println("Multiplication: " + (num1 * num2));
        System.out.println("Division: " + (num1 / num2));
    }
}

Exercise 2: Even or Odd

Write a program to check if a number entered by the user is even or odd.

Solution:

import java.util.Scanner;

public class EvenOdd {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter a number: ");
        int num = scanner.nextInt();

        if (num % 2 == 0) {
            System.out.println("The number is even.");
        } else {
            System.out.println("The number is odd.");
        }
    }
}

Exercise 3: Simple Interest Calculator

Write a program to calculate simple interest (SI = P × R × T / 100).

Solution:

import java.util.Scanner;

public class SimpleInterest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter principal amount: ");
        double principal = scanner.nextDouble();

        System.out.print("Enter rate of interest: ");
        double rate = scanner.nextDouble();

        System.out.print("Enter time in years: ");
        double time = scanner.nextDouble();

        double simpleInterest = (principal * rate * time) / 100;
        System.out.println("The Simple Interest is: " + simpleInterest);
    }
}

7. Summary

Key Takeaways:

  1. Data Types: Java provides a rich set of primitive data types.
  2. Operators: Perform calculations and comparisons using arithmetic, relational, and logical operators.
  3. Input/Output: Use Scanner for user input and System.out.println for output.
  4. Type Conversion: Learn implicit and explicit conversions.
  5. Best Practices: Write clean, readable, and well-documented code.

 

Module 3: Control Flow Statements

Objective: Learn how to control the flow of execution in Java programs using conditional statements, loops, and jump statements.


1. If-Else Statements

A. Syntax

if (condition) {
    // Code to execute if condition is true
} else if (anotherCondition) {
    // Code to execute if anotherCondition is true
} else {
    // Code to execute if none of the above conditions are true
}

B. Example

public class IfElseExample {
    public static void main(String[] args) {
        int number = 10;

        if (number > 0) {
            System.out.println("The number is positive.");
        } else if (number < 0) {
            System.out.println("The number is negative.");
        } else {
            System.out.println("The number is zero.");
        }
    }
}

2. Switch-Case

A. Syntax

switch (variable) {
    case value1:
        // Code to execute for value1
        break;
    case value2:
        // Code to execute for value2
        break;
    default:
        // Code to execute if no cases match
}

B. Example

public class SwitchExample {
    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;
            default:
                System.out.println("Invalid day");
        }
    }
}

3. Loops

A. For Loops

Syntax

for (initialization; condition; increment/decrement) {
    // Code to execute in each iteration
}

Example

public class ForLoopExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Count: " + i);
        }
    }
}

B. While Loops

Syntax

while (condition) {
    // Code to execute while condition is true
}

Example

public class WhileLoopExample {
    public static void main(String[] args) {
        int i = 1;

        while (i <= 5) {
            System.out.println("Count: " + i);
            i++;
        }
    }
}

C. Do-While Loops

Syntax

do {
    // Code to execute
} while (condition);

Example

public class DoWhileExample {
    public static void main(String[] args) {
        int i = 1;

        do {
            System.out.println("Count: " + i);
            i++;
        } while (i <= 5);
    }
}

4. Break, Continue, and Labels

A. Break Statement

Exits the loop immediately.

Example

public class BreakExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                break;
            }
            System.out.println("Count: " + i);
        }
    }
}

B. Continue Statement

Skips the current iteration and moves to the next iteration.

Example

public class ContinueExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                continue;
            }
            System.out.println("Count: " + i);
        }
    }
}

C. Labels

Used with break or continue to exit or continue a specific loop.

Example

public class LabelExample {
    public static void main(String[] args) {
        outerLoop:
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                if (i == 2 && j == 2) {
                    break outerLoop;
                }
                System.out.println("i = " + i + ", j = " + j);
            }
        }
    }
}

5. Practical Exercises

Exercise 1: Find the Maximum of Three Numbers

Write a program that takes three numbers as input and prints the largest of the three using if-else.

Solution:

import java.util.Scanner;

public class MaxNumber {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter first number: ");
        int num1 = scanner.nextInt();

        System.out.print("Enter second number: ");
        int num2 = scanner.nextInt();

        System.out.print("Enter third number: ");
        int num3 = scanner.nextInt();

        if (num1 >= num2 && num1 >= num3) {
            System.out.println("The largest number is: " + num1);
        } else if (num2 >= num1 && num2 >= num3) {
            System.out.println("The largest number is: " + num2);
        } else {
            System.out.println("The largest number is: " + num3);
        }
    }
}

Exercise 2: Multiplication Table

Write a program that prints the multiplication table of a number using a for loop.

Solution:

import java.util.Scanner;

public class MultiplicationTable {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter a number: ");
        int number = scanner.nextInt();

        for (int i = 1; i <= 10; i++) {
            System.out.println(number + " x " + i + " = " + (number * i));
        }
    }
}

Exercise 3: Sum of Even Numbers

Write a program to calculate the sum of all even numbers between 1 and 100 using a while loop.

Solution:

public class SumEvenNumbers {
    public static void main(String[] args) {
        int sum = 0;
        int i = 1;

        while (i <= 100) {
            if (i % 2 == 0) {
                sum += i;
            }
            i++;
        }

        System.out.println("Sum of even numbers from 1 to 100 is: " + sum);
    }
}

6. Summary

Key Takeaways:

  1. If-Else Statements:
    • Use for conditional branching.
  2. Switch-Case:
    • Suitable for fixed-value conditions.
  3. Loops:
    • For loops for definite iterations.
    • While loops for indefinite iterations.
    • Do-while loops to ensure at least one execution.
  4. Break and Continue:
    • Control loop flow and exit early or skip iterations.

 

 

Module 4: Arrays and Strings

Objective: Learn how to work with collections of data using arrays and manipulate text using Java’s String classes and utilities.


1. Arrays

A. Declaring, Initializing, and Accessing Arrays

Syntax:

dataType[] arrayName = new dataType[size];

Example:

public class ArrayExample {
    public static void main(String[] args) {
        int[] numbers = new int[5];  // Declare and initialize an array of size 5
        numbers[0] = 10;            // Assign values
        numbers[1] = 20;
        numbers[2] = 30;

        System.out.println("First element: " + numbers[0]);
        System.out.println("Second element: " + numbers[1]);

        // Iterating through the array
        for (int i = 0; i < numbers.length; i++) {
            System.out.println("Element at index " + i + ": " + numbers[i]);
        }
    }
}

B. Multidimensional Arrays

Syntax:

dataType[][] arrayName = new dataType[rows][columns];

Example:

public class MultiDimensionalArray {
    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        // Accessing elements
        System.out.println("Element at (1, 1): " + matrix[1][1]);  // Outputs 5

        // Iterating through the matrix
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }
}

C. Common Array Operations

1. Sorting

Use Arrays.sort() to sort an array.

Example:

import java.util.Arrays;

public class ArraySort {
    public static void main(String[] args) {
        int[] numbers = {5, 3, 8, 1, 2};
        Arrays.sort(numbers);

        System.out.println("Sorted array: " + Arrays.toString(numbers));
    }
}

2. Searching

Use Arrays.binarySearch() to search in a sorted array.

Example:

import java.util.Arrays;

public class ArraySearch {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        int index = Arrays.binarySearch(numbers, 3);

        System.out.println("Element 3 found at index: " + index);
    }
}

2. Strings in Java

A. String Basics

Declaration and Initialization:

String str1 = "Hello";
String str2 = new String("World");

String Methods:

  1. length(): Returns the length of the string.
  2. charAt(): Returns the character at a specific index.
  3. substring(): Extracts a portion of the string.
  4. toUpperCase()/toLowerCase(): Converts the string to upper/lower case.

Example:

public class StringMethods {
    public static void main(String[] args) {
        String str = "Hello, World!";

        System.out.println("Length: " + str.length());
        System.out.println("Character at index 1: " + str.charAt(1));
        System.out.println("Substring (0, 5): " + str.substring(0, 5));
        System.out.println("Uppercase: " + str.toUpperCase());
    }
}

B. StringBuilder and StringBuffer

StringBuilder:

Used for mutable strings (faster, not thread-safe).

Example:

public class StringBuilderExample {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("Hello");
        sb.append(", World!");
        System.out.println(sb.toString());
    }
}

StringBuffer:

Similar to StringBuilder, but thread-safe.

Example:

public class StringBufferExample {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello");
        sb.append(", World!");
        System.out.println(sb.toString());
    }
}

C. Regular Expressions for String Manipulation

Using the Pattern and Matcher Classes:

  1. Find Patterns:

    import java.util.regex.*;
    
    public class RegexExample {
        public static void main(String[] args) {
            String text = "The rain in Spain";
            String regex = "rain";
    
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(text);
    
            while (matcher.find()) {
                System.out.println("Found: " + matcher.group() + " at index " + matcher.start());
            }
        }
    }
    
  2. Replace Patterns:

    public class RegexReplace {
        public static void main(String[] args) {
            String text = "123-456-7890";
            String formatted = text.replaceAll("-", " ");
            System.out.println("Formatted number: " + formatted);
        }
    }
    

3. Practical Exercises

Exercise 1: Reverse an Array

Write a program to reverse an array.

Solution:

public class ReverseArray {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        for (int i = arr.length - 1; i >= 0; i--) {
            System.out.print(arr[i] + " ");
        }
    }
}

Exercise 2: Count Vowels in a String

Write a program to count the number of vowels in a given string.

Solution:

public class VowelCount {
    public static void main(String[] args) {
        String str = "Hello, World!";
        int count = 0;

        for (char c : str.toLowerCase().toCharArray()) {
            if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
                count++;
            }
        }

        System.out.println("Number of vowels: " + count);
    }
}

Exercise 3: Palindrome Checker

Write a program to check if a string is a palindrome.

Solution:

public class PalindromeChecker {
    public static void main(String[] args) {
        String str = "madam";
        String reversed = new StringBuilder(str).reverse().toString();

        if (str.equals(reversed)) {
            System.out.println(str + " is a palindrome.");
        } else {
            System.out.println(str + " is not a palindrome.");
        }
    }
}

4. Summary

Key Takeaways:

  1. Arrays:
    • Store collections of similar data types.
    • Perform operations like sorting and searching using built-in methods.
  2. Strings:
    • Immutable by default.
    • Use StringBuilder or StringBuffer for mutable strings.
  3. Regular Expressions:
    • Useful for pattern matching and string manipulation.

 

Module 5: Object-Oriented Programming (OOP) Basics

Objective: Learn the foundational principles of Object-Oriented Programming (OOP) in Java, including working with classes, objects, encapsulation, inheritance, and polymorphism.


1. Classes and Objects

A. What are Classes and Objects?

  • Class: A blueprint for creating objects. It defines properties (fields) and behaviors (methods).
  • Object: An instance of a class.

B. Syntax

class ClassName {
    // Fields (attributes)
    int attribute;

    // Methods (behaviors)
    void methodName() {
        // Method implementation
    }
}

C. Example

class Car {
    String brand;
    int speed;

    void display() {
        System.out.println("Brand: " + brand + ", Speed: " + speed);
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();  // Create an object of Car
        car.brand = "Toyota";
        car.speed = 120;
        car.display();
    }
}

2. Constructors

A. What is a Constructor?

  • A constructor is a special method used to initialize objects.
  • It has the same name as the class and no return type.

B. Types of Constructors

  1. Default Constructor:

    • Automatically provided if no constructor is defined.
    class Person {
        String name;
    
        Person() {  // Default constructor
            name = "Default Name";
        }
    }
    
  2. Parameterized Constructor:

    • Used to pass initial values to fields.
    class Person {
        String name;
    
        Person(String name) {  // Parameterized constructor
            this.name = name;
        }
    }
    

C. Example

class Person {
    String name;

    // Default Constructor
    Person() {
        name = "Unknown";
    }

    // Parameterized Constructor
    Person(String name) {
        this.name = name;
    }

    void display() {
        System.out.println("Name: " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person();  // Default constructor
        person1.display();

        Person person2 = new Person("Alice");  // Parameterized constructor
        person2.display();
    }
}

3. The this Keyword

  • Refers to the current instance of the class.
  • Used to resolve naming conflicts between instance variables and method parameters.

Example

class Rectangle {
    int width, height;

    Rectangle(int width, int height) {
        this.width = width;   // Resolving variable conflict
        this.height = height;
    }

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

4. Encapsulation

  • Encapsulation is the concept of restricting direct access to certain fields and methods of a class.
  • Achieved using private fields and public getters and setters.

A. Example

class BankAccount {
    private double balance;  // Private field

    // Getter
    public double getBalance() {
        return balance;
    }

    // Setter
    public void setBalance(double balance) {
        if (balance >= 0) {
            this.balance = balance;
        } else {
            System.out.println("Invalid balance!");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.setBalance(1000.50);  // Setting value using setter
        System.out.println("Balance: " + account.getBalance());  // Accessing value using getter
    }
}

5. Inheritance

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

A. Syntax

class ParentClass {
    // Fields and methods
}

class ChildClass extends ParentClass {
    // Additional fields and methods
}

B. Example

class Animal {
    void eat() {
        System.out.println("This animal eats food.");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks.");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();  // Inherited method
        dog.bark(); // Child class method
    }
}

C. super Keyword

  • Refers to the parent class.
  • Used to access parent class methods or constructors.

Example:

class Animal {
    Animal() {
        System.out.println("Animal Constructor");
    }
}

class Dog extends Animal {
    Dog() {
        super();  // Calls the parent class constructor
        System.out.println("Dog Constructor");
    }
}

6. Polymorphism

Polymorphism allows methods to perform different behaviors based on the object calling them.


A. Method Overloading

  • Same method name with different parameter lists.

Example:

class Calculator {
    int add(int a, int b) {
        return a + b;
    }

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

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 10));       // Calls int version
        System.out.println(calc.add(5.5, 10.5));  // Calls double version
    }
}

B. Method Overriding

  • A child class provides its own implementation of a method from the parent class.

Example:

class Animal {
    void sound() {
        System.out.println("Animal makes a sound.");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();  // Dynamic method dispatch
        myDog.sound();             // Calls Dog's version of sound()
    }
}

C. Dynamic Method Dispatch

  • A reference of a parent class points to a child class object, and method calls are resolved at runtime.

Example:

Animal animal = new Dog();  // Parent reference, child object
animal.sound();             // Calls Dog's overridden method

7. Practical Exercises

Exercise 1: Create a Student Class

Write a program to create a Student class with fields name, rollNumber, and marks. Use constructors, getters, and setters.


Exercise 2: Bank Account Inheritance

Create a base Account class with fields like accountNumber and balance. Extend it with a SavingsAccount class to add interest calculation.


Exercise 3: Calculator with Overloading

Write a program to demonstrate method overloading in a calculator class (e.g., add, subtract for different data types).


8. Summary

Key Takeaways:

  1. Classes and Objects: Understand the relationship between classes (blueprints) and objects (instances).
  2. Constructors: Use default and parameterized constructors to initialize objects.
  3. Encapsulation: Protect data using private fields and public getters/setters.
  4. Inheritance: Reuse code and extend functionality with extends and the super keyword.
  5. Polymorphism:
    • Overloading: Same method, different parameters.
    • Overriding: Redefine parent class methods in the child class.

 

Module 6: Exception Handling

Objective: Learn how to handle runtime errors gracefully in Java using exception handling techniques, including working with checked and unchecked exceptions, try-catch-finally blocks, throwing exceptions, and creating custom exceptions.


1. What is Exception Handling?

  • An exception is an event that disrupts the normal flow of a program.
  • Exception handling allows you to manage these events and prevent your program from crashing.

2. Types of Exceptions

A. Checked Exceptions

  • Checked at compile-time.
  • Must be either caught or declared in the method signature using throws.
  • Examples:
    • IOException
    • SQLException

B. Unchecked Exceptions

  • Occur at runtime and are not checked at compile-time.
  • Extend the RuntimeException class.
  • Examples:
    • ArithmeticException
    • NullPointerException

3. Try-Catch-Finally Blocks

A. Try-Catch Block

Used to catch exceptions and handle them.

Syntax:

try {
    // Code that may throw an exception
} catch (ExceptionType e) {
    // Code to handle the exception
}

Example:

public class TryCatchExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;  // This will throw ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("Error: Division by zero is not allowed.");
        }
    }
}

B. Finally Block

The finally block is always executed, regardless of whether an exception occurred or not.

Syntax:

try {
    // Code that may throw an exception
} catch (ExceptionType e) {
    // Code to handle the exception
} finally {
    // Code that will always execute
}

Example:

public class FinallyExample {
    public static void main(String[] args) {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[5]);  // This will throw ArrayIndexOutOfBoundsException
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Error: Array index out of bounds.");
        } finally {
            System.out.println("This block always executes.");
        }
    }
}

4. Throwing Exceptions with throw

  • Use throw to explicitly throw an exception.

Syntax:

throw new ExceptionType("Error message");

Example:

public class ThrowExample {
    public static void main(String[] args) {
        try {
            validateAge(15);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }

    public static void validateAge(int age) {
        if (age < 18) {
            throw new IllegalArgumentException("Age must be at least 18.");
        }
    }
}

5. Custom Exceptions

  • You can create your own exception classes by extending the Exception class or RuntimeException.

Example: Creating a Custom Exception

class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            withdraw(500, 300);
        } catch (InsufficientFundsException e) {
            System.out.println(e.getMessage());
        }
    }

    public static void withdraw(int balance, int amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException("Insufficient funds for withdrawal.");
        }
        System.out.println("Withdrawal successful.");
    }
}

6. Practical Exercises

Exercise 1: Handle Division by Zero

Write a program to take two integers as input and handle the division by zero exception.

Solution:

import java.util.Scanner;

public class Division {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            System.out.print("Enter numerator: ");
            int numerator = scanner.nextInt();

            System.out.print("Enter denominator: ");
            int denominator = scanner.nextInt();

            int result = numerator / denominator;
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Error: Division by zero is not allowed.");
        }
    }
}

Exercise 2: Validate User Input

Write a program to validate user input. If the input is negative, throw an exception.

Solution:

import java.util.Scanner;

public class ValidateInput {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            System.out.print("Enter a positive number: ");
            int number = scanner.nextInt();

            if (number < 0) {
                throw new IllegalArgumentException("Number must be positive.");
            }

            System.out.println("You entered: " + number);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}

Exercise 3: File Not Found

Write a program to handle a FileNotFoundException.

Solution:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class FileRead {
    public static void main(String[] args) {
        try {
            File file = new File("nonexistentfile.txt");
            Scanner scanner = new Scanner(file);
        } catch (FileNotFoundException e) {
            System.out.println("Error: File not found.");
        }
    }
}

7. Summary

Key Takeaways:

  1. Checked vs Unchecked Exceptions:
    • Checked exceptions are compile-time errors (e.g., IOException).
    • Unchecked exceptions occur at runtime (e.g., ArithmeticException).
  2. Try-Catch-Finally:
    • Use try to write code that may throw an exception.
    • Use catch to handle exceptions.
    • Use finally for cleanup operations.
  3. Throw and Custom Exceptions:
    • Use throw to manually throw exceptions.
    • Create custom exceptions by extending Exception or RuntimeException.

 

Module 7: Java Collections Framework

Objective: Learn how to use Java’s built-in data structures to efficiently manage and manipulate collections of data.


1. Introduction to Collections

  • The Java Collections Framework (JCF) provides a set of classes and interfaces to store and manipulate groups of objects.
  • It includes Lists, Sets, Maps, and Queues.

Key Interfaces in the Collections Framework

InterfaceDescription
ListAn ordered collection of elements that allows duplicates.
SetA collection that does not allow duplicate elements.
MapA collection of key-value pairs with unique keys.
QueueA collection that represents a queue (FIFO).

2. List

  • A List is an ordered collection that allows duplicate elements.
  • Common implementations:
    • ArrayList: Uses a dynamic array.
    • LinkedList: Uses a doubly-linked list.

A. ArrayList

Example:

import java.util.ArrayList;

public class ArrayListExample {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();

        // Adding elements
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");

        // Accessing elements
        System.out.println("First name: " + names.get(0));

        // Iterating
        for (String name : names) {
            System.out.println(name);
        }

        // Removing elements
        names.remove("Bob");
        System.out.println("After removal: " + names);
    }
}

B. LinkedList

Example:

import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        LinkedList<Integer> numbers = new LinkedList<>();

        // Adding elements
        numbers.add(10);
        numbers.add(20);
        numbers.addFirst(5);  // Adds 5 at the beginning

        // Accessing elements
        System.out.println("First element: " + numbers.getFirst());

        // Removing elements
        numbers.removeLast();
        System.out.println("After removal: " + numbers);
    }
}

3. Set

  • A Set is a collection that does not allow duplicate elements.
  • Common implementations:
    • HashSet: Unordered, does not maintain insertion order.
    • TreeSet: Sorted in natural order.

A. HashSet

Example:

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        HashSet<String> cities = new HashSet<>();

        // Adding elements
        cities.add("New York");
        cities.add("Los Angeles");
        cities.add("Chicago");
        cities.add("New York");  // Duplicate, will not be added

        // Iterating
        for (String city : cities) {
            System.out.println(city);
        }
    }
}

B. TreeSet

Example:

import java.util.TreeSet;

public class TreeSetExample {
    public static void main(String[] args) {
        TreeSet<Integer> numbers = new TreeSet<>();

        // Adding elements
        numbers.add(50);
        numbers.add(10);
        numbers.add(30);

        // Displaying sorted elements
        System.out.println("Sorted Set: " + numbers);
    }
}

4. Map

  • A Map is a collection of key-value pairs.
  • Common implementations:
    • HashMap: Unordered, allows one null key.
    • TreeMap: Sorted by keys.

A. HashMap

Example:

import java.util.HashMap;

public class HashMapExample {
    public static void main(String[] args) {
        HashMap<String, Integer> ages = new HashMap<>();

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

        // Accessing values
        System.out.println("Bob's age: " + ages.get("Bob"));

        // Iterating
        for (String name : ages.keySet()) {
            System.out.println(name + ": " + ages.get(name));
        }
    }
}

B. TreeMap

Example:

import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        TreeMap<String, Integer> scores = new TreeMap<>();

        // Adding key-value pairs
        scores.put("Math", 90);
        scores.put("English", 85);
        scores.put("Science", 95);

        // Displaying sorted keys
        for (String subject : scores.keySet()) {
            System.out.println(subject + ": " + scores.get(subject));
        }
    }
}

5. Iterators and Enhanced For Loops

A. Iterators

  • Used to traverse collections.
  • Provides methods like hasNext() and next().

Example:

import java.util.ArrayList;
import java.util.Iterator;

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");

        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

B. Enhanced For Loop

  • A simpler way to iterate over collections.

Example:

import java.util.ArrayList;

public class EnhancedForLoopExample {
    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);

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

6. Practical Exercises

Exercise 1: Remove Duplicates from a List

Write a program to remove duplicate elements from a list using a HashSet.

Solution:

import java.util.ArrayList;
import java.util.HashSet;

public class RemoveDuplicates {
    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(2);
        numbers.add(3);

        HashSet<Integer> uniqueNumbers = new HashSet<>(numbers);
        System.out.println("Unique numbers: " + uniqueNumbers);
    }
}

Exercise 2: Count Word Frequency

Write a program to count the frequency of words in a given text using a HashMap.

Solution:

import java.util.HashMap;

public class WordFrequency {
    public static void main(String[] args) {
        String text = "apple banana apple orange banana apple";

        HashMap<String, Integer> wordCount = new HashMap<>();
        for (String word : text.split(" ")) {
            wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
        }

        System.out.println("Word Frequency: " + wordCount);
    }
}

7. Summary

Key Takeaways:

  1. Lists:
    • Use ArrayList for dynamic arrays.
    • Use LinkedList for frequent insertions/deletions.
  2. Sets:
    • Use HashSet for unique elements.
    • Use TreeSet for sorted unique elements.
  3. Maps:
    • Use HashMap for key-value pairs without order.
    • Use TreeMap for sorted key-value pairs.
  4. Iterators and Enhanced For Loops:
    • Simplify traversal of collections.

 

Module 8: File Handling

Objective: Learn how to work with files and directories in Java, including reading, writing, handling file paths, and understanding serialization and deserialization.


1. Basics of File Handling in Java

  • The java.io package provides classes for file handling.
  • Common Classes:
    • File: Represents file and directory paths.
    • FileReader and FileWriter: For reading and writing text files.
    • BufferedReader and BufferedWriter: For efficient reading and writing.

2. Reading and Writing to Files

A. Writing to a File with FileWriter

Example:

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

public class FileWriterExample {
    public static void main(String[] args) {
        try {
            FileWriter writer = new FileWriter("example.txt");
            writer.write("Hello, World!\n");
            writer.write("Welcome to Java File Handling.");
            writer.close();
            System.out.println("File written successfully.");
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

B. Reading from a File with FileReader

Example:

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

public class FileReaderExample {
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader("example.txt");
            int character;
            while ((character = reader.read()) != -1) {
                System.out.print((char) character);
            }
            reader.close();
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

C. Using BufferedReader for Efficient Reading

Example:

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

public class BufferedReaderExample {
    public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new FileReader("example.txt"));
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
            br.close();
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

3. Handling File Paths and Directories

A. Using the File Class

Creating and Checking Files/Directories:

import java.io.File;
import java.io.IOException;

public class FileExample {
    public static void main(String[] args) {
        File file = new File("example.txt");

        try {
            if (file.createNewFile()) {
                System.out.println("File created: " + file.getName());
            } else {
                System.out.println("File already exists.");
            }
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }

        // Check if it's a file or directory
        System.out.println("Is file: " + file.isFile());
        System.out.println("Is directory: " + file.isDirectory());
    }
}

Working with Directories:

import java.io.File;

public class DirectoryExample {
    public static void main(String[] args) {
        File directory = new File("MyDirectory");

        // Create directory
        if (directory.mkdir()) {
            System.out.println("Directory created: " + directory.getName());
        } else {
            System.out.println("Directory already exists.");
        }

        // List files in the directory
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                System.out.println(file.getName());
            }
        }
    }
}

4. Serialization and Deserialization

Serialization is the process of converting an object into a byte stream, while deserialization is the reverse process.

A. Steps to Serialize and Deserialize

  1. Implement the Serializable interface in your class.
  2. Use ObjectOutputStream to serialize.
  3. Use ObjectInputStream to deserialize.

B. Example: Serialization

Class to Serialize:

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    int age;

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

Serialization:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class SerializeExample {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25);

        try {
            FileOutputStream fileOut = new FileOutputStream("person.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(person);
            out.close();
            fileOut.close();
            System.out.println("Object serialized successfully.");
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

C. Example: Deserialization

Deserialization:

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class DeserializeExample {
    public static void main(String[] args) {
        try {
            FileInputStream fileIn = new FileInputStream("person.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Person person = (Person) in.readObject();
            in.close();
            fileIn.close();

            System.out.println("Deserialized Object:");
            System.out.println("Name: " + person.name);
            System.out.println("Age: " + person.age);
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

5. Practical Exercises

Exercise 1: Count Words in a File

Write a program to count the number of words in a text file.


Exercise 2: File Copy

Write a program to copy the content of one file to another.

Solution:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileCopy {
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader("source.txt");
            FileWriter writer = new FileWriter("destination.txt");

            int character;
            while ((character = reader.read()) != -1) {
                writer.write(character);
            }

            reader.close();
            writer.close();
            System.out.println("File copied successfully.");
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

Exercise 3: Directory Explorer

Write a program to list all files and directories in a given directory path.


6. Summary

Key Takeaways:

  1. Reading and Writing:
    • Use FileReader and FileWriter for text files.
    • Use BufferedReader and BufferedWriter for efficient file handling.
  2. File and Directory Management:
    • Use the File class to create, delete, and check files or directories.
  3. Serialization:
    • Use ObjectOutputStream and ObjectInputStream to serialize and deserialize objects.

 


Module 9: Threads and Multithreading

Objective: Learn how to write concurrent programs in Java using threads and understand thread lifecycle, synchronization, and thread pool management with ExecutorService.


1. What are Threads and Multithreading?

  • A thread is a lightweight process that allows multiple operations to run concurrently.
  • Multithreading is the ability of a program to execute multiple threads simultaneously, improving efficiency.

2. Creating Threads in Java

A. Using the Thread Class

Steps:

  1. Extend the Thread class.
  2. Override the run() method.
  3. Call start() to begin execution.

Example:

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Thread: " + i);
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();  // Start the thread
    }
}

B. Using the Runnable Interface

Steps:

  1. Implement the Runnable interface.
  2. Override the run() method.
  3. Pass the Runnable object to a Thread object and call start().

Example:

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Runnable Thread: " + i);
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

3. Thread Lifecycle and Methods

A. Thread Lifecycle

  1. New: Thread object is created but not started.
  2. Runnable: Thread is ready to run and waiting for CPU time.
  3. Running: Thread is executing.
  4. Blocked/Waiting: Thread is waiting for resources or signals.
  5. Terminated: Thread execution is complete.

B. Common Thread Methods

MethodDescription
start()Starts the thread and invokes the run() method.
join()Waits for a thread to finish before proceeding.
sleep()Pauses the thread for a specified duration.
isAlive()Checks if the thread is still running.

Example: Using join() and sleep()

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Thread: " + i);
            try {
                Thread.sleep(1000);  // Pause for 1 second
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted");
            }
        }
    }
}

public class ThreadMethodsExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();

        try {
            thread.join();  // Main thread waits for MyThread to finish
        } catch (InterruptedException e) {
            System.out.println("Main thread interrupted");
        }

        System.out.println("Main thread resumes after MyThread finishes.");
    }
}

4. Synchronization and Thread Safety

When multiple threads access shared resources, data inconsistencies can occur. Synchronization ensures that only one thread accesses a resource at a time.

A. Synchronized Methods

Syntax:

synchronized void methodName() {
    // Critical section
}

Example:

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizationExample {
    public static void main(String[] args) {
        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            System.out.println("Thread interrupted");
        }

        System.out.println("Final count: " + counter.getCount());
    }
}

B. Synchronized Blocks

Example:

class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}

5. Using the ExecutorService for Thread Pools

  • The ExecutorService framework simplifies thread pool management and execution.

Steps:

  1. Create an ExecutorService using Executors class.
  2. Submit tasks to the thread pool.
  3. Shutdown the thread pool.

Example:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        Runnable task1 = () -> System.out.println("Task 1 executed by " + Thread.currentThread().getName());
        Runnable task2 = () -> System.out.println("Task 2 executed by " + Thread.currentThread().getName());
        Runnable task3 = () -> System.out.println("Task 3 executed by " + Thread.currentThread().getName());

        executor.submit(task1);
        executor.submit(task2);
        executor.submit(task3);

        executor.shutdown();  // Stop accepting new tasks
    }
}

6. Practical Exercises

Exercise 1: Multi-threaded Counter

Create a program with two threads incrementing a shared counter. Ensure thread safety using synchronized.


Exercise 2: Print Even and Odd Numbers

Write a program with two threads:

  • Thread 1 prints even numbers.
  • Thread 2 prints odd numbers.

Exercise 3: Task Executor

Create a program using ExecutorService where multiple threads calculate the factorial of numbers from a list.


7. Summary

Key Takeaways:

  1. Creating Threads:
    • Extend Thread or implement Runnable.
    • Use start() to begin execution.
  2. Thread Methods:
    • sleep(), join(), and isAlive() manage thread behavior.
  3. Synchronization:
    • Use synchronized methods or blocks to ensure thread safety.
  4. Thread Pools:
    • Use ExecutorService to manage thread pools for efficient execution.

 


Module 10: Java GUI Development

Objective: Learn how to build graphical user interfaces (GUIs) in Java using Swing, including working with components, handling events, managing layouts, and building a simple GUI application.


1. Introduction to Swing

  • Swing is a part of Java’s javax.swing package and provides a set of lightweight, platform-independent components for creating GUIs.
  • Common Swing components:
    • JFrame: The main window.
    • JPanel: A container for other components.
    • JButton: A clickable button.
    • JTextField: A text input field.

2. Basic Swing Components

A. Creating a Simple Window

Example:

import javax.swing.*;

public class SimpleWindow {
    public static void main(String[] args) {
        JFrame frame = new JFrame("My First GUI");  // Create a JFrame
        frame.setSize(400, 300);                   // Set dimensions
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // Exit on close

        frame.setVisible(true);  // Make the window visible
    }
}

B. Adding Components to the JFrame

Example:

import javax.swing.*;

public class AddComponents {
    public static void main(String[] args) {
        JFrame frame = new JFrame("GUI with Components");
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("Click Me!");  // Create a button
        JTextField textField = new JTextField("Enter text here", 20);  // Create a text field

        JPanel panel = new JPanel();  // Create a panel
        panel.add(button);            // Add button to the panel
        panel.add(textField);         // Add text field to the panel

        frame.add(panel);             // Add panel to the frame
        frame.setVisible(true);       // Display the GUI
    }
}

3. Event Handling

  • Event handling is how a program responds to user interactions like button clicks or typing in a text field.
  • Key Classes and Interfaces:
    • ActionListener: Listens for actions like button clicks.
    • KeyListener: Listens for key presses.

A. Handling Button Clicks with ActionListener

Example:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ButtonClickExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Button Click Example");
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("Click Me");
        JLabel label = new JLabel("Button not clicked");

        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                label.setText("Button clicked!");
            }
        });

        JPanel panel = new JPanel();
        panel.add(button);
        panel.add(label);

        frame.add(panel);
        frame.setVisible(true);
    }
}

4. Layout Managers

  • Layout managers control how components are arranged in a container.
  • Common Layout Managers:
    • FlowLayout: Arranges components in a row.
    • BorderLayout: Divides the container into regions (North, South, East, West, Center).
    • GridLayout: Arranges components in a grid.

A. FlowLayout

Example:

import javax.swing.*;
import java.awt.*;

public class FlowLayoutExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("FlowLayout Example");
        frame.setSize(400, 200);

        JPanel panel = new JPanel(new FlowLayout());
        panel.add(new JButton("Button 1"));
        panel.add(new JButton("Button 2"));
        panel.add(new JButton("Button 3"));

        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

B. BorderLayout

Example:

import javax.swing.*;
import java.awt.*;

public class BorderLayoutExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("BorderLayout Example");
        frame.setSize(400, 200);

        frame.setLayout(new BorderLayout());
        frame.add(new JButton("North"), BorderLayout.NORTH);
        frame.add(new JButton("South"), BorderLayout.SOUTH);
        frame.add(new JButton("East"), BorderLayout.EAST);
        frame.add(new JButton("West"), BorderLayout.WEST);
        frame.add(new JButton("Center"), BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

C. GridLayout

Example:

import javax.swing.*;
import java.awt.*;

public class GridLayoutExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("GridLayout Example");
        frame.setSize(400, 200);

        JPanel panel = new JPanel(new GridLayout(2, 3));  // 2 rows, 3 columns
        panel.add(new JButton("Button 1"));
        panel.add(new JButton("Button 2"));
        panel.add(new JButton("Button 3"));
        panel.add(new JButton("Button 4"));
        panel.add(new JButton("Button 5"));
        panel.add(new JButton("Button 6"));

        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

5. Building a Simple GUI Application

Example: A Basic Calculator

Code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SimpleCalculator {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Simple Calculator");
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JLabel label1 = new JLabel("Number 1:");
        JLabel label2 = new JLabel("Number 2:");
        JLabel resultLabel = new JLabel("Result: ");

        JTextField num1Field = new JTextField(10);
        JTextField num2Field = new JTextField(10);

        JButton addButton = new JButton("Add");

        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(4, 2));
        panel.add(label1);
        panel.add(num1Field);
        panel.add(label2);
        panel.add(num2Field);
        panel.add(new JLabel());  // Empty cell
        panel.add(addButton);
        panel.add(resultLabel);

        addButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    int num1 = Integer.parseInt(num1Field.getText());
                    int num2 = Integer.parseInt(num2Field.getText());
                    resultLabel.setText("Result: " + (num1 + num2));
                } catch (NumberFormatException ex) {
                    resultLabel.setText("Invalid input!");
                }
            }
        });

        frame.add(panel);
        frame.setVisible(true);
    }
}

6. Practical Exercises

Exercise 1: Login Form

Build a GUI for a login form with fields for username and password and a login button.


Exercise 2: To-Do List

Create a simple to-do list app where users can add tasks and mark them as completed.


Exercise 3: Temperature Converter

Build a GUI to convert temperatures between Celsius and Fahrenheit.


7. Summary

Key Takeaways:

  1. Swing Components:
    • Use JFrame, JPanel, JButton, and other components to build interfaces.
  2. Event Handling:
    • Use ActionListener to handle user interactions like button clicks.
  3. Layout Managers:
    • Use FlowLayout, BorderLayout, or GridLayout to arrange components.
  4. Building Applications:
    • Combine components, event handling, and layout managers to create functional GUIs.

 


Module 11: Database Connectivity

Objective: Learn how to connect Java applications to databases using JDBC, set up a database, and perform CRUD operations (Create, Read, Update, Delete) securely with prepared statements and parameterized queries.


1. Introduction to JDBC (Java Database Connectivity)

  • JDBC is a Java API that allows you to interact with databases.
  • It provides methods to connect to a database, execute SQL queries, and process results.

Key Classes in JDBC

Class/InterfaceDescription
DriverManagerManages database connections.
ConnectionRepresents a connection to the database.
StatementUsed to execute SQL queries.
PreparedStatementUsed for parameterized queries (prevents SQL injection).
ResultSetHolds the result of a query.

2. Setting Up a Database

A. Install MySQL or SQLite

  • Install a database system like MySQL or use a lightweight database like SQLite.
  • Create a sample database for testing (e.g., testdb) and a table (e.g., users).

SQL to Create a Sample Table

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(50),
    age INT
);

B. Add JDBC Driver to Your Project

  1. MySQL: Download the MySQL Connector JAR file and add it to your project’s classpath.
  2. SQLite: Download the SQLite JDBC driver JAR file.

3. Establishing a Database Connection

A. MySQL Example

Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnection {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb"; // Replace with your database name
        String user = "root"; // Replace with your username
        String password = "password"; // Replace with your password

        try (Connection connection = DriverManager.getConnection(url, user, password)) {
            System.out.println("Connected to the database successfully!");
        } catch (SQLException e) {
            System.out.println("Connection failed: " + e.getMessage());
        }
    }
}

B. SQLite Example

Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class SQLiteConnection {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:testdb.db"; // SQLite database file

        try (Connection connection = DriverManager.getConnection(url)) {
            System.out.println("Connected to the SQLite database successfully!");
        } catch (SQLException e) {
            System.out.println("Connection failed: " + e.getMessage());
        }
    }
}

4. Performing CRUD Operations

A. Insert Data (Create)

Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class InsertData {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "password";

        String query = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";

        try (Connection connection = DriverManager.getConnection(url, user, password);
             PreparedStatement statement = connection.prepareStatement(query)) {

            statement.setString(1, "Alice");
            statement.setString(2, "[email protected]");
            statement.setInt(3, 25);

            int rowsInserted = statement.executeUpdate();
            System.out.println(rowsInserted + " row(s) inserted.");
        } catch (SQLException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

B. Retrieve Data (Read)

Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class RetrieveData {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "password";

        String query = "SELECT * FROM users";

        try (Connection connection = DriverManager.getConnection(url, user, password);
             PreparedStatement statement = connection.prepareStatement(query);
             ResultSet resultSet = statement.executeQuery()) {

            while (resultSet.next()) {
                System.out.println("ID: " + resultSet.getInt("id"));
                System.out.println("Name: " + resultSet.getString("name"));
                System.out.println("Email: " + resultSet.getString("email"));
                System.out.println("Age: " + resultSet.getInt("age"));
                System.out.println("-------------------");
            }
        } catch (SQLException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

C. Update Data

Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UpdateData {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "password";

        String query = "UPDATE users SET age = ? WHERE name = ?";

        try (Connection connection = DriverManager.getConnection(url, user, password);
             PreparedStatement statement = connection.prepareStatement(query)) {

            statement.setInt(1, 30);
            statement.setString(2, "Alice");

            int rowsUpdated = statement.executeUpdate();
            System.out.println(rowsUpdated + " row(s) updated.");
        } catch (SQLException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

D. Delete Data

Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DeleteData {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "password";

        String query = "DELETE FROM users WHERE name = ?";

        try (Connection connection = DriverManager.getConnection(url, user, password);
             PreparedStatement statement = connection.prepareStatement(query)) {

            statement.setString(1, "Alice");

            int rowsDeleted = statement.executeUpdate();
            System.out.println(rowsDeleted + " row(s) deleted.");
        } catch (SQLException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

5. Prepared Statements and Parameterized Queries

  • Use Prepared Statements for:
    • Preventing SQL Injection.
    • Improved performance for repetitive queries.

Example:

String query = "SELECT * FROM users WHERE age > ?";
PreparedStatement statement = connection.prepareStatement(query);
statement.setInt(1, 20);

6. Practical Exercises

Exercise 1: User Registration

Create a program where users can register their name, email, and age in a database.


Exercise 2: Search by Email

Create a program to search for a user by their email address and display their details.


Exercise 3: CRUD Application

Build a Java console application that allows users to perform all CRUD operations on a database table.


7. Summary

Key Takeaways:

  1. JDBC Basics:
    • Use DriverManager to connect to a database.
    • Use PreparedStatement for secure and parameterized queries.
  2. CRUD Operations:
    • Perform insert, retrieve, update, and delete operations with SQL queries.
  3. Best Practices:
    • Use try-with-resources to manage connections and statements.
    • Always validate user inputs to prevent errors.

Module 12: Java Networking

Objective: Learn how to build networked applications in Java, including using sockets, writing client-server programs, handling HTTP requests and responses, and working with REST APIs.


1. Introduction to Sockets

  • A socket is an endpoint for communication between two machines.
  • Sockets use the TCP/IP protocol to establish connections and exchange data.
  • Java provides the following classes for networking:
    • Socket: Used for client-side connections.
    • ServerSocket: Used for server-side connections.

2. Writing a Simple Client-Server Program

A. Server Program

The server listens for incoming connections on a specific port and responds to the client.

Code:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleServer {
    public static void main(String[] args) {
        int port = 1234;

        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Server is listening on port " + port);

            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("New client connected");

                OutputStream output = socket.getOutputStream();
                PrintWriter writer = new PrintWriter(output, true);

                writer.println("Hello, client!");

                socket.close();
            }
        } catch (IOException e) {
            System.out.println("Server error: " + e.getMessage());
        }
    }
}

B. Client Program

The client connects to the server and reads the response.

Code:

import java.io.*;
import java.net.Socket;

public class SimpleClient {
    public static void main(String[] args) {
        String hostname = "localhost";
        int port = 1234;

        try (Socket socket = new Socket(hostname, port)) {
            InputStream input = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));

            String serverMessage = reader.readLine();
            System.out.println("Server says: " + serverMessage);

        } catch (IOException e) {
            System.out.println("Client error: " + e.getMessage());
        }
    }
}

3. HTTP Requests and Responses

To handle HTTP requests, Java provides the HttpURLConnection class in the java.net package. This class is used to send GET and POST requests.

A. Sending a GET Request

Code:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpGetExample {
    public static void main(String[] args) {
        String urlString = "https://jsonplaceholder.typicode.com/posts/1";

        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);

            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
                reader.close();
            }
        } catch (Exception e) {
            System.out.println("HTTP GET error: " + e.getMessage());
        }
    }
}

B. Sending a POST Request

Code:

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpPostExample {
    public static void main(String[] args) {
        String urlString = "https://jsonplaceholder.typicode.com/posts";
        String jsonData = "{ \"title\": \"foo\", \"body\": \"bar\", \"userId\": 1 }";

        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json; utf-8");
            connection.setRequestProperty("Accept", "application/json");
            connection.setDoOutput(true);

            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = jsonData.getBytes("utf-8");
                os.write(input, 0, input.length);
            }

            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);
        } catch (Exception e) {
            System.out.println("HTTP POST error: " + e.getMessage());
        }
    }
}

4. Working with REST APIs

REST APIs allow interaction with external services or applications over HTTP. Use the HttpClient class introduced in Java 11 for modern and efficient HTTP communication.

A. Fetching Data from a REST API

Code:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class RestApiExample {
    public static void main(String[] args) {
        String url = "https://jsonplaceholder.typicode.com/posts/1";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println("Response Code: " + response.statusCode());
            System.out.println("Response Body: " + response.body());
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

B. Sending Data to a REST API

Code:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;

public class RestApiPostExample {
    public static void main(String[] args) {
        String url = "https://jsonplaceholder.typicode.com/posts";
        String jsonData = "{ \"title\": \"foo\", \"body\": \"bar\", \"userId\": 1 }";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .POST(BodyPublishers.ofString(jsonData))
                .header("Content-Type", "application/json")
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println("Response Code: " + response.statusCode());
            System.out.println("Response Body: " + response.body());
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

5. Practical Exercises

Exercise 1: Chat Application

Build a simple chat application using sockets, where a server can handle messages from multiple clients.


Exercise 2: Fetch Weather Data

Create a Java program that fetches weather data from a public REST API and displays it.


Exercise 3: Task Manager

Create a client-server program where clients can add, view, and delete tasks stored on the server.


6. Summary

Key Takeaways:

  1. Sockets:
    • Use ServerSocket for the server and Socket for the client.
  2. HTTP Communication:
    • Use HttpURLConnection for HTTP requests and responses.
    • Use HttpClient for modern REST API communication.
  3. REST APIs:
    • Work with external services by sending GET/POST requests and parsing responses.

Module 13: Functional Programming in Java

Objective: Explore functional programming features in Java, including lambda expressions, functional interfaces, the Streams API, and method references.


1. Introduction to Functional Programming in Java

Functional programming is a programming paradigm where you build software by composing pure functions, avoiding shared state and mutable data. Java introduced functional programming features in Java 8, allowing for a more concise and declarative coding style.

Key Concepts:

  • Lambda Expressions: Anonymous functions that can be treated as objects.
  • Functional Interfaces: Interfaces with a single abstract method, which can be implemented using lambda expressions.
  • Streams API: Provides a high-level abstraction for processing sequences of elements.
  • Method References: Shortcuts for writing lambda expressions by referring to existing methods.

2. Lambda Expressions

A. What is a Lambda Expression?

A lambda expression is a concise way to represent an anonymous function (a function without a name) that can be passed around as an object.

B. Syntax

(parameters) -> expression

// Or with multiple statements:
(parameters) -> { statements; }

C. Examples

1. No Parameters

Runnable r = () -> System.out.println("Hello, Lambda!");
r.run();  // Outputs: Hello, Lambda!

2. Single Parameter

Consumer<String> printer = message -> System.out.println(message);
printer.accept("Hello, World!");  // Outputs: Hello, World!

3. Multiple Parameters

BiFunction<Integer, Integer, Integer> adder = (a, b) -> a + b;
int result = adder.apply(5, 3);  // result = 8

4. With Type Declarations

BiFunction<Integer, Integer, Integer> multiplier = (Integer a, Integer b) -> a * b;
int product = multiplier.apply(4, 6);  // product = 24

3. Functional Interfaces

A. What is a Functional Interface?

  • An interface with exactly one abstract method.
  • Used as the assignment target for lambda expressions or method references.
  • Annotated with @FunctionalInterface (optional but recommended).

B. Common Functional Interfaces

Located in java.util.function package.

1. Predicate<T>

Represents a boolean-valued function of one argument.

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

Example:

Predicate<Integer> isEven = n -> n % 2 == 0;
boolean check = isEven.test(4);  // true

2. Consumer<T>

Represents an operation that accepts a single input argument and returns no result.

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

Example:

Consumer<String> greeter = name -> System.out.println("Hello, " + name);
greeter.accept("Alice");  // Outputs: Hello, Alice

3. Supplier<T>

Represents a supplier of results; takes no arguments but returns a result.

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

Example:

Supplier<Double> randomValue = () -> Math.random();
double value = randomValue.get();  // Generates a random number

4. Function<T, R>

Represents a function that accepts one argument and produces a result.

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

Example:

Function<String, Integer> stringLength = s -> s.length();
int length = stringLength.apply("Lambda");  // length = 6

4. Streams API

A. What is the Streams API?

  • Introduced in Java 8.
  • Provides a declarative way to process sequences of elements.
  • Supports operations like filtering, mapping, and reducing.

B. Stream Operations

  1. Intermediate Operations: Return a new stream (e.g., filter(), map(), sorted()).
  2. Terminal Operations: Produce a result or side-effect (e.g., collect(), forEach(), reduce()).

C. Example Data

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");

D. Filtering

Use filter() to select elements based on a condition.

Example:

List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("A"))
    .collect(Collectors.toList());

System.out.println(filteredNames);  // Outputs: [Alice]

E. Mapping

Use map() to transform each element.

Example:

List<Integer> nameLengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());

System.out.println(nameLengths);  // Outputs: [5, 3, 7, 5, 3]

F. Reducing

Use reduce() to combine elements into a single result.

Example:

Optional<String> longestName = names.stream()
    .reduce((name1, name2) -> name1.length() > name2.length() ? name1 : name2);

longestName.ifPresent(System.out::println);  // Outputs: Charlie

G. Combining Operations

Example:

List<String> sortedNames = names.stream()
    .filter(name -> name.length() > 3)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());

System.out.println(sortedNames);  // Outputs: [ALICE, CHARLIE, DAVID]

5. Method References

A. What is a Method Reference?

  • A shorthand notation of a lambda expression to call a method.
  • Uses the :: operator.

B. Types of Method References

  1. Reference to a Static Method

    ClassName::staticMethodName
    

    Example:

    Consumer<String> printer = System.out::println;
    printer.accept("Hello, Method Reference!");  // Outputs: Hello, Method Reference!
    
  2. Reference to an Instance Method of a Particular Object

    instance::instanceMethodName
    

    Example:

    String prefix = "Hello, ";
    Function<String, String> greeter = prefix::concat;
    System.out.println(greeter.apply("World"));  // Outputs: Hello, World
    
  3. Reference to an Instance Method of an Arbitrary Object of a Particular Type

    ClassName::instanceMethodName
    

    Example:

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    names.forEach(System.out::println);
    
  4. Reference to a Constructor

    ClassName::new
    

    Example:

    Supplier<List<String>> listSupplier = ArrayList::new;
    List<String> newList = listSupplier.get();
    

6. Practical Exercises

Exercise 1: Filtering and Printing Even Numbers

Given a list of integers, use the Streams API to filter out the even numbers and print them.

Solution:

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

numbers.stream()
    .filter(n -> n % 2 == 0)
    .forEach(System.out::println);  // Outputs: 2, 4, 6

Exercise 2: Mapping to Uppercase

Given a list of strings, convert all strings to uppercase using the Streams API.

Solution:

List<String> words = Arrays.asList("java", "lambda", "stream");

List<String> uppercaseWords = words.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println(uppercaseWords);  // Outputs: [JAVA, LAMBDA, STREAM]

Exercise 3: Calculating the Sum of Squares

Given a list of integers, calculate the sum of their squares using map() and reduce().

Solution:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

int sumOfSquares = numbers.stream()
    .map(n -> n * n)
    .reduce(0, Integer::sum);

System.out.println("Sum of squares: " + sumOfSquares);  // Outputs: Sum of squares: 30

Exercise 4: Using Custom Functional Interface

Create a custom functional interface TriFunction that takes three arguments and returns a result. Use a lambda expression to implement it.

Solution:

@FunctionalInterface
interface TriFunction<A, B, C, R> {
    R apply(A a, B b, C c);
}

// Usage
TriFunction<Integer, Integer, Integer, Integer> addThreeNumbers = (a, b, c) -> a + b + c;

int result = addThreeNumbers.apply(10, 20, 30);  // result = 60
System.out.println("Result: " + result);

7. Summary

Key Takeaways:

  1. Lambda Expressions:

    • Provide a concise way to implement functional interfaces.
    • Syntax: (parameters) -> expression or (parameters) -> { statements; }.
  2. Functional Interfaces:

    • Interfaces with a single abstract method.
    • Common ones include Predicate, Consumer, Supplier, Function.
  3. Streams API:

    • Offers a powerful way to process collections.
    • Supports operations like filter(), map(), reduce().
  4. Method References:

    • Shorthand for lambda expressions that call existing methods.
    • Types include references to static methods, instance methods, and constructors.

Module 14: Advanced Topics

Objective: Explore advanced Java concepts, including generics, annotations, the reflection API, and an introduction to JVM internals.


1. Generics

Generics allow you to write type-safe code by enabling classes, interfaces, and methods to operate on types specified by the programmer.

A. Generic Classes

A generic class allows you to specify a data type when the class is instantiated.

Syntax:

class GenericClass<T> {
    private T data;

    public GenericClass(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }
}

Example:

public class GenericsExample {
    public static void main(String[] args) {
        GenericClass<String> stringBox = new GenericClass<>("Hello, Generics");
        System.out.println(stringBox.getData());  // Outputs: Hello, Generics

        GenericClass<Integer> intBox = new GenericClass<>(123);
        System.out.println(intBox.getData());  // Outputs: 123
    }
}

B. Generic Methods

A generic method allows you to define a method with a type parameter.

Syntax:

public static <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

Example:

public class GenericMethodExample {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        String[] words = {"Java", "Generics", "Example"};
        Integer[] numbers = {1, 2, 3, 4};

        printArray(words);
        printArray(numbers);
    }
}

C. Wildcards

  • ?: Represents an unknown type.
  • Use cases:
    • Unbounded Wildcards (?): Accepts any type.
    • Bounded Wildcards (? extends Type): Accepts a specific type or its subtypes.
    • Lower Bounded Wildcards (? super Type): Accepts a specific type or its supertypes.

Example:

public class WildcardExample {
    public static void printList(List<?> list) {
        for (Object element : list) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        printList(numbers);
    }
}

2. Annotations and Metadata

Annotations provide metadata about your code to the compiler or runtime.

A. Built-in Annotations

AnnotationDescription
@OverrideEnsures a method is overriding a superclass method.
@DeprecatedMarks a method or class as deprecated.
@SuppressWarningsSuppresses compiler warnings.
@FunctionalInterfaceMarks an interface as a functional interface (Java 8+).

Example:

class Parent {
    @Deprecated
    public void oldMethod() {
        System.out.println("This is a deprecated method.");
    }
}

public class AnnotationsExample {
    @Override
    public String toString() {
        return "Annotations Example";
    }

    public static void main(String[] args) {
        Parent parent = new Parent();
        parent.oldMethod();  // Warning: This method is deprecated
    }
}

B. Custom Annotations

Syntax:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface CustomAnnotation {
    String value();
}

Usage:

public class CustomAnnotationExample {
    @CustomAnnotation("This is a custom annotation")
    public void myMethod() {
        System.out.println("Method with custom annotation.");
    }

    public static void main(String[] args) throws Exception {
        CustomAnnotationExample example = new CustomAnnotationExample();
        example.myMethod();
    }
}

3. Reflection API

Reflection allows you to inspect or modify classes, methods, and fields at runtime.

A. Common Use Cases

  • Inspecting class information (methods, fields, constructors).
  • Invoking methods dynamically.
  • Modifying private fields.

Example:

import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("java.lang.String");

            System.out.println("Methods in String class:");
            for (Method method : clazz.getDeclaredMethods()) {
                System.out.println(method.getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

B. Accessing and Modifying Private Fields

Example:

import java.lang.reflect.Field;

class Person {
    private String name = "John";
}

public class ModifyPrivateField {
    public static void main(String[] args) throws Exception {
        Person person = new Person();

        Field field = person.getClass().getDeclaredField("name");
        field.setAccessible(true);

        System.out.println("Before: " + field.get(person));

        field.set(person, "Alice");
        System.out.println("After: " + field.get(person));
    }
}

4. Introduction to JVM Internals

The Java Virtual Machine (JVM) is the engine that runs Java bytecode. Understanding its internals helps optimize code.

A. JVM Memory Model

  1. Heap: Stores objects and class instances.
  2. Stack: Stores method calls and local variables.
  3. Method Area: Stores class metadata and static variables.
  4. Program Counter (PC): Keeps track of the current instruction.
  5. Native Method Stack: Used for native method execution.

B. Garbage Collection

  • What is Garbage Collection?

    • Automatic memory management in Java.
    • Removes unused objects from memory.
  • Garbage Collectors:

    • Serial GC.
    • Parallel GC.
    • G1 GC (default since Java 9).

Example: Requesting Garbage Collection:

public class GarbageCollectionExample {
    public static void main(String[] args) {
        GarbageCollectionExample obj = new GarbageCollectionExample();
        obj = null;  // Make the object eligible for GC
        System.gc(); // Request garbage collection
    }

    @Override
    protected void finalize() {
        System.out.println("Garbage Collector called!");
    }
}

5. Practical Exercises

Exercise 1: Generic Pair Class

Write a generic class Pair that holds two values of the same type.

Solution:

class Pair<T> {
    private T first;
    private T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public T getSecond() {
        return second;
    }
}

Exercise 2: Using Reflection

Create a program that dynamically invokes a method from a class using reflection.


Exercise 3: Custom Annotation

Create a custom annotation to log the execution time of a method.


6. Summary

Key Takeaways:

  1. Generics:

    • Provide type safety and reusability for classes and methods.
    • Use wildcards for flexible type constraints.
  2. Annotations:

    • Provide metadata for classes, methods, or fields.
    • Can define and use custom annotations for specific use cases.
  3. Reflection:

    • Enables runtime inspection and modification of classes, methods, and fields.
  4. JVM Internals:

    • Understanding the memory model helps in optimizing applications.
    • Garbage collection automates memory management.

Module 15: Building a Real-World Project

Objective: Apply your knowledge of Java to build a complete, functional application. This module focuses on modular coding, error handling, and best practices for professional Java development.


1. Choosing a Real-World Project

Here are three example projects to consider. Choose one based on your interests or requirements:

A. Inventory Management System

  • Tracks products, stock levels, and suppliers.
  • Features include adding, updating, and viewing inventory.

B. Chat Application (Client-Server)

  • A real-time communication app with a server and multiple clients.
  • Implements networking concepts using sockets.

C. Library Management System

  • Manages books, borrowers, and due dates.
  • Includes features like issuing books, returning books, and generating reports.

2. Key Concepts to Focus On

  • Modular Coding:

    • Use packages to organize your code into logical modules.
    • Separate concerns (e.g., database layer, service layer, UI layer).
  • Error Handling:

    • Implement robust error-handling mechanisms using try-catch-finally.
    • Use custom exceptions for specific error scenarios.
  • Best Practices:

    • Use meaningful variable and method names.
    • Write modular and reusable code.
    • Follow design patterns like MVC (Model-View-Controller) where applicable.

3. Example: Inventory Management System

A. Overview of the System

  • Features:

    1. Add new products.
    2. View product details.
    3. Update stock levels.
    4. Search for products by name or ID.
    5. Save and load inventory data to/from a file or database.
  • Modules:

    • Product: Represents individual products.
    • Inventory: Manages the collection of products.
    • Database: Handles data storage and retrieval.
    • UI: Provides a user interface for interaction.

B. System Design

Class Diagram

  1. Product Class:

    • Fields: id, name, price, quantity.
    • Methods: getters, setters, toString().
  2. Inventory Class:

    • Fields: List<Product>.
    • Methods: addProduct(), updateStock(), viewProduct(), searchProduct().
  3. Main Class:

    • Contains the main() method to run the application.

C. Implementation

1. Product Class

public class Product {
    private int id;
    private String name;
    private double price;
    private int quantity;

    public Product(int id, String name, double price, int quantity) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.quantity = quantity;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    @Override
    public String toString() {
        return "Product [ID=" + id + ", Name=" + name + ", Price=" + price + ", Quantity=" + quantity + "]";
    }
}

2. Inventory Class

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

public class Inventory {
    private List<Product> products;

    public Inventory() {
        products = new ArrayList<>();
    }

    public void addProduct(Product product) {
        products.add(product);
    }

    public void updateStock(int id, int newQuantity) {
        for (Product product : products) {
            if (product.getId() == id) {
                product.setQuantity(newQuantity);
                return;
            }
        }
        System.out.println("Product not found!");
    }

    public void viewProducts() {
        for (Product product : products) {
            System.out.println(product);
        }
    }

    public Product searchProduct(int id) {
        for (Product product : products) {
            if (product.getId() == id) {
                return product;
            }
        }
        return null;
    }
}

3. Main Class

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Inventory inventory = new Inventory();
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.println("\nInventory Management System");
            System.out.println("1. Add Product");
            System.out.println("2. Update Stock");
            System.out.println("3. View Products");
            System.out.println("4. Search Product");
            System.out.println("5. Exit");
            System.out.print("Choose an option: ");
            int choice = scanner.nextInt();

            switch (choice) {
                case 1:
                    System.out.print("Enter Product ID: ");
                    int id = scanner.nextInt();
                    System.out.print("Enter Product Name: ");
                    String name = scanner.next();
                    System.out.print("Enter Product Price: ");
                    double price = scanner.nextDouble();
                    System.out.print("Enter Product Quantity: ");
                    int quantity = scanner.nextInt();

                    Product product = new Product(id, name, price, quantity);
                    inventory.addProduct(product);
                    System.out.println("Product added successfully!");
                    break;

                case 2:
                    System.out.print("Enter Product ID to update: ");
                    int updateId = scanner.nextInt();
                    System.out.print("Enter new quantity: ");
                    int newQuantity = scanner.nextInt();
                    inventory.updateStock(updateId, newQuantity);
                    break;

                case 3:
                    inventory.viewProducts();
                    break;

                case 4:
                    System.out.print("Enter Product ID to search: ");
                    int searchId = scanner.nextInt();
                    Product foundProduct = inventory.searchProduct(searchId);
                    if (foundProduct != null) {
                        System.out.println(foundProduct);
                    } else {
                        System.out.println("Product not found!");
                    }
                    break;

                case 5:
                    System.out.println("Exiting...");
                    scanner.close();
                    return;

                default:
                    System.out.println("Invalid choice! Please try again.");
            }
        }
    }
}

4. Best Practices for Real-World Projects

  1. Follow Clean Code Principles:

    • Write readable, maintainable, and modular code.
  2. Handle Errors Gracefully:

    • Use proper error handling with try-catch blocks.
    • Validate user inputs to avoid runtime exceptions.
  3. Use Design Patterns:

    • Apply design patterns like MVC (Model-View-Controller) for larger projects.
  4. Document Code:

    • Use comments and Javadoc to explain complex parts of the code.
  5. Test Your Application:

    • Write unit tests to ensure functionality.
    • Use tools like JUnit for testing.

5. Practical Exercises

Exercise 1: Extend the Inventory System

  • Add features like:
    • Sorting products by name or price.
    • Exporting inventory data to a CSV file.

Exercise 2: Build a Library Management System

  • Track books, members, and loan records.
  • Add features like:
    • Issuing and returning books.
    • Generating overdue reports.

Exercise 3: Chat Application

  • Implement a chat system where multiple clients can connect to a server and exchange messages in real-time.

6. Summary

Key Takeaways:

  1. Building real-world applications involves combining multiple Java concepts into a cohesive system.
  2. Modularize your code by separating concerns into different classes and packages.
  3. Implement robust error handling and follow best coding practices.
  4. Focus on building reusable and maintainable code structures.

 



Resource Materials

 

  • Oracle’s Java Tutorials on Lambda Expressions: Link
  • Java Streams API Documentation: Link
  • Functional Interfaces in Java: Link