java
8
总安装量
7
周安装量
#35176
全站排名
安装命令
npx skills add https://github.com/nguyenhuuca/assessment --skill java
Agent 安装分布
mcpjam
7
claude-code
7
replit
7
junie
7
windsurf
7
zencoder
7
Skill 文档
Java Development
Project Setup
# Maven project
mvn archetype:generate -DgroupId=com.example -DartifactId=my-app \
-DarchetypeArtifactId=maven-archetype-quickstart
# Spring Boot project
curl https://start.spring.io/starter.zip \
-d dependencies=web,data-jpa,postgresql \
-d type=maven-project \
-d javaVersion=24 \
-o project.zip
pom.xml (Java 24 with Preview Features)
<properties>
<java.version>24</java.version>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>24</release>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
</plugins>
</build>
Modern Java Features
Records (Java 14+)
// Immutable data class
public record User(Long id, String email, String name) {
// Compact constructor for validation
public User {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
}
}
// Usage
User user = new User(1L, "test@example.com", "John");
String email = user.email(); // Getter auto-generated
Pattern Matching (Java 21+)
// Pattern matching in switch
String format(Object obj) {
return switch (obj) {
case Integer i -> "int %d".formatted(i);
case String s when s.length() > 10 -> "long string: %s".formatted(s);
case String s -> "short string: %s".formatted(s);
case null -> "null";
default -> obj.toString();
};
}
// Pattern matching in instanceof
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
}
Virtual Threads (Java 21+)
// Enable in Spring Boot
@Configuration
public class ThreadConfig {
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}
// Manual usage
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = urls.stream()
.map(url -> executor.submit(() -> fetchData(url)))
.toList();
for (Future<String> future : futures) {
String result = future.get();
}
}
StructuredTaskScope (Java 21+ Preview)
// Structured concurrency
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> fetchUser(userId));
Future<List<Order>> orders = scope.fork(() -> fetchOrders(userId));
scope.join(); // Wait for all tasks
scope.throwIfFailed(); // Propagate errors
return new UserProfile(user.resultNow(), orders.resultNow());
}
Text Blocks (Java 15+)
String json = """
{
"name": "%s",
"age": %d,
"email": "%s"
}
""".formatted(name, age, email);
String sql = """
SELECT u.id, u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.status = 'ACTIVE'
""";
Spring Boot Patterns
Controller with Validation
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<UserDto> create(@Valid @RequestBody CreateUserRequest request) {
UserDto user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@GetMapping("/{id}")
public ResponseEntity<UserDto> getById(@PathVariable @Positive Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
Service Layer
@Service
@Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Override
public UserDto create(CreateUserRequest request) {
// Validation
if (userRepository.existsByEmail(request.email())) {
throw new DuplicateEmailException("Email already exists");
}
// Business logic
User user = User.builder()
.email(request.email())
.password(passwordEncoder.encode(request.password()))
.createdAt(Instant.now())
.build();
User saved = userRepository.save(user);
return mapToDto(saved);
}
@Override
@Transactional(readOnly = true)
public Optional<UserDto> findById(Long id) {
return userRepository.findById(id)
.map(this::mapToDto);
}
}
Repository with Custom Query
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByEmail(String email);
Optional<User> findByEmail(String email);
@Query("""
SELECT u FROM User u
WHERE u.status = :status
AND u.createdAt > :since
ORDER BY u.createdAt DESC
""")
Page<User> findActiveUsers(
@Param("status") UserStatus status,
@Param("since") Instant since,
Pageable pageable
);
@Modifying
@Query("UPDATE User u SET u.lastLogin = :now WHERE u.id = :id")
void updateLastLogin(@Param("id") Long id, @Param("now") Instant now);
}
Exception Handling
Custom Exception
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// Global exception handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
Instant.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
FieldError::getDefaultMessage
));
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"Validation failed",
errors,
Instant.now()
);
return ResponseEntity.badRequest().body(error);
}
}
Testing
Unit Test with JUnit 5 & Mockito
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks
private UserServiceImpl userService;
@Test
void create_ValidRequest_ReturnsUser() {
// Given
CreateUserRequest request = new CreateUserRequest("test@example.com", "password");
User savedUser = User.builder()
.id(1L)
.email(request.email())
.build();
when(userRepository.existsByEmail(request.email())).thenReturn(false);
when(passwordEncoder.encode(request.password())).thenReturn("encoded");
when(userRepository.save(any(User.class))).thenReturn(savedUser);
// When
UserDto result = userService.create(request);
// Then
assertThat(result.email()).isEqualTo("test@example.com");
verify(userRepository).save(any(User.class));
}
@Test
void create_DuplicateEmail_ThrowsException() {
// Given
CreateUserRequest request = new CreateUserRequest("test@example.com", "password");
when(userRepository.existsByEmail(request.email())).thenReturn(true);
// When & Then
assertThrows(DuplicateEmailException.class, () -> userService.create(request));
verify(userRepository, never()).save(any());
}
}
Integration Test with TestContainers
@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb");
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void findByEmail_ExistingUser_ReturnsUser() {
// Given
User user = User.builder()
.email("test@example.com")
.password("password")
.build();
userRepository.save(user);
// When
Optional<User> found = userRepository.findByEmail("test@example.com");
// Then
assertThat(found).isPresent();
assertThat(found.get().getEmail()).isEqualTo("test@example.com");
}
}
REST API Test with MockMvc
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void getById_ExistingUser_ReturnsOk() throws Exception {
// Given
UserDto user = new UserDto(1L, "test@example.com", "Test User");
when(userService.findById(1L)).thenReturn(Optional.of(user));
// When & Then
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value("test@example.com"))
.andExpect(jsonPath("$.name").value("Test User"));
}
@Test
void create_InvalidEmail_ReturnsBadRequest() throws Exception {
// Given
String requestBody = """
{
"email": "invalid-email",
"password": "password"
}
""";
// When & Then
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isBadRequest());
}
}
Best Practices
Use Optional for Nullable Returns
// Good
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
// Bad
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
Use Builder Pattern with Lombok
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String password;
@Builder.Default
private Instant createdAt = Instant.now();
}
// Usage
User user = User.builder()
.email("test@example.com")
.password("encoded")
.build();
Validation with Jakarta Bean Validation
public record CreateUserRequest(
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
String email,
@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
String password
) {}
Tooling
# Maven commands
mvn clean install # Build project
mvn test # Run tests
mvn verify # Run tests + integration tests
mvn jacoco:report # Generate coverage report
# Run with preview features
mvn clean package
java --enable-preview -jar target/app.jar
# Spring Boot
mvn spring-boot:run # Run application
mvn spring-boot:build-image # Build Docker image
Common Dependencies
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- TestContainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>