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.
First Java program
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
public class Maindefines a class namedMain.mainis the conventional entry point for a standalone Java application.String[] argsreceives 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.
| 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
- A programmer writes source code in a
.javafile. - The
javaccompiler converts source code into bytecode in a.classfile. - The JVM class loader finds and loads required classes.
- The JVM verifies, links, and initializes classes as needed.
- 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
| 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 |
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.
Stringis a reference type and is immutable.- Wrapper classes such as
IntegerandDoublerepresent 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
| 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, andswitchselect between alternatives.for,while, anddo-whilerepeat statements.- The enhanced
forloop is useful for arrays and collections. breakexits a loop or switch;continueskips 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.
thisrefers to the current object.staticmembers belong to the class rather than an individual object.finalprevents 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
| 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 |
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.
| 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.
| 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.
| 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());
}
}
}
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.
| 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.
Map belongs to the
Collections Framework, but it does not extend the Collection
interface. It stores key-value mappings.
| 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. |
| 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
| 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
-
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. -
Is Java pass-by-reference?
Answer: No. Java is pass-by-value. When an object is passed, the value copied is the object reference. -
Can a Java class extend multiple classes?
Answer: No. Java supports single inheritance for classes, but a class can implement multiple interfaces. -
What is the difference between
==andequals()for strings?
Answer:==compares references, whileequals()compares string content. -
Why is try-with-resources useful?
Answer: It automatically closes declared resources even when an exception occurs.