Complete Java Tutorial with Usage Examples

Table of Contents

1. Introduction to Java

Java is a high-level, class-based, object-oriented programming language that is designed to have as few implementation dependencies as possible. It is a general-purpose programming language intended to let application developers write once, run anywhere (WORA), meaning that compiled Java code can run on all platforms that support Java without the need for recompilation.

Java is one of the most popular programming languages, widely used for:

Key Features of Java:

Tip for Practice: To compile and run Java code, you'll need a Java Development Kit (JDK). Use an IDE like IntelliJ IDEA, Eclipse, or VS Code for a better development experience.

2. Java Development Environment (JDK)

To write, compile, and run Java programs, you need a **Java Development Kit (JDK)**. The JDK includes the Java Runtime Environment (JRE), a compiler (javac), and other development tools.

A. Install JDK:

You can download JDK from Oracle or OpenJDK distributions like Adoptium (Temurin).

  1. Go to adoptium.net/temurin/releases/ (recommended for OpenJDK).
  2. Download the LTS (Long Term Support) version (e.g., JDK 17 or JDK 21) for your operating system.
  3. Follow the installation wizard.

B. Set up Environment Variables:

After installation, you might need to set the `JAVA_HOME` environment variable and add Java's `bin` directory to your system's `PATH` variable.

C. Verify Installation:

Open your terminal/command prompt and run:

java -version
javac -version

You should see the installed JDK versions.

3. Hello World Program

The classic first program.

A. Create the File:

Create a file named `HelloWorld.java` using any text editor.

// HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!"); // Prints "Hello, World!" to the console
    }
}

B. Compile the Program:

Open your terminal/command prompt, navigate to the directory where you saved `HelloWorld.java`, and run the Java compiler:

javac HelloWorld.java

If compilation is successful, a `HelloWorld.class` file will be created in the same directory. This is the bytecode, which the JVM understands.

C. Run the Program:

java HelloWorld

Expected Output:

Hello, World!

4. Java Basics (Syntax, Comments)

A. Basic Syntax Rules:

B. Comments:

Used to explain code and make it more readable. Ignored by the compiler.

5. Data Types & Variables

Variables are containers for storing data values. Java is a strongly-typed language, meaning you must declare the type of a variable before using it.

A. Primitive Data Types:

Store simple values directly in memory.

int age = 30;
double price = 19.99;
boolean isActive = true;
char initial = 'J';
long population = 8000000000L; // 'L' suffix for long literal
float temperature = 98.6f; // 'f' suffix for float literal

B. Non-Primitive Data Types (Reference Types):

Store references (memory addresses) to objects. Includes `String`, Arrays, Classes, Interfaces.

String name = "John Doe";
int[] numbers = {1, 2, 3}; // Array of integers
Date today = new Date(); // Object of Date class

C. Variable Declaration & Initialization:

int quantity; // Declaration
quantity = 100; // Initialization

String city = "New York"; // Declaration and Initialization

final double PI = 3.14159; // 'final' keyword makes a variable a constant (cannot be changed)
// PI = 3.14; // Error: Cannot assign a value to a final variable

D. Type Casting:

Converting one data type to another.

6. Operators

Symbols that perform operations on variables and values.

7. Control Flow (Conditionals & Loops)

Control flow statements dictate the order in which instructions are executed.

A. Conditionals:

B. Loops:

8. Methods (Functions)

Methods are blocks of code that perform a specific task. They are defined within classes.

9. Object-Oriented Programming (OOP)

Java is built on OOP principles: Encapsulation, Inheritance, Polymorphism, Abstraction.

A. Classes and Objects:

B. Encapsulation:

Bundling data (fields) and methods that operate on the data within a single unit (class), and restricting direct access to some of the component's internals (using access modifiers).

10. Arrays

Arrays are fixed-size, ordered collections of elements of the same data type.

11. Strings

In Java, strings are objects (not primitive types) representing sequences of characters. They are immutable (cannot be changed after creation).

12. Inheritance & Polymorphism

A. Inheritance:

Allows a class (subclass/child) to inherit properties and methods from another class (superclass/parent).

class Animal { // Superclass
    String name;

    public Animal(String name) {
        this.name = name;
    }

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

class Dog extends Animal { // Subclass
    public Dog(String name) {
        super(name); // Call parent class constructor
    }

    @Override // Annotation indicating method overrides a superclass method
    public void makeSound() {
        System.out.println(name + " barks!");
    }

    public void fetch() {
        System.out.println(name + " is fetching.");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        Animal myAnimal = new Animal("Generic");
        Dog myDog = new Dog("Buddy");

        myAnimal.makeSound(); // Output: Animal makes a sound
        myDog.makeSound();    // Output: Buddy barks! (Overridden method)
        myDog.fetch();        // Output: Buddy is fetching.
    }
}

B. Polymorphism:

Means "many forms." It allows objects of different classes to be treated as objects of a common superclass. The actual method called depends on the object's runtime type.

class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void makeSound() {
        System.out.println(name + " meows!");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        Animal myAnimal1 = new Dog("Lucy"); // Animal reference, Dog object
        Animal myAnimal2 = new Cat("Whiskers"); // Animal reference, Cat object
        Animal myAnimal3 = new Animal("Leo");

        myAnimal1.makeSound(); // Output: Lucy barks! (Runtime polymorphism)
        myAnimal2.makeSound(); // Output: Whiskers meows!
        myAnimal3.makeSound(); // Output: Leo makes a sound
    }
}

13. Abstraction & Interfaces

A. Abstraction:

Hiding the complex implementation details and showing only the essential features of an object. Achieved using abstract classes and interfaces.

abstract class Shape { // Abstract class
    String color;

    public Shape(String color) {
        this.color = color;
    }

    // Abstract method (no body)
    public abstract double getArea();

    // Concrete method
    public String getColor() {
        return color;
    }
}

class Circle extends Shape {
    double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double getArea() { // Implementation for abstract method
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    double length;
    double width;

    public Rectangle(String color, double length, double width) {
        super(color);
        this.length = length;
        this.width = width;
    }

    @Override
    public double getArea() { // Implementation for abstract method
        return length * width;
    }
}

public class AbstractionDemo {
    public static void main(String[] args) {
        Shape circle = new Circle("Red", 5.0);
        Shape rectangle = new Rectangle("Blue", 4.0, 6.0);

        System.out.println("Circle Area: " + circle.getArea());       // Output: Circle Area: 78.53...
        System.out.println("Rectangle Area: " + rectangle.getArea()); // Output: Rectangle Area: 24.0
        System.out.println("Circle Color: " + circle.getColor());     // Output: Circle Color: Red
    }
}

B. Interfaces:

A blueprint of a class. It defines a contract specifying methods that implementing classes must provide. A class can implement multiple interfaces.

interface Playable { // Interface
    void play(); // Abstract method (implicitly public abstract)
    void pause();
    default void stop() { // Default method (Java 8+)
        System.out.println("Stopped playback.");
    }
}

class MusicPlayer implements Playable {
    @Override
    public void play() {
        System.out.println("Playing music...");
    }

    @Override
    public void pause() {
        System.out.println("Music paused.");
    }
}

class VideoPlayer implements Playable {
    @Override
    public void play() {
        System.out.println("Playing video...");
    }

    @Override
    public void pause() {
        System.out.println("Video paused.");
    }
}

public class InterfaceDemo {
    public static void main(String[] args) {
        Playable music = new MusicPlayer();
        Playable video = new VideoPlayer();

        music.play();  // Output: Playing music...
        music.pause(); // Output: Music paused.
        music.stop();  // Output: Stopped playback.

        video.play();  // Output: Playing video...
        video.stop();  // Output: Stopped playback.
    }
}

14. Exception Handling

A mechanism to handle runtime errors (exceptions) that might occur during program execution, preventing the program from crashing.

public class ExceptionHandlingDemo {
    public static void main(String[] args) {
        // Example 1: ArithmeticException
        try {
            int result = 10 / 0; // This will throw an ArithmeticException
            System.out.println("Result: " + result); // This line will not execute
        } catch (ArithmeticException e) {
            System.err.println("Error: Cannot divide by zero. " + e.getMessage());
        } finally {
            System.out.println("Arithmetic operation attempted.");
        }

        // Example 2: NullPointerException
        String str = null;
        try {
            System.out.println(str.length()); // This will throw a NullPointerException
        } catch (NullPointerException e) {
            System.err.println("Error: String is null. " + e.getMessage());
        }

        // Example 3: Custom Exception (throws keyword)
        try {
            validateAge(5); // This will throw an IllegalArgumentException
        } catch (IllegalArgumentException e) {
            System.err.println("Validation Error: " + e.getMessage());
        }
    }

    // Method that declares it might throw an IllegalArgumentException
    public static void validateAge(int age) throws IllegalArgumentException {
        if (age < 18) {
            throw new IllegalArgumentException("Age must be 18 or older.");
        }
        System.out.println("Age is valid.");
    }
}

Checked vs. Unchecked Exceptions:

15. Collections Framework

A set of interfaces and classes that provide a unified architecture for representing and manipulating collections of objects.

Common Collection Types:

16. File I/O (Input/Output)

Reading from and writing to files.

Example: Writing to a file:

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

public class FileWriteDemo {
    public static void main(String[] args) {
        String filename = "output.txt";
        try (PrintWriter writer = new PrintWriter(new FileWriter(filename))) {
            writer.println("This is the first line.");
            writer.println("This is the second line.");
            System.out.println("Data written to " + filename);
        } catch (IOException e) {
            System.err.println("Error writing to file: " + e.getMessage());
        }
    }
}

Example: Reading from a file:

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

public class FileReadDemo {
    public static void main(String[] args) {
        String filename = "output.txt"; // Assuming output.txt exists from previous example
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            String line;
            System.out.println("Reading from " + filename + ":");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("Error reading from file: " + e.getMessage());
        }
    }
}

`try-with-resources` (Java 7+): The `try (resource-declaration)` statement automatically closes resources (like `writer` or `reader`) when the `try` block exits, even if an exception occurs. This is the recommended way.

17. Multithreading

Java supports multithreading, allowing multiple parts of a program to execute concurrently, improving performance for certain tasks.

18. Generics

Generics enable you to write classes, interfaces, and methods that operate on types that are specified as parameters at creation time. This provides type safety and avoids type casting.

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

public class GenericsDemo {
    public static void main(String[] args) {
        // Using Generics with List (type-safe)
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        stringList.add("World");
        // stringList.add(123); // Compile-time error: cannot add an Integer to a List<String>

        String s = stringList.get(0); // No casting needed

        // Creating a custom generic class
        Box<Integer> integerBox = new Box<>();
        integerBox.set(123);
        System.out.println("Integer in box: " + integerBox.get());

        Box<String> stringBox = new Box<>();
        stringBox.set("Generic String");
        System.out.println("String in box: " + stringBox.get());
    }
}

// Custom Generic Class
class Box<T> { // 'T' is a type parameter
    private T t; // 't' is of type T

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }
}

19. Lambda Expressions & Streams API (Java 8+)

Introduced in Java 8, these features simplify writing functional-style code.

A. Lambda Expressions:

Provide a concise way to represent an anonymous function (a function without a name).

interface MyFunctionalInterface {
    void performAction(String s);
}

public class LambdaDemo {
    public static void main(String[] args) {
        // Using an anonymous inner class (pre-Java 8)
        MyFunctionalInterface oldWay = new MyFunctionalInterface() {
            @Override
            public void performAction(String s) {
                System.out.println("Old way: " + s);
            }
        };
        oldWay.performAction("Hello");

        // Using a Lambda expression (Java 8+)
        MyFunctionalInterface lambdaWay = (s) -> System.out.println("Lambda way: " + s);
        lambdaWay.performAction("World");

        // More complex lambda for a Comparator (used for sorting)
        List<Integer> numbers = Arrays.asList(5, 2, 8, 1);
        numbers.sort((a, b) -> a.compareTo(b)); // Sorts ascending
        System.out.println("Sorted: " + numbers); // Output: Sorted: [1, 2, 5, 8]
    }
}

B. Streams API:

A sequence of elements from a source that supports aggregate operations (map, filter, reduce, collect) that can be executed sequentially or in parallel.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamAPIDemo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Anna", "Charlie", "Andrew");

        // Filter names starting with 'A' and convert to uppercase
        List<String> filteredAndUppercaseNames = names.stream() // Create a stream
            .filter(name -> name.startsWith("A"))             // Filter elements
            .map(String::toUpperCase)                         // Transform elements (method reference)
            .collect(Collectors.toList());                    // Collect results into a List

        System.out.println(filteredAndUppercaseNames); // Output: [ALICE, ANNA, ANDREW]

        // Count names with more than 4 letters
        long count = names.stream()
            .filter(name -> name.length() > 4)
            .count();
        System.out.println("Names longer than 4 letters: " + count); // Output: 3

        // Find the first name starting with 'B'
        names.stream()
            .filter(name -> name.startsWith("B"))
            .findFirst()
            .ifPresent(name -> System.out.println("First 'B' name: " + name)); // Output: First 'B' name: Bob
    }
}

20. Java Modules (Java 9+)

Introduced in Java 9, the Java Platform Module System (JPMS), or Project Jigsaw, aims to make the Java platform more scalable, secure, and easier to maintain. It breaks down the monolithic JDK into a set of modules.

Example: Simple Modular Application

Project Structure:

myproject/
├── com.example.app/
│   ├── module-info.java
│   └── com/example/app/
│       └── Main.java
└── com.example.utils/
    ├── module-info.java
    └── com/example/utils/
        └── StringUtils.java
// com.example.utils/module-info.java
module com.example.utils {
    exports com.example.utils; // Export the package
}
// com.example.utils/com/example/utils/StringUtils.java
package com.example.utils;

public class StringUtils {
    public static String capitalize(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        return text.substring(0, 1).toUpperCase() + text.substring(1).toLowerCase();
    }
}
// com.example.app/module-info.java
module com.example.app {
    requires com.example.utils; // Declare dependency on utils module
}
// com.example.app/com/example/app/Main.java
package com.example.app;

import com.example.utils.StringUtils; // Import class from required module

public class Main {
    public static void main(String[] args) {
        String name = "java";
        String capitalizedName = StringUtils.capitalize(name);
        System.out.println("Capitalized name: " + capitalizedName); // Output: Capitalized name: Java
    }
}

Compile and Run (from `myproject` directory):

# Compile utils module
javac -d out/com.example.utils com.example.utils/module-info.java com.example.utils/com/example/utils/StringUtils.java

# Compile app module (requires utils module)
javac -d out/com.example.app --module-path out/com.example.utils com.example.app/module-info.java com.example.app/com/example/app/Main.java

# Run the application
java --module-path out/com.example.utils:out/com.example.app -m com.example.app/com.example.app.Main

21. Packaging & Build Tools

For real-world Java projects, build automation tools are indispensable.

22. Best Practices

The Vast World of Java!

This tutorial covers the fundamentals of Java programming. Java's ecosystem is vast, with numerous frameworks (Spring, Hibernate), testing tools, and deployment options. Mastering these core concepts will provide a strong foundation for building robust, scalable, and high-performance applications across various domains, from enterprise systems to Android mobile apps. The journey to becoming a proficient Java developer involves continuous learning and hands-on practice!