Java Code Review by trydofor
website·Indexed 31 days ago
https://java-code-review.moilioncircle.comA comprehensive guide to Java code quality, focusing on team rules, technical debt, and engineering practices. It covers coding styles, naming conventions, static and dynamic analysis (including CheckStyle), exception handling (NPE), side effects, and referential transparency. The site also features a 'Technical Debt' section analyzing real-world code examples and recommends resources like Effective Java 3rd Edition and the WingsBoot SpringBoot scaffold.
What's inside Java Code Review
- WingsBoot is a specialized scaffold for SpringBoot projects. More information can be found at https://wings.fessional.pro.
Separate business logic from MVC Controllers
To maintain a clean architecture, business logic and database operations should be placed in the Service layer. The Controller (MVC 'C' layer) should be restricted to data validation, data transformation, and orchestrating calls to the Service layer.Handle date and time calculations using Java 8+ Temporal API
Avoid treating date calculations as simple arithmetic (e.g., adding months and days manually) because date operations do not follow commutative laws. UseTemporalAdjustersand explicit methods likeminusMonths()instead ofplusMonths(-n). Ensure time-zone handling is centralized rather than manually adjusting hours (e.g., avoidminusHours(8)).final LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate .minusWeeks(4) .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)); final LocalDate firstDayOfYear = endDate.with(TemporalAdjusters.firstDayOfYear()); if (startDate.isBefore(firstDayOfYear)) { startDate = firstDayOfYear; }Define explicit return types for public API interfaces
Avoid using wildcard return types likeR<?>in public controller methods. Explicitly defining the return type ensures that Swagger can automatically generate accurate API documentation and guarantees data consistency during deserialization.// Avoid public R<?> getLineChartWeeks() { ... } // Prefer public R<LineChartWeeksResponse> getLineChartWeeks() { ... }Replace long parameter lists with Request Objects
Avoid controllers with long lists of
@RequestParamarguments. Instead:- Use
@RequestBody BizIns insto encapsulate business data. - Use
@PageDefault(size = 20) PageQuery pqfor query string parameters to leverage alias functionality. - Use primitive
booleaninstead ofBooleanfor mandatory flags to prevent null values. - Use JSON wrapping for business data to better support the parsing of enums and date-time types.
- Use
Improve code readability through variable extraction
Enhance the clarity of business logic by extracting frequently used method calls into local variables. For example, instead of callingpo.getStatus()multiple times within complexifstatements, extract the value into afinalvariable to ensure immutability and improve conciseness.Use @Param.Out and @Param.InOut to signal parameter mutation
Use@Param.Outand@Param.InOutannotations on method parameters to explicitly warn the caller that the passed object/collection will be modified within the method (similar toinandoutmodifiers in C#).void create( @NotNull OwtLadingMain owtLadingMain, @Param.Out List<OwtLadingOrder> owtLadingOrdersOut, @Param.InOut List<OwtLadingExpress> owtLadingExpressesInOut );Manage class members and inheritance depth
To preserve the Single Responsibility Principle and encapsulation:
- Class Members: Limit the number of fields and methods to approximately 10. Members following the same pattern (e.g., polymorphic methods or similar rule injections) can be grouped as one type.
- Inheritance: Limit inheritance depth to 3 generations (excluding framework interfaces). This avoids excessive
Overridechains and deepsuper.invoke()calls.
Choose the appropriate method for object cloning and DTO mapping
When copying values between DTOs or cloning objects, use the following priority (from highest to lowest efficiency/safety):
- MapStruct: Strong typing, compile-time static assignment.
- BeanUtil: Weak typing, runtime reflection-based assignment or shallow clone.
- Serialization/Deserialization: Weak typing, deep copy (Kryo is preferred over JSON).
Optimize Cacheable and JOOQ query implementation
When implementing cached data retrieval and JOOQ queries:
- Use predefined constants for
@Cacheable(value = ...)instead of hardcoded strings. - Use
StateAssert.notNull()forCodeExceptionchecks instead ofOptionalin non-reactive flows. - Use Lombok's
@Slf4jfor logging. - In JOOQ, avoid using static table instances; use the DAO with lambda expressions for simple queries to ensure correct alias handling.
@Cacheable(value = UserDetail.CacheNameDetail) public OwtUserDetail getByUserIdFromCache(Long id) { log.debug("find from db"); final OwtUserDetail detail = owtUserDetailDao.fetchOne(t -> t.onlyLive(t.UserId.eq(id))); StateAssert.notNull(detail, CommonErrorEnum.AssertNotFound1, id); return detail; }- Use predefined constants for
Optimize SLF4J logging to avoid unnecessary serialization
Avoid calling expensive methods likeJSONObject.toJSONString()directly inside a logger call. Because the method executes before the logger checks if the log level is enabled, it causes unnecessary performance overhead and memory fragmentation. Use anif (logger.isInfoEnabled())guard or the Fluent API with a lambda for lazy evaluation.// Recommended: Guard with log level check if (logger.isInfoEnabled()) { logger.info("truck rate {}", JSONObject.toJSONString(request)); } // Alternative: Fluent API with lambda (SLF4J 2.0+) logger.atInfo() .setMessage("truck rate {}") .addArgument(() -> JSONObject.toJSONString(request)) .log();Implement Static Code Analysis for Java projects
Static Code Analysis is recommended to eliminate approximately 80% of low-quality code. The following tools are recommended for static analysis: CheckStyle, SpotBugs (formerly FindBugs), and PMD. When using multiple tools or rules that conflict, the team's established rules should take precedence over tool defaults.