The throw keyword in Java is used to explicitly throw an exception from a method or any block of code. It's a powerful tool for handling error conditions in your applications.
1. Basic Syntax
throw new ExceptionType("Error message"); 2. Basic Throw Examples
Throwing Built-in Exceptions
public class BasicThrowExamples { // Throwing IllegalArgumentException public static void setAge(int age) { if (age < 0 || age > 150) { throw new IllegalArgumentException("Age must be between 0 and 150: " + age); } System.out.println("Age set to: " + age); } // Throwing NullPointerException public static void printLength(String text) { if (text == null) { throw new NullPointerException("Text cannot be null"); } System.out.println("Text length: " + text.length()); } // Throwing ArithmeticException public static double divide(double a, double b) { if (b == 0) { throw new ArithmeticException("Division by zero is not allowed"); } return a / b; } public static void main(String[] args) { try { setAge(25); // Valid setAge(-5); // Throws IllegalArgumentException } catch (IllegalArgumentException e) { System.out.println("Caught: " + e.getMessage()); } try { printLength("Hello"); // Valid printLength(null); // Throws NullPointerException } catch (NullPointerException e) { System.out.println("Caught: " + e.getMessage()); } } } 3. Throwing Checked vs Unchecked Exceptions
Unchecked Exceptions (RuntimeException subclasses)
public class UncheckedExceptionExample { // No need to declare in method signature public static void validateEmail(String email) { if (email == null || !email.contains("@")) { throw new IllegalArgumentException("Invalid email format: " + email); } System.out.println("Email is valid: " + email); } public static void processArray(int[] array, int index) { if (array == null) { throw new NullPointerException("Array cannot be null"); } if (index < 0 || index >= array.length) { throw new ArrayIndexOutOfBoundsException( "Index " + index + " out of bounds for array length " + array.length); } System.out.println("Array element: " + array[index]); } } Checked Exceptions (Must be handled or declared)
import java.io.*; public class CheckedExceptionExample { // Must declare checked exceptions in method signature public static void readConfigFile(String filename) throws IOException { if (filename == null || filename.trim().isEmpty()) { throw new IOException("Filename cannot be null or empty"); } File file = new File(filename); if (!file.exists()) { throw new FileNotFoundException("Config file not found: " + filename); } // Simulate file reading System.out.println("Reading config file: " + filename); } // Alternative: Handle the exception internally public static boolean safeReadConfig(String filename) { try { if (filename == null) { throw new IOException("Filename cannot be null"); } System.out.println("Reading: " + filename); return true; } catch (IOException e) { System.out.println("Error reading config: " + e.getMessage()); return false; } } } 4. Custom Exceptions with Throw
Creating Custom Exceptions
// Custom checked exception class InsufficientBalanceException extends Exception { private double currentBalance; private double requiredAmount; public InsufficientBalanceException(double currentBalance, double requiredAmount) { super(String.format("Insufficient balance. Current: $%.2f, Required: $%.2f", currentBalance, requiredAmount)); this.currentBalance = currentBalance; this.requiredAmount = requiredAmount; } public double getCurrentBalance() { return currentBalance; } public double getRequiredAmount() { return requiredAmount; } } // Custom unchecked exception class InvalidTransactionException extends RuntimeException { private String transactionId; public InvalidTransactionException(String transactionId, String message) { super("Transaction " + transactionId + ": " + message); this.transactionId = transactionId; } public String getTransactionId() { return transactionId; } } Using Custom Exceptions
public class BankingService { private double balance; public BankingService(double initialBalance) { if (initialBalance < 0) { throw new InvalidTransactionException("INIT", "Initial balance cannot be negative: " + initialBalance); } this.balance = initialBalance; } // Using custom checked exception public void withdraw(double amount) throws InsufficientBalanceException { if (amount <= 0) { throw new InvalidTransactionException("WITHDRAW", "Withdrawal amount must be positive: " + amount); } if (amount > balance) { throw new InsufficientBalanceException(balance, amount); } balance -= amount; System.out.printf("Withdrawn: $%.2f, New balance: $%.2f%n", amount, balance); } // Using custom unchecked exception public void transfer(BankingService target, double amount) { if (target == null) { throw new InvalidTransactionException("TRANSFER", "Target account cannot be null"); } if (amount <= 0) { throw new InvalidTransactionException("TRANSFER", "Transfer amount must be positive: " + amount); } try { this.withdraw(amount); // Might throw checked exception target.deposit(amount); System.out.printf("Transferred: $%.2f to target account%n", amount); } catch (InsufficientBalanceException e) { // Convert checked exception to unchecked for this method throw new InvalidTransactionException("TRANSFER", "Insufficient funds for transfer: " + e.getMessage()); } } public void deposit(double amount) { if (amount <= 0) { throw new InvalidTransactionException("DEPOSIT", "Deposit amount must be positive: " + amount); } balance += amount; System.out.printf("Deposited: $%.2f, New balance: $%.2f%n", amount, balance); } public double getBalance() { return balance; } } 5. Throw in Real-World Scenarios
Validation Framework
public class UserValidator { public static void validateUser(String username, String email, int age) { // Collect all validation errors StringBuilder errors = new StringBuilder(); if (username == null || username.trim().length() < 3) { errors.append("Username must be at least 3 characters. "); } if (email == null || !email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) { errors.append("Invalid email format. "); } if (age < 13 || age > 120) { errors.append("Age must be between 13 and 120. "); } // Throw exception if any validation failed if (errors.length() > 0) { throw new IllegalArgumentException("Validation failed: " + errors.toString().trim()); } System.out.println("User validation successful!"); } public static void validatePassword(String password) { if (password == null || password.length() < 8) { throw new SecurityException("Password must be at least 8 characters long"); } if (!password.matches(".*[A-Z].*")) { throw new SecurityException("Password must contain at least one uppercase letter"); } if (!password.matches(".*[a-z].*")) { throw new SecurityException("Password must contain at least one lowercase letter"); } if (!password.matches(".*\\d.*")) { throw new SecurityException("Password must contain at least one digit"); } } } Data Processing with Throw
import java.util.*; public class DataProcessor { public static void processData(List<Integer> data, int divisor) { if (data == null) { throw new NullPointerException("Data list cannot be null"); } if (data.isEmpty()) { throw new IllegalArgumentException("Data list cannot be empty"); } if (divisor == 0) { throw new ArithmeticException("Divisor cannot be zero"); } List<Double> results = new ArrayList<>(); for (int i = 0; i < data.size(); i++) { Integer value = data.get(i); if (value == null) { throw new IllegalArgumentException( "Null value found at index " + i + " in data list"); } if (value < 0) { throw new IllegalArgumentException( "Negative value " + value + " found at index " + i); } double result = (double) value / divisor; results.add(result); } System.out.println("Processed results: " + results); } // Batch processing with detailed error reporting public static void processBatch(List<List<Integer>> batches) { int successful = 0; int failed = 0; for (int i = 0; i < batches.size(); i++) { try { processData(batches.get(i), 2); successful++; } catch (RuntimeException e) { failed++; System.err.printf("Batch %d failed: %s%n", i, e.getMessage()); // Log detailed information but don't stop execution if (e instanceof IllegalArgumentException) { System.err.println(" -> Input validation error"); } else if (e instanceof ArithmeticException) { System.err.println(" -> Mathematical operation error"); } } } System.out.printf("Batch processing complete. Successful: %d, Failed: %d%n", successful, failed); } } 6. Advanced Throw Patterns
Exception Chaining
public class ExceptionChaining { public static void processOrder(String orderId) { try { validateOrder(orderId); processPayment(orderId); shipOrder(orderId); } catch (Exception e) { // Chain the exception with additional context throw new RuntimeException("Failed to process order: " + orderId, e); } } private static void validateOrder(String orderId) { if (orderId == null || orderId.isEmpty()) { throw new IllegalArgumentException("Invalid order ID"); } System.out.println("Order validated: " + orderId); } private static void processPayment(String orderId) { // Simulate payment failure if (orderId.startsWith("FAIL")) { throw new RuntimeException("Payment processing failed for: " + orderId); } System.out.println("Payment processed: " + orderId); } private static void shipOrder(String orderId) { // Simulate shipping failure if (orderId.startsWith("SHIP_FAIL")) { throw new RuntimeException("Shipping failed for: " + orderId); } System.out.println("Order shipped: " + orderId); } public static void main(String[] args) { try { processOrder("ORDER123"); processOrder("FAIL456"); // This will cause exception chaining } catch (RuntimeException e) { System.out.println("Main caught: " + e.getMessage()); System.out.println("Root cause: " + e.getCause().getMessage()); e.printStackTrace(); } } } Conditional Throw with Helper Methods
public class ValidationUtils { // Helper methods for common validation patterns public static void requireNonNull(Object obj, String message) { if (obj == null) { throw new NullPointerException(message); } } public static void requireNonEmpty(String str, String message) { if (str == null || str.trim().isEmpty()) { throw new IllegalArgumentException(message); } } public static void requirePositive(int number, String message) { if (number <= 0) { throw new IllegalArgumentException(message); } } public static void requireInRange(int number, int min, int max, String message) { if (number < min || number > max) { throw new IllegalArgumentException( String.format("%s. Value %d must be between %d and %d", message, number, min, max)); } } } // Using the validation helpers public class ProductService { public void addProduct(String name, double price, int quantity) { ValidationUtils.requireNonEmpty(name, "Product name cannot be empty"); ValidationUtils.requirePositive((int) (price * 100), "Price must be positive"); ValidationUtils.requirePositive(quantity, "Quantity must be positive"); ValidationUtils.requireInRange(quantity, 1, 1000, "Invalid quantity"); System.out.printf("Product added: %s, Price: $%.2f, Quantity: %d%n", name, price, quantity); } public void updateProduct(String productId, String newName) { ValidationUtils.requireNonEmpty(productId, "Product ID cannot be empty"); ValidationUtils.requireNonEmpty(newName, "Product name cannot be empty"); if (productId.length() != 8) { throw new IllegalArgumentException("Product ID must be exactly 8 characters"); } System.out.printf("Product %s updated to: %s%n", productId, newName); } } 7. Testing Throw Statements
JUnit Tests for Exception Throwing
import org.junit.Test; import static org.junit.Assert.*; public class ThrowTestExamples { @Test(expected = IllegalArgumentException.class) public void testSetAgeWithNegativeValue() { BasicThrowExamples.setAge(-5); } @Test public void testWithdrawWithInsufficientBalance() { BankingService account = new BankingService(100); try { account.withdraw(200); // This should throw exception fail("Expected InsufficientBalanceException was not thrown"); } catch (InsufficientBalanceException e) { assertEquals(100.0, e.getCurrentBalance(), 0.001); assertEquals(200.0, e.getRequiredAmount(), 0.001); assertTrue(e.getMessage().contains("Insufficient balance")); } } @Test public void testUserValidatorWithInvalidData() { try { UserValidator.validateUser("ab", "invalid-email", 10); fail("Expected IllegalArgumentException was not thrown"); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains("Validation failed")); assertTrue(e.getMessage().contains("Username")); assertTrue(e.getMessage().contains("email")); assertTrue(e.getMessage().contains("Age")); } } @Test public void testProcessDataWithNullList() { try { DataProcessor.processData(null, 5); fail("Expected NullPointerException was not thrown"); } catch (NullPointerException e) { assertEquals("Data list cannot be null", e.getMessage()); } } } 8. Best Practices
1. Provide Meaningful Messages
// Good throw new IllegalArgumentException("Age must be between 0 and 150, but was: " + age); // Avoid throw new IllegalArgumentException("Invalid age"); 2. Use Appropriate Exception Types
// Use specific exceptions when possible throw new FileNotFoundException("Config file missing: " + filename); // Rather than generic throw new IOException("Config file missing: " + filename); 3. Don't Throw Generic Exceptions Unnecessarily
// Avoid - too generic public void process() throws Exception { // code } // Prefer - specific exceptions public void process() throws IOException, SQLException { // code } 4. Consider Performance
// Exception creation is expensive, so avoid in performance-critical code public void validateInput(String input) { if (input == null) { // Only create exception when actually needed throw new NullPointerException("Input cannot be null"); } } Summary
throwis used to explicitly throw exceptions- Can throw both built-in and custom exceptions
- Checked exceptions must be declared or handled
- Unchecked exceptions don't require declaration
- Use exception chaining to preserve original exceptions
- Always provide meaningful error messages
- Choose the appropriate exception type for each scenario
The throw keyword is essential for creating robust, self-documenting code that properly handles error conditions and provides clear feedback when things go wrong.