java-coding-guideline
Java Coding Guideline
CRITICAL: These rules apply to ALL Java code unless explicitly overridden by more specific skills.
Import Statements
Always use explicit imports without wildcards for better code clarity and maintainability.
✅ Do
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
❌ Don't
import static org.assertj.core.api.Assertions.*;
Vertical Spacing (Readability)
Use consistent vertical spacing inside classes to make diffs smaller and code easier to scan.
Rule
- Insert exactly one blank line between:
- field blocks and the next field block (when grouped)
- fields and the first constructor
- constructors and methods
- methods and the next method
- Also insert one blank line right after the opening
{of a class (before the first field/initializer), unless the class is intentionally empty.
✅ Do
public final class SomeClass {
@NonNull
private final String fieldA;
@NonNull
private final String fieldB;
public void someFunc() {
// some action
}
public String getFieldA() {
return fieldA;
}
}
❌ Don't
public final class SomeClass {
@NonNull
private final String fieldA;
@NonNull
private final String fieldB;
public void someFunc() {
// some action
}
public String getFieldA() {
return fieldA;
}
}
Constructors
Use a single primary constructor and delegate auxiliary constructors using this().
✅ Do
public final class SomeClass {
private final int field1;
private final int field2;
public SomeClass() {
this(1, 2);
}
public SomeClass(int field1, int field2) {
this.field1 = field1;
this.field2 = field2;
}
}
❌ Don't
public final class SomeClass {
private final int field1;
private final int field2;
public SomeClass() {
this.field1 = 1;
this.field2 = 2;
}
public SomeClass(int field1, int field2) {
this.field1 = field1;
this.field2 = field2;
}
}
Optional Usage
Avoid using Optional as return values or parameters. Use @Nullable annotations instead.
✅ Do
public final class SomeClass {
@Nullable
public Integer someNullable() {
if (something) return null;
return 1;
}
public void func(@Nullable Integer param) {
// do something
}
}
❌ Don't
public final class SomeClass {
@NonNull
public Optional<Integer> someNullable() {
if (something) return Optional.ofNullable(null);
return Optional.ofNullable(1);
}
public void func(@NonNull Optional<Integer> param) {
// do something
}
}
Wrapper vs Primitive Types
- Use wrapper types when
nullis expected - Use primitive types when
nullshould not occur
✅ Do
public final class SomeClass {
private final int field1;
@Nullable
private final Long field2;
public SomeClass(int field1, @Nullable Long field2) {
this.field1 = field1;
this.field2 = field2;
}
}
❌ Don't
public final class SomeClass {
@NonNull
private final Integer field1;
@Nullable
private final Long field2;
public SomeClass(@NonNull Integer field1, @Nullable Long field2) {
this.field1 = field1;
this.field2 = field2;
}
}
Null Comparison
- Use
A == nullfor general cases - Use
Objects.isNullfor Stream API or functional programming contexts
✅ Do
final Integer someVariable = null;
if (someVariable == null) {
// do something
}
List<Integer> lists = new ArrayList<>();
lists.stream()
.filter(Objects::isNull)
.collect(Collectors.toList());
❌ Don't
final Integer someVariable = null;
if (Objects.isNull(someVariable)) {
// do something
}
List<Integer> lists = new ArrayList<>();
lists.stream()
.filter(p -> p == null)
.collect(Collectors.toList());
Nullability Annotations
- Use JSpecify annotations
- Apply
@Nullableand@NonNullto all methods including private ones - Avoid class-level or package-level annotations (Lombok compatibility issues)
✅ Do
public final class SomeClass {
@Nullable
private final Integer field1;
@NonNull
private final String field2;
@NonNull
public String func() {
return process();
}
@Nullable
private String process() {
if (field1 == null) return null;
return "awesome";
}
}
❌ Don't
public final class SomeClass {
private final Integer field1;
private final String field2;
public String func() {
return process();
}
private String process() {
if (field1 == null) return null;
return "awesome";
}
}
Package Naming
Use all lowercase and numbers only, following Google Java Style Guide conventions.
✅ Do
package com.example.project.initscript;
❌ Don't
package com.example.project.initScript;
Static Factory Methods
Use only of naming convention. For multiple types, use format of<Postfix>.
✅ Do
public final class SomeClass {
private final int field1;
private SomeClass(int field1) {
this.field1 = field1;
}
public static SomeClass ofDefault() {
return new SomeClass(1);
}
public static SomeClass ofSpecial() {
return new SomeClass(2);
}
}
❌ Don't
public final class SomeClass {
private final int field1;
private SomeClass(int field1) {
this.field1 = field1;
}
public static SomeClass from() {
return new SomeClass(1);
}
public static SomeClass valueOf() {
return new SomeClass(2);
}
}
Resource Management
Always use try-with-resources for AutoCloseable resources. Never rely on finalizers.
Try-With-Resources
✅ Do
public String readFile(@NonNull Path path) throws IOException {
try (var reader = Files.newBufferedReader(path)) {
return reader.lines().collect(Collectors.joining("\n"));
}
}
// Multiple resources
public void copyStream(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
try (in; out) { // Java 9+: can use already-declared variables
in.transferTo(out);
}
}
❌ Don't
public String readFile(@NonNull Path path) throws IOException {
BufferedReader reader = Files.newBufferedReader(path);
try {
return reader.lines().collect(Collectors.joining("\n"));
} finally {
reader.close(); // The original exception may be suppressed if one occurs here
}
}
Composition over Inheritance
Prefer composition over inheritance.
✅ Do
public final class SomeFeature {
public void func() { /* ... */ }
}
public final class SomeClass {
private final SomeFeature feature;
public void func2() {
feature.func();
// do something
}
}
❌ Don't
public class SomeFeature {
public void func() { /* ... */ }
}
public final class SomeClass extends SomeFeature {
public void func2() {
super.func();
// do something
}
}
Immutable Objects
All objects should use final keyword unless needed for extension or framework requirements.
✅ Do
public final class SomeClass {
// ...
}
❌ Don't
public class SomeClass {
// ...
}
Exception Catching
Catch only the minimum necessary exceptions.
✅ Do
try {
// do something
} catch (IllegalArgumentException e) {
// do catch
}
❌ Don't
try {
// do something
} catch (RuntimeException e) {
// do catch
}
Implementing AutoCloseable
When implementing AutoCloseable:
- Make
close()idempotent (safe to call multiple times) - Never throw exceptions from
close()(suppressed exceptions problem) - Log errors instead of throwing them
✅ Do
public final class DatabaseConnection implements AutoCloseable {
@NonNull
private final Connection connection;
private volatile boolean closed = false;
@Override
public void close() {
if (closed) return; // Idempotent: safe to call multiple times
closed = true;
try {
connection.close();
} catch (SQLException e) {
// Log only; do not throw (exceptions in close can suppress the original)
logger.warn("Failed to close connection", e);
}
}
}
❌ Don't
public final class DatabaseConnection implements AutoCloseable {
@NonNull
private final Connection connection;
@Override
public void close() throws SQLException {
connection.close(); // Not idempotent; exception propagates
}
}
Date/Time API
Use java.time (JSR-310) exclusively. java.util.Date and java.util.Calendar are banned in new code.
Type Selection
| Use Case | Type | Example |
|---|---|---|
| Storage/transfer (UTC) | Instant |
API timestamps, DB storage |
| User display | ZonedDateTime |
Time shown in UI |
| Date only | LocalDate |
Birthdays, expiration dates |
| Time only | LocalTime |
Business hours, alarms |
| Duration | Duration / Period |
Elapsed time, date differences |
✅ Do
public final class Event {
@NonNull
private final Instant createdAt; // UTC timestamp
@NonNull
private final LocalDate eventDate; // Date only
@NonNull
private final ZoneId timeZone; // Display time zone
@NonNull
public ZonedDateTime displayTime() {
return createdAt.atZone(timeZone);
}
}
Formatter reuse (thread-safe):
public final class DateFormats {
// DateTimeFormatter is thread-safe, so reuse as constants
public static final DateTimeFormatter ISO_DATE = DateTimeFormatter.ISO_LOCAL_DATE;
public static final DateTimeFormatter ENGLISH_DATE = DateTimeFormatter.ofPattern("MMM dd, yyyy");
private DateFormats() {}
}
❌ Don't
public final class Event {
@NonNull
private final Date createdAt; // Do not use java.util.Date
@NonNull
private final Calendar calendar; // Do not use java.util.Calendar
@NonNull
public String format() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // Not thread-safe
return sdf.format(createdAt);
}
}
Legacy Interop (boundary only)
When interfacing with legacy APIs that require Date, convert at the boundary:
// Instant → Date (boundary layer only)
Date legacyDate = Date.from(instant);
// Date → Instant (convert immediately on receipt)
Instant instant = legacyDate.toInstant();
Additional Guidelines
Builder Pattern
Use Lombok-style builders for API consistency.
Variable and Method Naming
- Use
varkeyword from JVM 10+ - Use meaningful variable names
- But avoid too long variable, function, field names
- Method names should be verbs
- Avoid using 'get' prefix except for getters
Camel Case Convention
Apply camel case to mixed-case words and abbreviations (e.g., "MultiNIC" becomes "MultiNic").
Control Flow Statements
Use one-liner syntax for simple if statements without braces.
✅ Do
if (someCondition) doSomething();
❌ Don't
if (someCondition) {
doSomething();
}
Comments
- Use comments only in special cases
- TODO comments should be for immediate tasks
- Avoid unnecessary comments that restate the code
See Also
Reference Guides
For deeper understanding of specific topics:
- references/async.md - CompletableFuture and async programming patterns
- references/javadoc.md - Javadoc conventions and exception documentation
- references/lombok.md - Allowed and banned Lombok annotations
- references/security-resources.md - Security best practices, password handling, SQL injection prevention, PII masking