Comments and Javadoc
Use line, block and documentation comments well, write Javadoc that tools and IDEs pick up, and avoid the Unicode escape trap in comments.
// line comment
/* block comment */
/** Javadoc: @param, @return, @throws, {@code ...} */
/// Markdown doc comment (Java 23+)Java has three kinds of comment:
// line comment, to the end of the line
/* block comment,
can span lines */
/** documentation comment (Javadoc), placed before a declaration */
Since Java 23 a documentation comment can also be written as consecutive /// lines using Markdown, so `code`, lists and [Map#get(Object)] links work without HTML.
What to write
Comments explain why: the business rule, the bug the odd-looking line works around, the reason a timeout is 3 seconds. The code already says what it does. A comment that repeats the code goes stale the first time someone changes one and not the other.
Javadoc
The javadoc tool builds HTML documentation from doc comments, and IDEs show them when you hover over a call. For a public method, say what it returns, which inputs are valid and what it throws, using block tags:
@param namefor each parameter, and@param <T>for a type parameter.@returnfor the result.@throws ExceptionTypefor each exception a caller should expect.{@code ...}for inline code,{@link Type#member}for a link.
On a record, @param tags in the record's doc comment document its components. In shared libraries, internal SDKs and anything another team calls, Javadoc on public types is the contract. Inside a service, document the non-obvious public methods rather than every getter.
Traps
- Block comments do not nest. The first
*/ends the comment, so commenting out code that already contains/* ... */breaks the build. Use//on each line (your IDE's toggle comment does this). - Unicode escapes are processed before comments. The compiler turns
\u000ainto a newline before it looks for comments, so code after it on the same line runs. And a Windows path such asC:\usersin a comment is a compile error, because\umust be followed by four hex digits. - Commented-out code. Delete it. Version control already remembers it.
- Secrets in comments. A password in a comment is still in the repository, in every clone and in its history.
Run javadoc -d docs src/*.java (or your build tool's javadoc task) to generate the HTML; it also warns about missing or wrong tags.
Example
When to use it
- Document a shared client library's public methods so other teams see the contract in their IDE.
- Record the business rule or incident behind an unusual timeout or retry value next to the code.
- Generate HTML API documentation for an internal SDK as part of the build.
More examples
Markdown documentation comments (Java 23+)
Each /// line is part of one doc comment written in Markdown, and the usual @param, @return and @throws tags still apply. The javadoc tool and IDEs render it the same way as a /** */ comment.
/// Reads the page size from a `size` query parameter.
///
/// - A missing or blank value gives the default of `20`.
/// - Values above `100` are capped at `100`.
///
/// @param raw the raw query parameter, may be `null`
/// @return a page size between 1 and 100
/// @throws NumberFormatException if `raw` is not a whole number
/// @throws IllegalArgumentException if the value is less than 1
int pageSize(String raw) {
if (raw == null || raw.isBlank()) {
return 20;
}
int size = Integer.parseInt(raw.strip());
if (size < 1) {
throw new IllegalArgumentException("size must be at least 1");
}
return Math.min(size, 100);
}
void main() {
IO.println(pageSize(null));
IO.println(pageSize(" 50 "));
IO.println(pageSize("500"));
}
// Output:
// 20
// 50
// 100Documenting a record's components
@param tags on the record document its components, which become the accessor methods and the constructor parameters in the generated documentation. This is a good place for units and ranges that the types alone do not show.
/**
* One page of results from a list endpoint.
*
* @param items the items on this page
* @param page the zero-based page number
* @param size the requested page size
* @param total the number of items across all pages
* @param <T> the item type
*/
record Page<T>(List<T> items, int page, int size, long total) {
/**
* Returns whether another page follows this one.
*
* @return {@code true} if a request for {@code page + 1} would return items
*/
boolean hasNext() {
return (long) (page + 1) * size < total;
}
}
void main() {
var first = new Page<>(List.of("ada", "lin"), 0, 2, 3);
var last = new Page<>(List.of("sam"), 1, 2, 3);
IO.println(first.items() + " hasNext=" + first.hasNext());
IO.println(last.items() + " hasNext=" + last.hasNext());
}
// Output:
// [ada, lin] hasNext=true
// [sam] hasNext=falseA Unicode escape that turns a comment into code
The compiler translates \u000a into a line break before it looks for comments, so retries = 0; ends up on a new line and runs. Code review tools show the comment as one line, which is why this trick is worth recognising.
void main() {
int retries = 3;
// Reset for tests \u000a retries = 0;
IO.println("retries = " + retries);
}
// Output:
// retries = 0
Discussion