Java is an object-oriented programming language developed in 1995 at Sun Microsystems by a team led by James Gosling. Under the slogan “Write Once, Run Anywhere,” it provides a platform-independent execution environment. As of 2024, Java consistently ranks among the top programming languages in the TIOBE index, making it one of the most widely used programming languages in the world. It serves as a core language in various fields including enterprise applications, Android apps, big data processing, and web services. Java continues to evolve based on its strong type system, rich standard library, and active community.

Java Overview

What is Java?

Java is a general-purpose object-oriented programming language developed by Sun Microsystems (now Oracle). It runs on the JVM (Java Virtual Machine) to provide platform independence and features strong type checking, automatic memory management (garbage collection), and multithreading support.

Birth and History of Java

Java’s history began in 1991 with Sun Microsystems’ “Green Project.” James Gosling, Mike Sheridan, and Patrick Naughton started a project to develop a programming language for consumer electronics, which became the origin of Java.

YearVersion/EventKey Features
1991Green Project startsJames Gosling begins developing Oak language
1995Java 1.0 released“Write Once, Run Anywhere” slogan, web applet support
1997Java 1.1Inner Class, JDBC, JavaBeans introduced
1998Java 1.2 (J2SE)Collections Framework, Swing GUI
2004Java 5.0 (1.5)Generics, Annotations, Enum, Autoboxing
2006Java 6Performance improvements, Scripting API
2010Oracle acquisitionOracle acquires Sun Microsystems
2014Java 8 (LTS)Lambda, Stream API, Optional, Date/Time API
2017Java 9Module System (Jigsaw), JShell
2018Java 11 (LTS)var keyword, HTTP Client API
2021Java 17 (LTS)Sealed Classes, Pattern Matching
2023Java 21 (LTS)Virtual Threads, Pattern Matching extensions

Java vs Other Languages

CharacteristicJavaC++PythonC#
ParadigmObject-orientedMulti-paradigmMulti-paradigmObject-oriented
Memory ManagementAutomatic (GC)ManualAutomatic (GC)Automatic (GC)
PlatformCross-platform (JVM)NativeCross-platformPrimarily Windows
Type SystemStaticStaticDynamicStatic
ExecutionCompile+InterpretCompileInterpretCompile
PerformanceMedium-HighHighestLowMedium-High
Learning CurveMediumHighLowMedium

JVM and Java Execution Environment

What is JVM?

JVM (Java Virtual Machine) is a virtual machine that executes Java bytecode. It provides an execution environment independent of the operating system and hardware, realizing Java’s “Write Once, Run Anywhere” philosophy.

Java Execution Process

The execution process of a Java program goes through several stages from writing source code to execution on the JVM. The compiler and JVM play key roles in this process.

Source Code (.java)
       ↓ javac (compiler)
Bytecode (.class)
       ↓ Class Loader
Load to JVM Memory
       ↓ JIT Compiler/Interpreter
Native Code Execution

JDK, JRE, JVM Relationship

ComponentDescriptionIncluded Elements
JVMExecutes Java bytecodeExecution Engine, Memory Management
JREJava Runtime EnvironmentJVM + Standard Libraries
JDKJava Development KitJRE + Compiler (javac) + Development Tools

Java Compilation and Execution

To run a Java program, you must first compile the source code to generate bytecode, then execute it on the JVM.

// HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
# Compile: .java → .class (bytecode)
javac HelloWorld.java

# Execute: JVM runs bytecode
java HelloWorld

Four Pillars of Object-Oriented Programming

Java is designed based on four core principles of object-oriented programming (OOP): encapsulation, inheritance, polymorphism, and abstraction. Understanding and properly utilizing these principles enables writing maintainable and extensible code.

Encapsulation

What is Encapsulation?

Encapsulation bundles data (fields) and methods that process that data into a single unit (class), restricting direct external access to protect data integrity. It is a core principle of object-orientation.

public class BankAccount {
    // private fields block external access
    private double balance;
    private String accountNumber;

    // Constructor
    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    // getter allows reading
    public double getBalance() {
        return balance;
    }

    // Methods with validation logic change data
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public boolean withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
}

Inheritance

What is Inheritance?

Inheritance is a mechanism where a new class (child class) inherits fields and methods from an existing class (parent class) to reuse and extend code. Java uses the extends keyword and supports only single inheritance.

// Parent class
public class Vehicle {
    protected String brand;
    protected int year;

    public Vehicle(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    public void start() {
        System.out.println("Vehicle is starting...");
    }

    public void stop() {
        System.out.println("Vehicle is stopping...");
    }
}

// Child class
public class Car extends Vehicle {
    private int numberOfDoors;

    public Car(String brand, int year, int numberOfDoors) {
        super(brand, year);  // Call parent constructor
        this.numberOfDoors = numberOfDoors;
    }

    // Method overriding
    @Override
    public void start() {
        System.out.println("Car engine is starting...");
    }

    // Child class specific method
    public void honk() {
        System.out.println("Beep beep!");
    }
}

Polymorphism

What is Polymorphism?

Polymorphism is the ability to handle multiple implementations through a single interface or parent class type. It is implemented through method overriding (runtime polymorphism) and method overloading (compile-time polymorphism).

// Polymorphism example
public class PolymorphismExample {
    public static void main(String[] args) {
        // Reference child object with parent type (upcasting)
        Vehicle myVehicle = new Car("Toyota", 2023, 4);
        myVehicle.start();  // Outputs "Car engine is starting..."

        // Method overloading example
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 3));       // int version
        System.out.println(calc.add(5.0, 3.0));   // double version
        System.out.println(calc.add(1, 2, 3));    // three argument version
    }
}

class Calculator {
    // Method overloading: same name, different parameters
    public int add(int a, int b) {
        return a + b;
    }

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

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

Abstraction

What is Abstraction?

Abstraction extracts core concepts or functions from complex systems and expresses them as simple interfaces. In Java, it is implemented through abstract classes and interfaces.

// Abstract class
public abstract class Animal {
    protected String name;

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

    // Abstract method: no implementation, must be implemented in child class
    public abstract void makeSound();

    // Concrete method: has implementation
    public void sleep() {
        System.out.println(name + " is sleeping...");
    }
}

// Implementation classes
public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

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

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

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

Interfaces and Abstract Classes

Interfaces

An interface is a contract that defines method signatures that a class must implement. Since Java 8, interfaces can also include default and static methods. Multiple implementation is possible, compensating for the limitations of multiple inheritance.

// Interface definition
public interface Flyable {
    // Abstract method (public abstract omitted)
    void fly();

    // Java 8+ default method
    default void land() {
        System.out.println("Landing...");
    }

    // Java 8+ static method
    static void checkWeather() {
        System.out.println("Checking weather conditions...");
    }
}

public interface Swimmable {
    void swim();
}

// Multiple interface implementation
public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("Duck is flying!");
    }

    @Override
    public void swim() {
        System.out.println("Duck is swimming!");
    }
}

Abstract Class vs Interface

CharacteristicAbstract ClassInterface
Keywordabstract classinterface
Inheritance/Implementationextends (single)implements (multiple)
ConstructorPossibleNot possible
FieldsAll access modifierspublic static final only
MethodsAll typespublic (Java 8+: default, static)
Use CaseIS-A relationship, common functionalityCAN-DO relationship, contract definition

Generics

What are Generics?

Generics is a feature that specifies data types to be used in classes or methods at compile time, ensuring type safety and reducing the hassle of type casting. It was introduced in Java 5.

// Generic class
public class Box<T> {
    private T content;

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

    public T get() {
        return content;
    }
}

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

    // Bounded type parameter
    public static <T extends Number> double sum(T[] numbers) {
        double total = 0.0;
        for (T number : numbers) {
            total += number.doubleValue();
        }
        return total;
    }
}

// Usage example
public class GenericExample {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello");
        String str = stringBox.get();  // No casting needed

        Box<Integer> intBox = new Box<>();
        intBox.set(123);
        Integer num = intBox.get();

        Integer[] numbers = {1, 2, 3, 4, 5};
        System.out.println(GenericUtils.sum(numbers));
    }
}

Wildcards

// Unbounded wildcard
public void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

// Upper bounded wildcard (for reading)
public double sumOfList(List<? extends Number> list) {
    double sum = 0.0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}

// Lower bounded wildcard (for writing)
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

Collection Framework

What is Collection Framework?

Collection Framework is a set of standardized interfaces and classes for storing and manipulating data. It provides interfaces like List, Set, Map, Queue and implementations like ArrayList, HashSet, HashMap.

Collection Interface Hierarchy

Iterable
    └── Collection
            ├── List: ordered, allows duplicates
            │       ├── ArrayList
            │       ├── LinkedList
            │       └── Vector
            ├── Set: unordered, no duplicates
            │       ├── HashSet
            │       ├── LinkedHashSet
            │       └── TreeSet
            └── Queue: FIFO
                    ├── LinkedList
                    └── PriorityQueue

Map: key-value pairs
    ├── HashMap
    ├── LinkedHashMap
    ├── TreeMap
    └── Hashtable

List

List is an interface for storing ordered data. It allows index-based access and duplicate elements. Implementations include ArrayList, LinkedList, and Vector.

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

public class ListExample {
    public static void main(String[] args) {
        // ArrayList: dynamic array, fast random access
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Java");
        arrayList.add("Python");
        arrayList.add("JavaScript");

        // Access by index
        System.out.println(arrayList.get(0));  // "Java"

        // Iteration
        for (String lang : arrayList) {
            System.out.println(lang);
        }

        // LinkedList: fast insertion/deletion
        List<Integer> linkedList = new LinkedList<>();
        linkedList.add(1);
        linkedList.add(0, 0);  // Insert at front
        linkedList.remove(1);
    }
}

Set

Set is a collection of elements that does not allow duplicates. HashSet provides fast search based on hash tables. TreeSet maintains sorted order. LinkedHashSet maintains insertion order.

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetExample {
    public static void main(String[] args) {
        // HashSet: no order guarantee, fast search
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Apple");  // Duplicate ignored
        System.out.println(hashSet.size());  // 2

        // TreeSet: maintains sorted order
        Set<Integer> treeSet = new TreeSet<>();
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(2);
        // Output: 1, 2, 3 (sorted)

        // LinkedHashSet: maintains insertion order
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("First");
        linkedHashSet.add("Second");
        linkedHashSet.add("Third");
    }
}

Map

Map is an interface for storing key-value pair data. HashMap provides fast search based on hash tables. TreeMap maintains key-based sorting. LinkedHashMap maintains insertion order.

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class MapExample {
    public static void main(String[] args) {
        // HashMap: fast search, no order guarantee
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Java", 1995);
        hashMap.put("Python", 1991);
        hashMap.put("JavaScript", 1995);

        // Value lookup
        Integer year = hashMap.get("Java");
        System.out.println("Java was released in: " + year);

        // Check key existence
        if (hashMap.containsKey("Python")) {
            System.out.println("Python exists!");
        }

        // Iteration
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // getOrDefault
        int cYear = hashMap.getOrDefault("C", 1972);
    }
}

Collection Selection Guide

RequirementRecommended CollectionReason
Order maintained, index accessArrayListRandom access O(1)
Frequent insertion/deletionLinkedListInsertion/deletion O(1)
Remove duplicates, fast searchHashSetSearch O(1)
Sorted unique elementsTreeSetAuto sorting
Key-value pairs, fast searchHashMapSearch O(1)
Sorted by keyTreeMapAuto key sorting

Exception Handling

What is Exception Handling?

Exception handling is a mechanism that detects and appropriately responds to exceptional situations that may occur during program execution, preventing abnormal program termination. Java uses try-catch-finally syntax.

Exception Hierarchy

Throwable
    ├── Error (unrecoverable)
    │       ├── OutOfMemoryError
    │       └── StackOverflowError
    └── Exception
            ├── RuntimeException (Unchecked)
            │       ├── NullPointerException
            │       ├── ArrayIndexOutOfBoundsException
            │       └── IllegalArgumentException
            └── Checked Exception
                    ├── IOException
                    ├── SQLException
                    └── FileNotFoundException

Exception Handling Example

import java.io.*;

public class ExceptionExample {
    public static void main(String[] args) {
        // try-catch-finally
        try {
            int result = divide(10, 0);
        } catch (ArithmeticException e) {
            System.out.println("Division error: " + e.getMessage());
        } finally {
            System.out.println("This always executes");
        }

        // try-with-resources (Java 7+)
        try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
            String line = reader.readLine();
            System.out.println(line);
        } catch (IOException e) {
            System.out.println("IO error: " + e.getMessage());
        }

        // Multiple catch
        try {
            String str = null;
            str.length();
        } catch (NullPointerException | IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    // Propagate exception with throws
    public static int divide(int a, int b) throws ArithmeticException {
        if (b == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        return a / b;
    }
}

Conclusion

Java is an object-oriented programming language that has continuously evolved for nearly 30 years since its birth in 1995. It provides JVM-based platform independence, a strong type system, automatic memory management, and rich standard libraries. Java has established itself as the standard for enterprise application development.

Understanding and properly utilizing the four pillars of object-oriented programming—encapsulation, inheritance, polymorphism, and abstraction—enables writing maintainable and extensible code. Generics and the collection framework greatly improve type safety and data structure utilization. With continuous language development including lambda expressions and Stream API in Java 8, and virtual threads in Java 21, Java also supports modern programming paradigms.