Java Programming Notes: Core Concepts, OOP and Collections

Java is a class-based, strongly typed programming language used for server-side software, desktop applications, developer tools, and many large-scale systems. These notes explain the concepts most useful for learning Java and answering programming-theory questions accurately.

Each standalone example below can be saved as Main.java, compiled with javac Main.java, and run with java Main.

1. Introduction to Java

Java was developed at Sun Microsystems and was publicly released in 1995. A Java program is normally compiled into bytecode rather than directly into machine code for one operating system. A compatible Java runtime then loads and executes that bytecode.

This is the basis of Java's “write once, run anywhere” idea. In practice, portability depends on using a compatible runtime and avoiding unnecessary dependence on operating-system-specific APIs or native libraries.

Important: Java is not purely compiled or purely interpreted. Source code is compiled into bytecode, and the JVM may interpret bytecode or compile frequently used code into native machine code while the program runs.

First Java program

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
  • public class Main defines a class named Main.
  • main is the conventional entry point for a standalone Java application.
  • String[] args receives command-line arguments.
  • System.out.println() writes a line to standard output.

2. JDK, JRE and JVM

The Java platform has several related terms. They should not be treated as interchangeable.

Difference between JVM, JRE and JDK
Term Meaning Purpose
JVM Java Virtual Machine Loads, verifies, and executes Java bytecode.
JRE Java Runtime Environment The runtime concept: JVM plus the libraries and supporting components needed to run Java applications.
JDK Java Development Kit Provides development tools such as javac, debugging tools, documentation tools, and a Java runtime.

Some modern Java distributions do not provide a separate downloadable JRE package. The distinction remains useful: the JDK is for development, while a Java runtime is needed to execute an application.

How a Java program runs

  1. A programmer writes source code in a .java file.
  2. The javac compiler converts source code into bytecode in a .class file.
  3. The JVM class loader finds and loads required classes.
  4. The JVM verifies, links, and initializes classes as needed.
  5. The execution engine interprets bytecode or uses Just-In-Time compilation for frequently executed code.

Memory management

Java uses automatic garbage collection to reclaim heap memory occupied by objects that are no longer reachable. Garbage collection does not replace explicit cleanup of external resources such as files, sockets, or database connections. Those resources should be closed with try-with-resources or an appropriate API.

3. Data Types, Variables, Arrays and Strings

Java is statically typed: every variable has a declared type, and the compiler checks whether values are used in a type-safe way.

Primitive data types

The eight primitive types in Java
Type Size Typical range or value
byte 8 bits -128 to 127
short 16 bits -32,768 to 32,767
int 32 bits -231 to 231 - 1
long 64 bits -263 to 263 - 1
float 32 bits Single-precision floating-point value
double 64 bits Double-precision floating-point value
char 16 bits An unsigned UTF-16 code unit
boolean Not fixed by the language specification true or false
Note about char: a Java char is a UTF-16 code unit, not always one complete user-perceived character. Some Unicode characters require two char values.

Variables and type conversion

public class Main {
    public static void main(String[] args) {
        int age = 20;
        long population = 1_400_000_000L;
        float percentage = 82.5f;
        double average = 76.25;
        char grade = 'A';
        boolean passed = true;

        double widened = age;       // implicit widening conversion
        int truncated = (int) 9.8;  // explicit narrowing conversion

        System.out.println(widened);
        System.out.println(truncated); // 9
    }
}

Reference types

Classes, interfaces, arrays, enums, records, and strings are reference types. A variable of a reference type holds either a reference to an object or null.

  • An array has a fixed length and uses zero-based indexes.
  • String is a reference type and is immutable.
  • Wrapper classes such as Integer and Double represent primitive values as objects.
  • Java always uses pass-by-value, including when an object reference is passed to a method.
public class Main {
    public static void main(String[] args) {
        int[] marks = {72, 86, 91};
        int total = 0;

        for (int mark : marks) {
            total += mark;
        }

        String message = "Java";
        String updatedMessage = message + " Programming";

        System.out.println(total);
        System.out.println(updatedMessage);
    }
}

4. Operators and Control Statements

Common operators

Common Java operator categories
Category Examples Use
Arithmetic + - * / % Mathematical calculations
Relational == != > < >= <= Comparing values
Logical && || ! Combining boolean conditions
Assignment = += -= *= /= Assigning or updating values
Unary ++ -- + - ! Operating on one operand
Bitwise & | ^ ~ << >> >>> Working with individual bits
Ternary ? : Short conditional expression

The operators && and || short-circuit: the second condition may not be evaluated. The operators & and | can also work with boolean values, but both operands are evaluated.

Comparing strings correctly

public class Main {
    public static void main(String[] args) {
        String first = new String("Java");
        String second = new String("Java");

        System.out.println(first == second);       // false: different object references
        System.out.println(first.equals(second));  // true: same text content
    }
}

Use == to compare primitive values. For string content, use equals() or equalsIgnoreCase() when that comparison is appropriate.

Decision-making and loops

public class Main {
    public static void main(String[] args) {
        int marks = 85;
        String grade;

        if (marks >= 90) {
            grade = "A";
        } else if (marks >= 75) {
            grade = "B";
        } else {
            grade = "C";
        }

        System.out.println("Grade: " + grade);

        for (int number = 1; number <= 3; number++) {
            System.out.println(number);
        }
    }
}
  • if, if-else, and switch select between alternatives.
  • for, while, and do-while repeat statements.
  • The enhanced for loop is useful for arrays and collections.
  • break exits a loop or switch; continue skips to the next loop iteration.

5. Classes, Objects and OOP

Object-oriented programming organizes code around objects that combine state and behavior. A class is a blueprint, while an object is an instance created from that class.

Four central OOP principles

  • Encapsulation: protect internal state and expose controlled operations.
  • Inheritance: derive a class from another class.
  • Polymorphism: use one common type or method call with different behavior.
  • Abstraction: show essential behavior while hiding unnecessary implementation details.

Class, constructor and object example

public class Main {
    static class Student {
        private final String name;
        private final int age;

        Student(String name, int age) {
            if (age < 0) {
                throw new IllegalArgumentException("Age cannot be negative.");
            }

            this.name = name;
            this.age = age;
        }

        void printDetails() {
            System.out.println(name + " is " + age + " years old.");
        }
    }

    public static void main(String[] args) {
        Student student = new Student("Asha", 20);
        student.printDetails();
    }
}
  • A constructor initializes a newly created object.
  • this refers to the current object.
  • static members belong to the class rather than an individual object.
  • final prevents reassignment of a variable reference and can prevent overriding or inheritance when applied to methods or classes.

6. Inheritance and Polymorphism

Inheritance lets a subclass reuse accessible members of a superclass. Java supports single inheritance for classes: one class cannot directly extend more than one class. A class can, however, implement multiple interfaces.

Common forms of class inheritance

  • Single inheritance: one subclass extends one superclass.
  • Multilevel inheritance: a subclass is extended by another subclass.
  • Hierarchical inheritance: multiple subclasses extend one superclass.

Runtime polymorphism through overriding

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

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

    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.sound(); // Dog barks
    }
}

Method overloading vs method overriding

Difference between overloading and overriding
Feature Overloading Overriding
Where it occurs Usually within the same class Between a superclass and subclass
Parameters Must differ Must have the same parameter list
Selection time Compile time Runtime for instance methods
Return type only Cannot distinguish overloaded methods by return type alone Must be compatible with the inherited method
Remember: static methods are hidden, not overridden. Private methods are not inherited and therefore cannot be overridden.

7. Abstraction and Interfaces

Abstraction focuses on what an object can do instead of exposing every internal implementation detail. Java supports abstraction through abstract classes and interfaces.

Abstract classes and interfaces
Feature Abstract class Interface
Object creation Cannot be directly instantiated Cannot be directly instantiated
State and constructors Can have instance fields and constructors Does not provide normal instance constructors
Methods Can contain abstract and concrete methods Can contain abstract, default, static, and supported private methods
Inheritance A class can extend one class A class can implement multiple interfaces
public class Main {
    interface Printable {
        void print();
    }

    static class Report implements Printable {
        @Override
        public void print() {
            System.out.println("Printing report");
        }
    }

    public static void main(String[] args) {
        Printable item = new Report();
        item.print();
    }
}

A non-abstract subclass must implement every inherited abstract method that has not already been implemented by a superclass.

8. Encapsulation and Access Modifiers

Encapsulation protects an object's internal state by keeping fields private when appropriate and exposing validated methods for reading or changing that state.

Java member access levels
Modifier Same class Same package Subclass in another package Other classes
private Yes No No No
package-private
(no modifier)
Yes Yes No No
protected Yes Yes Yes, through inheritance rules No
public Yes Yes Yes Yes, subject to module visibility where applicable

Outside its package, a subclass cannot use a protected member through an arbitrary superclass object. Protected access is intended for code that is implementing the subclass relationship.

import java.math.BigDecimal;

public class Main {
    static class BankAccount {
        private BigDecimal balance = BigDecimal.ZERO;

        void deposit(BigDecimal amount) {
            if (amount.signum() <= 0) {
                throw new IllegalArgumentException("Deposit must be positive.");
            }

            balance = balance.add(amount);
        }

        BigDecimal getBalance() {
            return balance;
        }
    }

    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(new BigDecimal("250.50"));

        System.out.println(account.getBalance());
    }
}

BigDecimal is preferable to double when a program needs exact decimal monetary calculations.

9. Exception Handling and File Resources

An exception is an event that interrupts the normal flow of a program. Java provides try, catch, finally, throw, and throws to handle or communicate exceptional conditions.

Important exception categories
Category Meaning Example
Error Serious JVM or environment problem; applications do not normally recover from it. OutOfMemoryError
Checked exception Usually must be caught or declared with throws. IOException
Unchecked exception A subclass of RuntimeException; the compiler does not force handling. NullPointerException

Try-with-resources

Use try-with-resources for objects that must be closed, such as readers, streams, sockets, and database connections. The resource is closed automatically even if an exception occurs.

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class Main {
    public static void main(String[] args) {
        Path file = Path.of("notes.txt");

        try (BufferedReader reader =
                Files.newBufferedReader(file, StandardCharsets.UTF_8)) {

            System.out.println(reader.readLine());

        } catch (IOException exception) {
            System.err.println("Unable to read the file: "
                    + exception.getMessage());
        }
    }
}
Good practice: catch exceptions that your program can handle meaningfully. Avoid catching Error classes or using an empty catch block that hides a real problem.

10. Multithreading and Concurrency

Multithreading allows a program to perform multiple tasks concurrently. Java provides the low-level Thread class as well as higher-level concurrency utilities in java.util.concurrent.

Extending Thread is valid for learning, but using Runnable, Callable, or an executor is usually more flexible because a class can still extend another class.

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

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        try {
            executor.submit(() -> System.out.println("Task is running."));
        } finally {
            executor.shutdown();
        }
    }
}

Concurrent tasks do not have a guaranteed execution order unless a program explicitly coordinates them. Shared mutable data can create race conditions, so use synchronization, locks, immutable objects, or concurrent collections when appropriate.

JVM thread states
State Meaning
NEW The thread has been created but not started.
RUNNABLE The thread is eligible to run in the JVM.
BLOCKED The thread is waiting to acquire a monitor lock.
WAITING The thread is waiting indefinitely for another action.
TIMED_WAITING The thread is waiting for a specified period.
TERMINATED The thread has completed execution.

11. Java Collections Framework

The Java Collections Framework provides interfaces and classes for storing and processing groups of objects. Generics such as List<String> help the compiler detect type errors.

Important distinction: Map belongs to the Collections Framework, but it does not extend the Collection interface. It stores key-value mappings.
Main Collections Framework interfaces
Interface Purpose Key property
List Ordered sequence of elements Duplicates are generally allowed.
Set Collection of unique elements Duplicate elements are not allowed.
Queue Elements waiting to be processed Often follows FIFO behavior, depending on implementation.
Deque Double-ended queue Elements can be added or removed at both ends.
Map Key-value associations Each key maps to at most one value.
Common collection implementations
Implementation Typical use Ordering behavior
ArrayList General-purpose list with efficient indexed access Preserves insertion order
HashSet Unique elements with fast typical lookup No insertion-order guarantee
LinkedHashSet Unique elements while preserving insertion order Preserves insertion order
TreeSet Unique sorted elements Sorted by natural order or a comparator
HashMap General-purpose key-value mapping No key-order guarantee
TreeMap Key-value mapping with sorted keys Sorted by key
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        List<String> subjects = new ArrayList<>();
        subjects.add("Java");
        subjects.add("Networking");

        Set<String> uniqueSubjects = Set.of("Java", "DBMS");

        Map<Integer, String> students = new HashMap<>();
        students.put(101, "Asha");
        students.put(102, "Ravi");

        System.out.println(subjects.get(0));
        System.out.println(uniqueSubjects);
        System.out.println(students.getOrDefault(103, "Not found"));
    }
}

ArrayList is usually a good default choice for a list. Do not assume that LinkedList is automatically faster for all insertions or deletions; the right choice depends on the program's actual access pattern.

12. Quick Revision and Practice Questions

Java quick-revision table
Topic Key point
Java bytecode Produced by the Java compiler and executed by a compatible JVM.
JDK Development toolkit containing tools such as javac.
JVM Loads, verifies, and executes Java bytecode.
Primitive types Java has eight primitive types.
String comparison Use equals() for text content; == compares references.
Overloading Same method name with different parameter lists; selected at compile time.
Overriding A subclass supplies an instance-method implementation; selected at runtime.
Exceptions Checked exceptions are generally caught or declared; use try-with-resources for closeable resources.
Collections List, Set, Queue, and Map solve different storage needs.

Practice questions

  1. What is the difference between the JDK and JVM?
    Answer: The JDK contains development tools and a runtime; the JVM is the component that executes Java bytecode.
  2. Is Java pass-by-reference?
    Answer: No. Java is pass-by-value. When an object is passed, the value copied is the object reference.
  3. Can a Java class extend multiple classes?
    Answer: No. Java supports single inheritance for classes, but a class can implement multiple interfaces.
  4. What is the difference between == and equals() for strings?
    Answer: == compares references, while equals() compares string content.
  5. Why is try-with-resources useful?
    Answer: It automatically closes declared resources even when an exception occurs.

Further Reading