Java
SkillSecurityModern Java practices: design, errors, concurrency, security, testing, tooling. Targets Java 21 LTS baseline; Java 25 LTS features called out explicitly. Use when writing or reviewing Java code.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Java skill
What this skill tells your AI
The instructions your AI receives, as published by sumonmselim/agentguard in skills/java/SKILL.md and read by ahel’s review.
Design
- Composition over inheritance. Extend only for true is-a relationships
- Immutability by default.
finalfields, no setters unless mutation required - Records for data carriers (16+). No boilerplate POJOs
- Sealed classes for closed type hierarchies (17+). Use exhaustive
switchexpressions over them - Pattern matching
instanceof(16+):if (obj instanceof String s)— no explicit cast - Small interfaces. One concern per interface
- Factory methods or builders over telescoping constructors
- No
nullin public APIs.Optional<T>for absent values. NeverOptionalas field type Objects.requireNonNull(param, "param")at method entry for non-null enforcement
Errors
- Checked exceptions for recoverable conditions callers must handle. Unchecked for programming errors
- Never catch
ExceptionorThrowableexcept at boundaries (HTTP handler, queue consumer) - Log full stack trace. Never swallow exceptions silently
- try-with-resources for all
Closeable. No manualfinallyclose blocks - Catch specific exceptions. Never use exceptions for flow control
Modern Java
varwhere type is obvious from right-hand side. Not to obscure types- Streams for transformation pipelines.
Stream.toList()(16+) overCollectors.toList() List.of(),Map.of(),Set.of()for immutable collections.List.copyOf()for defensive copy- Sequenced collections (21+):
SequencedCollection,getFirst(),getLast()over index hacks switchexpressions over statements. Pattern matching inswitch(21+) with exhaustive coverage- Unnamed patterns and variables (22+, stable 25):
catch (IOException _),case Point(int x, _) - Record patterns (21+):
if (obj instanceof Point(int x, int y)) - Text blocks for multiline strings (SQL, JSON, HTML). No string concat across lines
- Stream gatherers (22+, stable 25):
stream.gather(...)for custom intermediate operations instanceofchecks before casts eliminated — use pattern matching everywhere
Concurrency
- Virtual threads (21+):
Thread.ofVirtual().start(...)orExecutors.newVirtualThreadPerTaskExecutor(). Use for I/O-bound work - Never
synchronizedorObject.wait/notifywith virtual threads — usejava.util.concurrentprimitives or structured concurrency - Structured concurrency (21+ preview, stable 25):
StructuredTaskScopefor fan-out with automatic cancellation and error propagation - Scoped values (21+ preview, stable 25):
ScopedValueoverThreadLocalfor virtual thread-safe immutable context CompletableFuturefor async pipelines without virtual threads. Avoidget()without timeoutExecutorServicealways in try-with-resources (19+) or explicitly shut down- Immutable shared state by default.
volatileonly when you understand happens-before - No
Thread.sleep()in production logic. No busy-wait loops
Security
- Parameterized queries only. Never string-concatenated SQL
- Validate and sanitize all external input. Bean Validation (
@NotNull,@Size, etc.) at API boundaries - Never log sensitive data: passwords, tokens, PII, session IDs
SecureRandomfor tokens and secrets. Neverjava.util.Random- No Java serialization for untrusted data. Use JSON or Protobuf with schema validation
- TLS: never disable certificate validation. Never catch
SSLExceptionand continue govulncheckequivalent: OWASP Dependency-Check or Snyk in CI. Fail on critical CVEs- Cryptography: use JCA standard algorithms. No homebrew crypto. Prefer
AES/GCMoverAES/CBC - Quantum-resistant algorithms available in Java 25 (
ML-KEM,ML-DSA) — evaluate for long-lived key material
Logging
- SLF4J API + Logback or Log4j2 implementation. Never
System.out.printlnin production - Structured logging (JSON) for machine-parseable output in production
- MDC (Mapped Diagnostic Context) for request-scoped fields (trace ID, user ID, tenant)
- Log levels:
ERRORfor actionable failures,WARNfor degraded state,INFOfor lifecycle events,DEBUGfor dev only - Parameterized log messages:
log.debug("user={}", userId)— never string concat in log args - Never log full stack traces at
WARNorINFO. Stack traces atERRORonly
Testing
- JUnit 5 + AssertJ. Arrange-Act-Assert. One concept per test
@ParameterizedTestfor data-driven cases- Mockito for unit doubles. Never mock value objects or records
- Testcontainers for database and external service integration tests
- No
Thread.sleep()in tests. Awaitility for async assertions - Mutation testing with PIT for critical business logic
@Nestedfor grouping related test cases within one class- Build tag separation: unit tests in
src/test, integration tests insrc/integrationTest(Gradle) or profiles (Maven)
Tooling
- Build: Maven or Gradle. Maven BOM for dependency version management. Gradle version catalogs (
libs.versions.toml) - Formatter:
google-java-formatorpalantir-java-formatenforced in CI. No style debates - Static analysis:
SpotBugs+Find Security Bugsplugin.Checkstylefor style enforcement ErrorPronecompiler plugin for correctness checks at compile timeArchUnitfor architectural rules: enforce layer boundaries, naming conventions in tests- JDK selection:
sdkmanor.sdkmanrcfor team-consistent JDK version. Pin distribution (Temurin preferred) - GraalVM native image: profile startup vs throughput tradeoff before adopting. Reflection config required
Performance
- Profile before optimizing. JMC (JDK Mission Control) or async-profiler
- Virtual threads remove the need to pool threads for I/O — size thread pools for CPU-bound work only
- Connection pooling (HikariCP) for DB. Never create connections per request
- Avoid excessive allocation in hot paths. Measure GC pressure with JFR (Java Flight Recorder)
StringBuilderfor string concat in loops.String.join()for fixed listsArrayListoverLinkedListfor most cases.ArrayDequeoverStackorLinkedListas queueList.copyOf()/Map.copyOf()create truly immutable snapshots. Use overCollections.unmodifiableList
Signals
- GitHub stars
- 56
- Forks
- 11
- Last commit
- Jul 2026
Advanced
- Catalog kind
- skill
- Gateway key
java-sumonmselim- Source
- github.com/sumonmselim/agentguard