java-guide

Applies to: Java 17+, Spring Boot, Maven/Gradle, Enterprise Applications

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "java-guide" with this command: npx skills add ar4mirez/samuel/ar4mirez-samuel-java-guide

Java Guide

Applies to: Java 17+, Spring Boot, Maven/Gradle, Enterprise Applications

Core Principles

  • Immutability by Default: Prefer records, final fields, and unmodifiable collections

  • Explicit Over Implicit: Clear type declarations, no raw types, no unchecked casts

  • Fail Fast: Validate inputs at boundaries, use Objects.requireNonNull liberally

  • Composition Over Inheritance: Favor delegation and interfaces over deep class hierarchies

  • Standard Library First: Use java.util , java.time , java.nio before adding dependencies

Guardrails

Version & Dependencies

  • Use Java 17+ (LTS) with preview features disabled in production

  • Manage dependencies with Maven (pom.xml ) or Gradle (build.gradle.kts )

  • Pin dependency versions explicitly (no dynamic versions like 1.+ )

  • Run mvn dependency:analyze or gradle dependencies to detect unused/undeclared deps

  • Check for vulnerabilities: mvn org.owasp:dependency-check-maven:check

Code Style

  • Follow Google Java Style Guide

  • Classes: PascalCase | Methods/fields: camelCase | Constants: UPPER_SNAKE_CASE

  • Packages: com.company.project.module (lowercase, no underscores)

  • One top-level class per file (name matches filename)

  • Use var for local variables only when the type is obvious from the right side

  • No wildcard imports (import java.util.* is forbidden)

Records & Sealed Classes

  • Use record for immutable data carriers instead of manual POJOs

  • Use sealed classes/interfaces for restricted type hierarchies

  • Add compact constructors for validation in records

  • Sealed classes enable exhaustive switch expressions with pattern matching

public record UserId(String value) { public UserId { Objects.requireNonNull(value, "UserId must not be null"); if (value.isBlank()) { throw new IllegalArgumentException("UserId must not be blank"); } } }

public sealed interface PaymentResult permits PaymentResult.Success, PaymentResult.Declined, PaymentResult.Error { record Success(String transactionId, BigDecimal amount) implements PaymentResult {} record Declined(String reason) implements PaymentResult {} record Error(Exception cause) implements PaymentResult {} }

Streams & Optional

  • Use streams for transformations, not for side effects

  • Never call Optional.get() -- use orElseThrow() , orElse() , map() , flatMap()

  • Do not use Optional as a method parameter or field type (only as a return type)

  • Avoid parallel streams unless measured to be faster (overhead is real)

  • Prefer toList() (Java 16+) over collect(Collectors.toList())

List<String> activeEmails = users.stream() .filter(User::isActive) .map(User::email) .toList();

String displayName = userRepository.findById(id) .map(User::displayName) .orElseThrow(() -> new UserNotFoundException(id));

Resource Management

  • Always use try-with-resources for AutoCloseable types

  • Never rely on finalize() (deprecated and unreliable)

  • Close resources in reverse order of acquisition

try (var connection = dataSource.getConnection(); var statement = connection.prepareStatement(sql); var resultSet = statement.executeQuery()) { while (resultSet.next()) { results.add(mapRow(resultSet)); } }

Project Structure

myproject/ ├── pom.xml ├── src/ │ ├── main/ │ │ ├── java/com/company/project/ │ │ │ ├── Application.java # Entry point │ │ │ ├── config/ # Configuration classes │ │ │ ├── controller/ # REST controllers / API layer │ │ │ ├── service/ # Business logic │ │ │ ├── repository/ # Data access │ │ │ ├── model/ # Domain entities and records │ │ │ ├── dto/ # Data transfer objects (records) │ │ │ └── exception/ # Custom exceptions │ │ └── resources/ │ │ ├── application.yml │ │ └── db/migration/ # Flyway/Liquibase migrations │ └── test/java/com/company/project/ # Mirrors main structure └── target/ # Build output (gitignored)

  • Packages map to bounded contexts (not technical layers at the top)

  • Test structure mirrors source structure

  • Keep resources/ flat; use db/migration/ for schema changes

Key Patterns

Sealed Classes with Pattern Matching

public static double area(Shape shape) { return switch (shape) { case Shape.Circle c -> Math.PI * c.radius() * c.radius(); case Shape.Rectangle r -> r.width() * r.height(); case Shape.Triangle t -> 0.5 * t.base() * t.height(); }; }

Immutable Collections

List<String> roles = List.of("ADMIN", "USER", "GUEST"); Map<String, String> config = Map.ofEntries( Map.entry("host", "localhost"), Map.entry("port", "8080")); List<Item> snapshot = List.copyOf(mutableList);

Text Blocks

String query = """ SELECT u.id, u.email, u.created_at FROM users u WHERE u.active = true ORDER BY u.created_at DESC LIMIT ? """;

Optional Chaining

// Fallback chain: cache -> database -> remote User user = cache.findUser(id) .or(() -> database.findUser(id)) .or(() -> remoteService.fetchUser(id)) .orElseThrow(() -> new UserNotFoundException(id));

// Conditional execution without get() userRepository.findById(userId) .ifPresentOrElse( u -> log.info("Found user: {}", u.name()), () -> log.warn("User {} not found", userId));

Testing

Standards

  • Test files mirror source under src/test/java

  • Test class names: ClassNameTest (not TestClassName )

  • Test methods: @Test void shouldDescribeBehavior()

  • Use @DisplayName for complex scenarios, @Nested to group related tests

  • Coverage target: >80% for business logic, >60% overall

  • Prefer AssertJ over JUnit assertions for readability

Basic Test Structure

class UserServiceTest {

private UserRepository userRepository;
private UserService userService;

@BeforeEach
void setUp() {
    userRepository = mock(UserRepository.class);
    userService = new UserService(userRepository);
}

@Test
void shouldReturnUserWhenFound() {
    var expected = new User("1", "alice@example.com", "Alice");
    when(userRepository.findById("1")).thenReturn(Optional.of(expected));

    User result = userService.getUser("1");

    assertThat(result).isEqualTo(expected);
    verify(userRepository).findById("1");
}

@Test
void shouldThrowWhenUserNotFound() {
    when(userRepository.findById("999")).thenReturn(Optional.empty());

    assertThatThrownBy(() -> userService.getUser("999"))
            .isInstanceOf(UserNotFoundException.class)
            .hasMessageContaining("999");
}

}

Parameterized Tests

@ParameterizedTest @CsvSource({ "alice@example.com, true", "bob@test.org, true", "invalid-email, false", "'', false", }) void shouldValidateEmail(String email, boolean expected) { assertThat(EmailValidator.isValid(email)).isEqualTo(expected); }

@ParameterizedTest @MethodSource("provideMoneyAdditions") void shouldAddMoneySameCurrency(Money a, Money b, Money expected) { assertThat(a.add(b)).isEqualTo(expected); }

static Stream<Arguments> provideMoneyAdditions() { var usd = Currency.getInstance("USD"); return Stream.of( Arguments.of(new Money(BigDecimal.ONE, usd), new Money(BigDecimal.TEN, usd), new Money(new BigDecimal("11"), usd)) ); }

Tooling

Maven Commands

mvn clean install # Build and install locally mvn test # Run all tests mvn verify # Run tests + integration tests mvn dependency:tree # Show dependency tree mvn dependency:analyze # Find unused/undeclared deps mvn spotbugs:check # Static analysis mvn checkstyle:check # Style check

Gradle Commands

./gradlew build # Full build with tests ./gradlew test # Run unit tests ./gradlew check # Run all verification tasks ./gradlew dependencies # Show dependency tree ./gradlew jacocoTestReport # Generate coverage report

References

For detailed patterns and examples, see:

  • references/patterns.md -- Spring patterns, Stream API recipes, Optional chaining, builder patterns

External References

  • Google Java Style Guide

  • Effective Java (3rd Edition) -- Joshua Bloch

  • Java Records (JEP 395)

  • Sealed Classes (JEP 409)

  • Pattern Matching for switch (JEP 441)

  • JUnit 5 User Guide

  • AssertJ Documentation

  • SpotBugs

  • Checkstyle

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

actix-web

No summary provided by upstream source.

Repository SourceNeeds Review
General

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

blazor

No summary provided by upstream source.

Repository SourceNeeds Review
General

web-artifacts-builder

No summary provided by upstream source.

Repository SourceNeeds Review