Java is a high-level, object-oriented programming language designed to have as few implementation dependencies as possible.
The Java Virtual Machine (JVM) is the runtime environment for executing Java bytecode.
| Type | Size | Range | Default Value |
|---|---|---|---|
| byte | 1 byte | -128 to 127 | 0 |
| short | 2 bytes | -32,768 to 32,767 | 0 |
| int | 4 bytes | -2Β³ΒΉ to 2Β³ΒΉ-1 | 0 |
| long | 8 bytes | -2βΆΒ³ to 2βΆΒ³-1 | 0L |
| float | 4 bytes | IEEE 754 | 0.0f |
| double | 8 bytes | IEEE 754 | 0.0d |
| char | 2 bytes | 0 to 65,535 | '\u0000' |
| boolean | 1 bit | true/false | false |
Wrapping data (variables) and methods that operate on the data into a single unit (class). It hides the internal details and provides controlled access through public methods.
public class Student {
private String name; // Private data member
private int age;
// Public getter method
public String getName() {
return name;
}
// Public setter method with validation
public void setAge(int age) {
if (age > 0) {
this.age = age;
}
}
}
Mechanism by which one class acquires the properties and methods of another class.
// Parent class
class Animal {
protected String name;
public void eat() {
System.out.println(name + " is eating");
}
}
// Child class inheriting from Animal
class Dog extends Animal {
public void bark() {
System.out.println(name + " is barking");
}
}
Ability of objects to take multiple forms. Same method name can have different implementations.
// Method Overriding (Runtime Polymorphism)
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
Hiding implementation details while showing only essential features.
// Abstract class
abstract class Shape {
protected String color;
// Abstract method - must be implemented by subclasses
public abstract double calculateArea();
// Concrete method
public void setColor(String color) {
this.color = color;
}
}
class Circle extends Shape {
private double radius;
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
The Java Collections Framework is a unified architecture for representing and manipulating collections of objects.
Collection
βββ List (ArrayList, LinkedList, Vector)
βββ Set (HashSet, LinkedHashSet, TreeSet)
βββ Queue (PriorityQueue, ArrayDeque)
Map (HashMap, LinkedHashMap, TreeMap, Hashtable)
// ArrayList Example
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Python");
arrayList.add(1, "JavaScript"); // Insert at index 1
// LinkedList Example
List<String> linkedList = new LinkedList<>();
linkedList.addFirst("First");
linkedList.addLast("Last");
// Iteration
for (String item : arrayList) {
System.out.println(item);
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Thread: " + Thread.currentThread().getName() + ", Count: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
// Usage
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Runnable: " + Thread.currentThread().getName() + ", Count: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
// Usage
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start();
thread2.start();
// Lambda expression with Runnable
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda Thread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
thread.start();
Synchronization is a mechanism to control access to shared resources by multiple threads to prevent race conditions and data inconsistency.
class Counter {
private int count = 0;
// Synchronized method
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
// Usage
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
class BankAccount {
private double balance = 1000;
private final Object lock = new Object();
public void withdraw(double amount) {
synchronized (lock) {
if (balance >= amount) {
System.out.println("Withdrawing: " + amount);
balance -= amount;
System.out.println("Remaining balance: " + balance);
} else {
System.out.println("Insufficient balance");
}
}
}
}
class Utils {
private static int counter = 0;
// Static synchronized method
public static synchronized void incrementCounter() {
counter++;
}
// Synchronized block on class object
public static void incrementCounterBlock() {
synchronized (Utils.class) {
counter++;
}
}
}
Lambda expressions provide a concise way to represent anonymous functions. They enable functional programming in Java.
// Basic syntax: (parameters) -> expression
// Or: (parameters) -> { statements; }
// Examples:
() -> System.out.println("Hello World"); // No parameters
(x) -> x * x; // Single parameter
(x, y) -> x + y; // Multiple parameters
(x, y) -> { // Block body
int sum = x + y;
return sum;
};
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Before Java 8 (Anonymous inner class)
names.forEach(new Consumer<String>() {
@Override
public void accept(String name) {
System.out.println(name);
}
});
// With Lambda expression
names.forEach(name -> System.out.println(name));
// Method reference (even more concise)
names.forEach(System.out::println);
// Filtering with lambda
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
Streams API provides a functional approach to processing collections of data. It allows for declarative data processing with operations like filter, map, and reduce.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter even numbers, square them, and collect to list
List<Integer> evenSquares = numbers.stream()
.filter(n -> n % 2 == 0) // Intermediate operation
.map(n -> n * n) // Intermediate operation
.collect(Collectors.toList()); // Terminal operation
// Find sum of all numbers
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
// Find first even number
Optional<Integer> firstEven = numbers.stream()
.filter(n -> n % 2 == 0)
.findFirst();
// Group by even/odd
Map<Boolean, List<Integer>> groupedByEvenOdd = numbers.stream()
.collect(Collectors.groupingBy(n -> n % 2 == 0));
// Process large datasets in parallel
List<String> largeList = // ... large list
long count = largeList.parallelStream()
.filter(s -> s.length() > 10)
.count();
Java uses try-catch-finally blocks to handle exceptions. Exceptions are objects that represent errors or exceptional conditions.
Throwable
βββ Error (OutOfMemoryError, StackOverflowError)
βββ Exception
βββ RuntimeException (Unchecked)
β βββ NullPointerException
β βββ ArrayIndexOutOfBoundsException
β βββ IllegalArgumentException
βββ Checked Exceptions
βββ IOException
βββ SQLException
βββ ClassNotFoundException
public class ExceptionExample {
public static void divideNumbers(int a, int b) {
try {
int result = a / b;
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: Division by zero");
e.printStackTrace();
} finally {
System.out.println("Finally block always executes");
}
}
// Method that throws checked exception
public static void readFile(String filename) throws IOException {
FileReader file = new FileReader(filename);
// File operations
file.close();
}
}
// Custom checked exception
class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
super("Insufficient funds: " + amount);
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
// Usage
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount);
}
balance -= amount;
}
Spring is a comprehensive framework for enterprise Java development that provides infrastructure support for developing Java applications.
// Service class
@Service
public class UserService {
private UserRepository userRepository;
// Constructor injection (recommended)
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findById(Long id) {
return userRepository.findById(id);
}
}
// Repository interface
@Repository
public interface UserRepository {
User findById(Long id);
void save(User user);
}
// Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
The Java Virtual Machine (JVM) is a runtime environment that executes Java bytecode. It provides platform independence and memory management.
JVM Architecture
βββ Class Loader Subsystem
β βββ Bootstrap ClassLoader
β βββ Extension ClassLoader
β βββ Application ClassLoader
βββ Runtime Data Areas
β βββ Method Area (Metaspace in Java 8+)
β βββ Heap Memory
β β βββ Young Generation (Eden, S1, S2)
β β βββ Old Generation (Tenured)
β βββ Stack Memory (per thread)
β βββ PC Registers (per thread)
β βββ Native Method Stack
βββ Execution Engine
βββ Interpreter
βββ Just-In-Time (JIT) Compiler
βββ Garbage Collector
// JVM Execution Process
Source Code (.java)
β javac compiler
Bytecode (.class)
β ClassLoader
Loaded Classes in Method Area
β Execution Engine
βββ Interpreter (line by line execution)
βββ JIT Compiler (compile to native code)
β
Native Machine Code Execution
// Class Loading Phases
1. Loading: Read .class file and create Class object
2. Linking:
a. Verification: Verify bytecode correctness
b. Preparation: Allocate memory for static variables
c. Resolution: Replace symbolic references with direct references
3. Initialization: Execute static initializers and static blocks
// Example
public class Example {
static int count = 10; // Prepared with default value 0
static { // Executed during initialization
count = 20;
System.out.println("Static block executed");
}
}
Java memory is divided into several regions, each serving different purposes in program execution.
Heap Memory
βββ Young Generation
β βββ Eden Space (new objects)
β βββ Survivor Space 1 (S1)
β βββ Survivor Space 2 (S2)
βββ Old Generation (Tenured Space)
βββ Long-lived objects
public class MemoryBestPractices {
// Avoid memory leaks
private static final Map<String, Object> cache = new HashMap<>();
// Use StringBuilder for string concatenation
public String buildString(List<String> items) {
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
return sb.toString();
}
// Close resources properly
public void readFile(String filename) {
try (FileReader reader = new FileReader(filename)) {
// File operations
} catch (IOException e) {
e.printStackTrace();
} // Auto-close with try-with-resources
}
}