Learn Java fundamentals: the JVM, types, control flow, classes and objects, inheritance, interfaces, records, collections, generics, streams, exceptions, and a complete runnable example.
What Is Java?
Java is a statically typed, object-oriented language commonly used for backend services, enterprise systems, Android development, and tools. Java source code is compiled to bytecode that runs on a Java Virtual Machine (JVM).
JDK, JRE, and JVM
- JVM: executes bytecode and provides memory management and garbage collection.
- JRE: the runtime environment that includes the JVM and standard libraries.
- JDK: the development kit with the compiler (
javac), tools, and the runtime. Install a current long-term-support JDK to develop.
Why Java is popular
- Strong typing and tooling catch many mistakes before runtime.
- Automatic garbage collection removes manual memory management.
- Large ecosystem: Spring, Hibernate, Maven, Gradle, JUnit.
- Runs on many platforms wherever a compatible JVM is available.
Hello, world and running it
// Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}
// Compile and run:
// javac Main.java
// java MainTypes, Variables, and Strings
- Primitive types:
int,long,double,boolean,char,byte,short,float. - Reference types: classes, arrays, interfaces, and
String. varlets the compiler infer local variable types.Stringis immutable; useStringBuilderto build text in loops.- Compare strings with
.equals(), not==. - Use text blocks (
""") for multi-line strings.
Variables, strings, and StringBuilder
public class Types {
public static void main(String[] args) {
int age = 21;
double price = 19.99;
boolean active = true;
var name = "Asha"; // inferred as String
String a = "java";
String b = new String("java");
System.out.println(a == b); // false (different objects)
System.out.println(a.equals(b)); // true
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 3; i++) {
sb.append("Item ").append(i).append('\n');
}
System.out.print(sb);
System.out.printf("%s is %d and costs %.2f%n", name, age, price);
}
}Control Flow: Conditions, Loops, and Switch Expressions
if/else,for, enhancedfor,while, anddo...whilebehave as in other C-style languages.- The enhanced
forloop iterates over arrays and collections. - Modern
switchexpressions use->, return a value, and need nobreak. - Always use braces for blocks, even for single statements.
Loops and a switch expression
public class Flow {
public static void main(String[] args) {
int[] scores = {92, 74, 58};
for (int score : scores) {
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8, 7 -> "B";
default -> "Needs work";
};
System.out.println(score + " -> " + grade);
}
int i = 0;
while (i < 3) {
System.out.println("Attempt " + (++i));
}
}
}Classes and Objects
A class defines fields and methods; an object is an instance of a class. Encapsulation keeps data and behavior together.
- Make fields
privateand expose behavior through methods. - Constructors create valid objects; use
thisto refer to the current instance. - Use
staticfor members that belong to the class, not an instance. - Override
toString,equals, andhashCodethoughtfully for value-like classes. - Use access modifiers intentionally and keep class responsibilities focused.
A well-encapsulated class
public class BankAccount {
private final String owner;
private double balance;
public BankAccount(String owner, double initialBalance) {
if (initialBalance < 0) {
throw new IllegalArgumentException("Balance cannot be negative");
}
this.owner = owner;
this.balance = initialBalance;
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
balance += amount;
}
public boolean withdraw(double amount) {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
return true;
}
public double getBalance() { return balance; }
public String getOwner() { return owner; }
@Override
public String toString() {
return owner + ": " + balance;
}
}Inheritance, Interfaces, and Polymorphism
extendscreates a subclass; Java supports single class inheritance.- An interface defines a contract that classes implement; a class can implement many.
- Polymorphism lets code work with the interface type while each object supplies its own behavior.
- Use
@Overrideso the compiler checks your intent. - Prefer composition and interfaces over deep inheritance trees.
abstractclasses provide partial implementations.
An interface with polymorphic implementations
import java.util.List;
interface Shape {
double area();
default String describe() {
return getClass().getSimpleName() + " with area " + String.format("%.2f", area());
}
}
class Circle implements Shape {
private final double radius;
Circle(double radius) { this.radius = radius; }
@Override public double area() { return Math.PI * radius * radius; }
}
class Rectangle implements Shape {
private final double width, height;
Rectangle(double width, double height) { this.width = width; this.height = height; }
@Override public double area() { return width * height; }
}
public class Shapes {
public static void main(String[] args) {
List<Shape> shapes = List.of(new Circle(2), new Rectangle(3, 4));
for (Shape s : shapes) {
System.out.println(s.describe());
}
}
}Records and Enums
- A record is a compact, immutable data carrier with generated constructor, accessors,
equals,hashCode, andtoString. - An enum defines a fixed set of constants and can have fields and methods.
- Records can validate input in a compact constructor.
A record and an enum
public class Models {
record Point(int x, int y) {
Point {
if (x < 0 || y < 0) throw new IllegalArgumentException("Negative coordinate");
}
double distanceFromOrigin() { return Math.sqrt(x * x + y * y); }
}
enum Level {
LOW(1), MEDIUM(5), HIGH(10);
private final int weight;
Level(int weight) { this.weight = weight; }
int weight() { return weight; }
}
public static void main(String[] args) {
Point p = new Point(3, 4);
System.out.println(p + " -> " + p.distanceFromOrigin()); // Point[x=3, y=4] -> 5.0
System.out.println(Level.HIGH.weight());
}
}Collections and Generics
The Java Collections Framework provides reusable data structures. Generics express which types a collection contains and catch mistakes at compile time.
- **
List(ArrayList):** ordered, allows duplicates, index access. - **
Set(HashSet,TreeSet):** unique elements;TreeSetkeeps them sorted. - **
Map(HashMap,TreeMap):** key-value pairs. - **
Deque(ArrayDeque):** stack or queue behavior. - Program to interfaces: declare
List<String> items = new ArrayList<>();. List.of,Set.of, andMap.ofcreate immutable collections.
Working with List, Set, and Map
import java.util.*;
public class Collections1 {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of("Asha", "Ravi"));
names.add("Meera");
names.sort(Comparator.naturalOrder());
System.out.println(names);
Set<Integer> unique = new TreeSet<>(List.of(3, 1, 3, 2)); // [1, 2, 3]
System.out.println(unique);
Map<String, Integer> ages = new HashMap<>();
ages.put("Asha", 21);
ages.merge("Asha", 1, Integer::sum); // 22
ages.putIfAbsent("Ravi", 19);
for (Map.Entry<String, Integer> e : ages.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
}
}Lambdas and the Streams API
- A lambda is a short anonymous function:
(a, b) -> a + b. - Streams process collections with a pipeline: source, intermediate operations, terminal operation.
- Common operations:
filter,map,sorted,limit,collect,reduce,count. - Streams do not modify the source collection.
- Use a simple loop when it is clearer than a stream.
Filtering, mapping, and grouping with streams
import java.util.*;
import java.util.stream.Collectors;
public class StreamsDemo {
record Employee(String name, String dept, double salary) {}
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("Asha", "Engineering", 90000),
new Employee("Ravi", "Engineering", 80000),
new Employee("Meera", "Design", 70000)
);
List<String> highEarners = staff.stream()
.filter(e -> e.salary() >= 80000)
.map(Employee::name)
.sorted()
.toList();
System.out.println(highEarners); // [Asha, Ravi]
Map<String, Double> avgByDept = staff.stream()
.collect(Collectors.groupingBy(Employee::dept,
Collectors.averagingDouble(Employee::salary)));
System.out.println(avgByDept);
}
}Exception Handling
- Checked exceptions (such as
IOException) must be caught or declared; they signal recoverable problems. - Unchecked exceptions (subclasses of
RuntimeException) signal programming errors such as bad arguments. - Catch specific exception types where recovery is possible; avoid empty catch blocks that hide failures.
- Use try-with-resources so files and connections are closed automatically.
- Create custom exceptions for meaningful domain failures.
- Preserve the cause when wrapping:
throw new X("message", cause).
try-with-resources and a custom exception
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Exceptions {
static class ConfigException extends Exception {
ConfigException(String message, Throwable cause) { super(message, cause); }
}
static String readFirstLine(Path path) throws ConfigException {
try (BufferedReader reader = Files.newBufferedReader(path)) {
return reader.readLine();
} catch (IOException e) {
throw new ConfigException("Could not read " + path, e);
}
}
public static void main(String[] args) {
try {
System.out.println(readFirstLine(Path.of("config.txt")));
} catch (ConfigException e) {
System.err.println(e.getMessage() + " (" + e.getCause().getClass().getSimpleName() + ")");
} finally {
System.out.println("Done");
}
}
}Packages, Build Tools, and Testing
- Packages group related classes:
package com.example.app;. - Use Maven or Gradle to manage dependencies and builds.
- Use JUnit 5 for unit tests.
- Keep one top-level public class per file, named after the file.
A simple JUnit 5 test
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class BankAccountTest {
@Test
void depositIncreasesBalance() {
BankAccount account = new BankAccount("Asha", 100);
account.deposit(50);
assertEquals(150, account.getBalance(), 0.0001);
}
@Test
void negativeDepositIsRejected() {
BankAccount account = new BankAccount("Asha", 100);
assertThrows(IllegalArgumentException.class, () -> account.deposit(-5));
}
}Best Practices
- Keep fields private and objects valid after construction.
- Prefer immutability:
finalfields, records, and immutable collections. - Program to interfaces and use generics instead of raw types.
- Use
equals, not==, for object comparison. - Never swallow exceptions silently; log or rethrow with context.
- Use try-with-resources for anything that must be closed.
- Write unit tests and keep methods short and focused.
- Follow common naming conventions:
CamelCaseclasses,camelCasemethods and variables.
Complete Example: Counting Words in a List
Save the code as Main.java. With a recent JDK, compile using javac Main.java and run with java Main.
Count words in a list
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
List<String> languages = List.of(
"Java", "Python", "Java", "C++", "Python", "Java"
);
Map<String, Integer> counts = new HashMap<>();
for (String language : languages) {
counts.merge(language, 1, Integer::sum);
}
counts.forEach((language, count) ->
System.out.println(language + ": " + count)
);
}
}Frequently Asked Questions
Is Java platform independent?
Java bytecode can run on platforms that provide a compatible JVM, though native dependencies and environment details can affect portability.
What is the JVM?
The Java Virtual Machine executes Java bytecode and provides runtime services such as memory management and garbage collection.
Why use generics?
Generics express type expectations for classes and collections and reduce the need for unsafe casts.
What is the difference between == and equals in Java?
== compares primitive values or whether two references point to the same object, while equals compares the logical content of objects when a class overrides it, as String does.
What is the difference between checked and unchecked exceptions?
Checked exceptions must be caught or declared and represent recoverable conditions such as IOException. Unchecked exceptions extend RuntimeException and usually indicate programming errors.
Should I learn Java or Python first?
Python has gentler syntax for beginners, while Java teaches strong typing and object-oriented structure. Pick the one that matches your goals, such as backend or Android work for Java.