☕ Java

Java Coding Standards

Coding standards are the agreed-upon rules that make a codebase consistent, readable, and maintainable — especially when multiple developers work on it. Java has well-established standards covering formatting, structure, naming, documentation, and error handling. Here's the complete guide used in professional Java teams.

Why Coding Standards Matter

Code is read far more often than it's written. A function gets written once and read dozens of times — during code review, debugging, refactoring, and onboarding. Coding standards exist to make that reading fast and predictable. When everyone on a team follows the same standards, code reviews focus on logic instead of style. New developers can read any part of the codebase without encountering surprises. Bugs are easier to spot because the code structure is consistent and expected. Java's coding standards have been stable since Sun Microsystems published the original Java Code Conventions in 1997. Google, Oracle, and most major organizations publish their own variants — all built on the same foundation.

File Structure and Organization

Every Java source file should follow a consistent internal structure. This order is the standard:
Java
// 1. Package declaration (always first, no blank line before):
package com.example.banking.service;

// 2. Imports (grouped, no wildcard imports in production code):
import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.banking.model.Account;
import com.example.banking.repository.AccountRepository;

// 3. Class Javadoc:
/**
 * Handles core banking operations including deposits, withdrawals,
 * and balance inquiries.
 *
 * @since 2.0
 */

// 4. Class declaration:
@Service
public class AccountService {

    // 5. Constants first:
    private static final int MAX_DAILY_TRANSACTIONS = 50;
    private static final double OVERDRAFT_FEE = 25.0;

    // 6. Static fields:
    private static int totalAccounts = 0;

    // 7. Instance fields:
    private final AccountRepository repository;

    // 8. Constructors:
    public AccountService(AccountRepository repository) {
        this.repository = repository;
    }

    // 9. Public methods:
    public Account findById(Long id) { ... }

    // 10. Package/protected methods:
    void processTransaction(Transaction t) { ... }

    // 11. Private methods:
    private void validateAmount(double amount) { ... }
}

Formatting Standards

Consistent formatting makes code visually scannable. These are the universally accepted Java formatting rules:
Java
// ── Indentation: 4 spaces (NOT tabs) ────────────────────────────
public void process() {
    if (condition) {
        doSomething();
        if (nested) {
            doNested();
        }
    }
}

// ── Line length: 120 characters max (Google style) ───────────────
// Long method signatures — wrap with 8-space continuation indent:
public ResponseEntity<AccountResponse> createAccount(
        @RequestBody AccountRequest request,
        @PathVariable Long customerId) {
    // ...
}

// ── Braces: K&R style (opening brace on same line) ───────────────
// DO:
public void method() {
    if (condition) {
        // ...
    } else {
        // ...
    }
}

// DON'T (Allman style — non-standard in Java):
public void method()
{
    if (condition)
    {
        // ...
    }
}

// ── Always use braces, even for single-line if/for ───────────────
// DON'T:
if (condition) doSomething();

// DO:
if (condition) {
    doSomething();
}

// ── Blank lines for logical separation ───────────────────────────
public class OrderService {
                                    // one blank line after class opening
    private final OrderRepository repo;
                                    // one blank line between fields and constructor
    public OrderService(OrderRepository repo) {
        this.repo = repo;
    }
                                    // one blank line between methods
    public Order findById(Long id) {
        return repo.findById(id).orElseThrow();
    }
}

Method Design Standards

Well-designed methods are the foundation of readable Java code. These rules apply universally:
Java
// ── Single Responsibility: one method, one job ──────────────────

// DON'T — method doing three things:
public void processOrder(Order order) {
    validateOrder(order);           // validation
    chargePayment(order);           // payment
    sendConfirmationEmail(order);   // notification
    updateInventory(order);         // inventory
}

// DO — split by responsibility, orchestrate at a higher level:
public void processOrder(Order order) {
    orderValidator.validate(order);
    paymentService.charge(order);
    notificationService.sendConfirmation(order);
    inventoryService.deduct(order);
}

// ── Method length: aim for under 20 lines ───────────────────────
// If a method exceeds a screen, extract sub-methods.
// Names of extracted methods document what each block does.

// ── Parameter count: max 3-4 params ─────────────────────────────
// DON'T:
void createUser(String name, String email, int age,
                String address, String phone, String role) { }

// DO — use a parameter object:
void createUser(CreateUserRequest request) { }

// ── Return early to avoid deep nesting ───────────────────────────
// DON'T — pyramid of doom:
public String getDiscount(User user) {
    if (user != null) {
        if (user.isPremium()) {
            if (user.getYearsActive() > 5) {
                return "20%";
            }
        }
    }
    return "0%";
}

// DO — guard clauses:
public String getDiscount(User user) {
    if (user == null) return "0%";
    if (!user.isPremium()) return "0%";
    if (user.getYearsActive() <= 5) return "0%";
    return "20%";
}

Exception Handling Standards

Poor exception handling is one of the most common sources of hard-to-debug production bugs. These rules prevent the most common mistakes:
Java
// ── Never swallow exceptions silently ──────────────────────────
// DON'T:
try {
    processPayment(order);
} catch (Exception e) {
    // do nothing  ← this hides bugs and makes debugging impossible
}

// DO — at minimum, log it:
try {
    processPayment(order);
} catch (PaymentException e) {
    log.error("Payment failed for order {}: {}", order.getId(), e.getMessage(), e);
    throw new OrderProcessingException("Payment failed", e);
}

// ── Catch specific exceptions, not generic Exception ────────────
// DON'T:
catch (Exception e) { }           // catches everything — hides bugs

// DO:
catch (IOException e) { }         // handle I/O failures specifically
catch (SQLException e) { }        // handle DB failures specifically

// ── Always include the original exception as cause ───────────────
// DON'T:
catch (SQLException e) {
    throw new DataAccessException("DB error");  // loses stack trace
}

// DO:
catch (SQLException e) {
    throw new DataAccessException("DB error", e);  // preserves stack trace
}

// ── Use try-with-resources for closeable resources ───────────────
// DON'T:
Connection conn = dataSource.getConnection();
try {
    // use conn
} finally {
    conn.close();  // easy to forget, exception in finally hides original
}

// DO:
try (Connection conn = dataSource.getConnection()) {
    // conn is automatically closed when block exits
}

Code Quality Rules

These rules go beyond formatting — they're about writing code that's correct, efficient, and easy to maintain:
Java
// ── Prefer immutability ────────────────────────────────────────
// DON'T:
public class Config {
    public String host;   // mutable public field
    public int port;
}

// DO:
public final class Config {
    private final String host;
    private final int port;

    public Config(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public String getHost() { return host; }
    public int getPort() { return port; }
}

// ── Use Optional instead of returning null ───────────────────────
// DON'T:
public User findByEmail(String email) {
    return null;  // caller might forget to null-check → NPE
}

// DO:
public Optional<User> findByEmail(String email) {
    return Optional.ofNullable(userMap.get(email));
}
// Caller is forced to handle the absent case:
userService.findByEmail(email)
    .ifPresent(user -> sendWelcomeEmail(user));

// ── Favor composition over inheritance ──────────────────────────
// DON'T — deep inheritance for code reuse:
class Vehicle { }
class Car extends Vehicle { }
class ElectricCar extends Car { }
class SelfDrivingElectricCar extends ElectricCar { }  // deep and fragile

// DO — compose with interfaces:
class ElectricCar implements Vehicle, Electric, Autonomous { }

// ── Use constants for magic numbers ─────────────────────────────
// DON'T:
if (userAge >= 18 && transactionAmount > 50000) { }

// DO:
private static final int LEGAL_AGE = 18;
private static final double HIGH_VALUE_THRESHOLD = 50_000.0;

if (userAge >= LEGAL_AGE && transactionAmount > HIGH_VALUE_THRESHOLD) { }

Automated Enforcement — Tools That Check Your Code

Professional Java teams don't manually check every convention. They use tools that enforce standards automatically on every commit and build:
XML
<!-- Checkstyle — enforces formatting and naming conventions -->
<!-- Add to pom.xml: -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>3.3.0</version>
    <configuration>
        <!-- Google's Java style rules: -->
        <configLocation>google_checks.xml</configLocation>
        <failOnViolation>true</failOnViolation>
    </configuration>
</plugin>

<!-- SpotBugs — finds bug patterns: null dereferences, resource leaks -->
<plugin>
    <groupId>com.github.spotbugs</groupId>
    <artifactId>spotbugs-maven-plugin</artifactId>
    <version>4.7.3.6</version>
</plugin>

The One Rule That Overrides All Others

Every standard above has one exception: consistency within your codebase. If you're joining an existing project that uses 2-space indentation, or puts opening braces on new lines, or imports with wildcards — follow that project's conventions, not the general standard. Consistency within a codebase matters more than which specific convention you chose. The worst codebases aren't ones that chose unusual conventions. They're the ones where different developers followed different standards — so you never know what to expect when you open a new file. Agree as a team, document your choices, and enforce them with tools.