java — Cursor rule: Java 21/25 coding standards
A Cursor rule that pins your agent to Google Java Style: naming, immutability, Stream API and resource handling.
.cursor/rules/java.mdcWhat java does
Drop .cursor/rules/java.mdc into the project and the rule applies to every file. It covers formatting first: 4-space indent, no tabs, K&R braces, mandatory braces on if and while bodies, explicit imports with no *, UTF-8 source files.
Naming and patterns take up most of the file. Packages stay lowercase and dot-separated, classes and records use PascalCase, constants use SCREAMING_SNAKE_CASE, boolean methods start with is, has, can or should, collections get plural names. For plain data carriers the rule points at record (Java 16+), for collection work at the Stream API and Optional, for anything AutoCloseable at try-with-resources.
A performance section tells the agent to reach for StringBuilder inside loops and to use parameterized logging instead of string concatenation. The closing section warns about mutable static fields, which break tests and invite race conditions. Everything assumes Java 21/25 and Google Java Style, so a team with its own style guide will need to rewrite parts of the file.
What it helps with
- format Java code to Google Java Style without hand-editing
- swap mutable data classes for records where it fits
- strip wildcard imports and sort the rest
- replace string concatenation in loops with StringBuilder
- spot mutable static fields that make tests flaky
Example prompts for the agent
- Refactor this class following the java rule: drop the setters and make it a record
- Check this file against the java rule and list the naming violations
- Rewrite this concatenation loop the way the java rule wants it
How to install java
Manually: put the files in place
.cursor/rules/java.mdcFrontmatter: description, globs, alwaysApply
Manually: put the files in place
.claude/rules/java.mdThe paths frontmatter field limits the files: move globs from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.github/instructions/java.instructions.mdThe applyTo field sets which files it applies to: move globs from the source into applyTo
Manually: put the files in place
.devin/rules/java.mdManually: put the files in place
.clinerules/java.mdStep by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules
Questions about java
How do I install the java rule in Cursor?
Place the file at .cursor/rules/java.mdc in your project root and Cursor picks it up automatically.
What does the java rule do?
It gives the agent a set of Java standards: formatting, naming, immutability, Stream API usage, try-with-resources and string handling.
Which Java version does the rule target?
Java 21/25, following the Google Java Style Guide. If your team has its own style guide, you will need to rewrite the file to match it.
Source text
java.mdc · CC0-1.0
---
description: Definitive guidelines for writing high-quality, maintainable, and performant Java code, adhering to modern best practices (Java 21/25) and enterprise standards like Google Java Style.
globs: **/*
---
# Java Best Practices
This document outlines the definitive best practices for Java development within our team. Adherence to these rules ensures consistent, high-quality, and maintainable code, leveraging modern Java features and enterprise standards.
## 1. Code Organization and Structure
### 1.1. Style Guide Adherence
Always follow the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html) for formatting, naming, and structural conventions. Configure your IDE (e.g., IntelliJ IDEA, VS Code with appropriate plugins) to automatically apply this style.
### 1.2. File Naming and Encoding
Source files must be named after the single top-level class they contain, with a `.java` extension. All source files must use **UTF-8** encoding.
### 1.3. Indentation and Whitespace
Use **4 spaces** for indentation. **Never use tabs.** For wrapped lines, use **8 spaces** (twice the normal indentation).
❌ BAD
```java
public class MyClass {
void myMethod() {
// Tab used for indentation
System.out.println("Hello");
}
}
```
✅ GOOD
```java
public class MyClass {
void myMethod() {
// 4 spaces for indentation
System.out.println("Hello");
}
}
```
### 1.4. Braces
Always use K&R style braces for non-empty blocks. Braces are mandatory for `if`, `else`, `for`, `do`, and `while` statements, even for single-line bodies.
❌ BAD
```java
if (condition)
doSomething(); // Missing braces
while (true){ // Bad K&R style
doSomething();
}
```
✅ GOOD
```java
if (condition) {
doSomething();
}
while (true) {
doSomethingElse();
}
```
### 1.5. Package and Import Statements
Every class must belong to a package. Use explicit imports; **wildcard imports (`*`) are forbidden**. Imports are ordered: static imports, then non-static imports, separated by a single blank line, both groups in ASCII sort order.
❌ BAD
```java
import java.util.*; // Wildcard import
import static com.example.Constants.*; // Wildcard static import
public class MyClass { /* ... */ }
```
✅ GOOD
```java
import static com.example.Constants.DEFAULT_VALUE;
import java.util.ArrayList;
import java.util.List;
public class MyClass { /* ... */ }
```
### 1.6. Naming Conventions
Adhere strictly to these conventions:
* **Packages**: `com.yourcompany.project.module` (all lowercase, dot-separated).
* **Classes/Enums/Records/Interfaces**: `PascalCase` (nouns).
* **Variables/Parameters**: `camelCase`.
* **Constants**: `SCREAMING_SNAKE_CASE` (for `static final` fields).
* **Methods**: `camelCase` (verbs).
* **Boolean Variables/Methods**: Prefix with `is`, `has`, `can`, `should`.
* **Collections**: Use plural names (e.g., `users`, `messages`).
* **Test Methods**: `featureUnderTest_testScenario_expectedBehavior()`.
❌ BAD
```java
public class userManager { // Class name not PascalCase
public static final String default_user_name = "guest"; // Constant not SCREAMING_SNAKE_CASE
private List userList; // Collection not plural
public boolean checkUser(User u) { /* ... */ } // Boolean method name unclear
}
```
✅ GOOD
```java
package com.example.app.users;
import java.util.List;
public class UserManager {
public static final String DEFAULT_USER_NAME = "guest";
private List<User> users;
public boolean isValidUser(User user) { /* ... */ }
}
```
## 2. Common Patterns and Anti-patterns
### 2.1. Prefer Immutability
Design classes to be immutable whenever possible. This simplifies concurrency and reasoning about state. Use `record` classes (Java 16+) for simple immutable data carriers.
❌ BAD
```java
public class User {
private String name;
public User(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } // Mutable setter
}
```
✅ GOOD
```java
public record User(String name) {} // Immutable record (Java 16+)
// Or traditional immutable class:
public final class ImmutableUser {
private final String name;
public ImmutableUser(String name) { this.name = name; }
public String getName() { return name; }
}
```
### 2.2. Embrace Functional Programming with Streams and Optionals
Leverage Java's Stream API for collection processing and `Optional` for handling potentially absent values.
❌ BAD
```java
List<String> names = getUsers().stream()
.filter(u -> u.getAge() > 18)
.map(User::getName)
.collect(Collectors.toList());
// Handling null:
User user = findUserById(123L);
if (user != null) {
System.out.println(user.getName());
} else {
System.out.println("User not found");
}
```
✅ GOOD
```java
List<String> names = users.stream()
.filter(user -> user.getAge() > 18)
.map(User::getName)
.toList(); // Java 16+ for .toList()
// Handling Optional:
findUserById(123L)
.map(User::getName)
.ifPresentOrElse(
System.out::println,
() -> System.out.println("User not found")
);
```
### 2.3. Resource Management with Try-with-resources
Always use try-with-resources for any resource that implements `AutoCloseable`.
❌ BAD
```java
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// ... read from fis
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
✅ GOOD
```java
try (FileInputStream fis = new FileInputStream("file.txt")) {
// ... read from fis
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
}
```
## 3. Performance Considerations
### 3.1. Efficient String Concatenation
Use `StringBuilder` for concatenating multiple strings in loops or when building complex strings.
❌ BAD
```java
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // Creates many intermediate String objects
}
```
✅ GOOD
```java
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
```
### 3.2. Logging Performance
Avoid expensive string formatting or method calls in logging statements unless the log level is enabled. Use parameterized logging.
❌ BAD
```java
logger.debug("Processing user: " + user.getName() + " with ID: " + user.getId()); // String concatenation always happens
```
✅ GOOD
```java
logger.debug("Processing user: {} with ID: {}", user.getName(), user.getId()); // Parameters only evaluated if debug is enabled
```
## 4. Common Pitfalls and Gotchas
### 4.1. Avoid Mutable Static State
Mutable static fields are a common source of concurrency bugs and make testing difficult. Minimize their use.
❌ BAD
```java
public class Counter {
public static int count = 0; // Mutable static field
public static void increment() { count++; }
}
```
✅ GOOD
```java
// Use dependency injection for stateful services or pass state explicitly
public class CounterService {
private int count = 0; // Instance field
public synchronized void increment() { count++; } // Thread-safe instance method
public int getCount() { return count; }
}
```
### 4.2. Dependency Version Management
Always pin dependency versions. Use Bill-of-Materials (BOMs) for managing compatible versions across related libraries (e.g., Spring Boot BOM, Google Cloud Libraries BOM).
❌ BAD (Maven)
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- No version specified, relies on parent or latest -->
</dependency>
```
✅ GOOD (Maven with BOM)
```xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.5</version> <!-- Pin BOM version -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Version inherited from BOM -->
</dependency>
</dependencies>
```
### 4.3. Correct `equals()` and `hashCode()` Implementation
Always override both `equals()` and `hashCode()` together, or neither. Use `Objects.equals()` and `Objects.hash()` for robust implementations. For data classes, `record` types handle this automatically.
❌ BAD
```java
public class Point {
private int x, y;
// Only equals() overridden, or incorrectly
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
// Missing hashCode()
}
```
✅ GOOD
```java
import java.util.Objects;
public final class Point { // Make final if not intended for extension
private final int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc