| name | java-code-review |
| description | Use when performing comprehensive code reviews on Java/Spring Boot code, checking for bugs, performance issues, security vulnerabilities, and adherence to best practices. |
Java Code Review Skill
You are an expert Java code reviewer. You systematically evaluate code for correctness, performance, security, and maintainability.
Review Categories
1. Correctness
Check for common bugs:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product other)) return false;
return Objects.equals(id, other.id);
}
@Column(nullable = false, unique = true)
private String sku;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product other)) return false;
return Objects.equals(sku, other.sku);
}
@Override
public int hashCode() {
return Objects.hash(sku);
}
}
2. Null Safety
public String getCustomerCity(Order order) {
return order.getCustomer().getAddress().getCity();
}
public Optional<String> getCustomerCity(Order order) {
return Optional.ofNullable(order)
.map(Order::getCustomer)
.map(Customer::getAddress)
.map(Address::getCity);
}
public void processOrder(Order order) {
Objects.requireNonNull(order, "Order must not be null");
Objects.requireNonNull(order.getCustomer(), "Customer must not be null");
}
3. Resource Management
public String readFile(Path path) throws IOException {
var reader = new BufferedReader(new FileReader(path.toFile()));
return reader.readLine();
}
public String readFile(Path path) throws IOException {
try (var reader = Files.newBufferedReader(path)) {
return reader.readLine();
}
}
public List<String> getNames(DataSource ds) throws SQLException {
var conn = ds.getConnection();
var stmt = conn.prepareStatement("SELECT name FROM users");
var rs = stmt.executeQuery();
var names = new ArrayList<String>();
while (rs.next()) names.add(rs.getString("name"));
return names;
}
public List<String> getNames(DataSource ds) throws SQLException {
try (var conn = ds.getConnection();
var stmt = conn.prepareStatement("SELECT name FROM users");
var rs = stmt.executeQuery()) {
var names = new ArrayList<String>();
while (rs.next()) names.add(rs.getString("name"));
return names;
}
}
4. Thread Safety
@Service
public class CounterService {
private int count = 0;
public int increment() {
return ++count;
}
}
@Service
public class CounterService {
private final AtomicInteger count = new AtomicInteger(0);
public int increment() {
return count.incrementAndGet();
}
}
@Service
public class CacheService {
private final Map<String, Object> cache = new HashMap<>();
public void put(String key, Object value) {
cache.put(key, value);
}
}
@Service
public class CacheService {
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
public void put(String key, Object value) {
cache.put(key, value);
}
}
5. Performance
@Transactional(readOnly = true)
public List<OrderResponse> getAllOrders() {
var orders = orderRepository.findAll();
return orders.stream()
.map(order -> new OrderResponse(
order.getId(),
order.getItems().size()))
.toList();
}
@Query("SELECT o FROM Order o JOIN FETCH o.items")
List<Order> findAllWithItems();
public String buildReport(List<String> lines) {
String result = "";
for (var line : lines) {
result += line + "\n";
}
return result;
}
public String buildReport(List<String> lines) {
return String.join("\n", lines);
}
6. Security
@Query(value = "SELECT * FROM users WHERE name = '" + name + "'", nativeQuery = true)
List<User> findByName(String name);
@Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true)
List<User> findByName(@Param("name") String name);
log.info("User login: email={}, password={}", email, password);
log.info("User login attempt: email={}", email);
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleError(Exception ex) {
return ResponseEntity.status(500).body(ex.toString());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiError> handleError(Exception ex) {
log.error("Unexpected error", ex);
return ResponseEntity.status(500).body(
new ApiError(Instant.now(), 500, "Internal Server Error",
"An unexpected error occurred", "", List.of()));
}
7. Exception Handling
try {
processPayment(order);
} catch (Exception e) {
log.error("Error", e);
}
try {
processPayment(order);
} catch (InsufficientFundsException e) {
log.warn("Insufficient funds for order: {}", order.getId());
throw new PaymentDeclinedException("Insufficient funds", e);
} catch (PaymentGatewayException e) {
log.error("Payment gateway error for order: {}", order.getId(), e);
throw new ServiceUnavailableException("Payment service temporarily unavailable", e);
}
try {
sendNotification(order);
} catch (NotificationException e) {
}
try {
sendNotification(order);
} catch (NotificationException e) {
log.warn("Failed to send notification for order: {}, will retry", order.getId(), e);
retryQueue.enqueue(new NotificationRetry(order.getId()));
}
Review Checklist
- Correctness: Logic errors, off-by-one, null derefs, equals/hashCode
- Null Safety: Proper use of Optional, null checks, @NonNull
- Resources: try-with-resources, connection leaks, stream closing
- Thread Safety: Mutable state in singletons, concurrent collections
- Performance: N+1 queries, string concatenation, unnecessary allocations
- Security: Injection, sensitive data logging, stack trace exposure
- Exceptions: Specific catches, no empty catch, proper wrapping
- Naming: Clear, consistent, following conventions
- Testing: Sufficient coverage, edge cases tested
- Documentation: Public API Javadoc, complex logic explained