Your original stays in the left pane
Most beautifiers rewrite in place and leave you hunting through undo history to see what moved. Here the source and the result sit side by side, so you read both before deciding the rewrite is an improvement. Press CtrlEnter to reformat without reaching for the mouse. Send Back moves the formatted text into the left pane when you want a second pass at a different indent width.
Switching any setting reformats straight away, so you compare 2 spaces against 4, or attached braces against Allman, by clicking twice.
What gets rebuilt
Every line is regenerated rather than patched. Your source runs through a Java tokenizer first, so string literals, character constants, text blocks, comments, and annotations are set aside before a single spacing rule fires. A brace inside a string no longer throws the indentation off by a level.
- Indentation comes from brace depth at the width you pick. Existing tabs and mixed leading whitespace are discarded outright.
- Statements break at semicolons outside a
forheader, so one statement gets one line. - Annotations on a type, field, or method move onto their own line above the declaration. Parameter annotations stay inline, where
@Nullable String labelbelongs. - Generic arguments stay tight.
Map<String, List<Integer>>keeps its stacked closing brackets, the diamond innew ArrayList<>()survives, and a shift likex >> 2is still read as arithmetic. - Lambda arrows and method references get the spacing Java readers expect:
s -> s.trim()andString::trim. - Both switch forms work. Colon labels indent under
switchwith the body on the next line, arrow labels stay on one line. - Casts keep their gap, so
(String) objrather than(String)obj, and a negative literal after a cast stays glued to its minus sign. - Empty bodies collapse to
{}, trailing whitespace goes, and runs of blank lines collapse to one. - A blank line lands after every method, type, and enum body.
The status bar under the panes reports what came out: line counts either side, how many type bodies and methods were found, and how deep the nesting goes. Nesting past four levels in a single method is worth a second look before the formatting question matters at all.
The import block is the part worth automating
Imports rot faster than anything else in a Java file. A merge drops a duplicate in, a refactor leaves a stale entry behind, and nobody notices because the compiler stays quiet about order. Set Imports to Tidy and the block is deduplicated, sorted inside each group, and separated by blank lines.
| Group | Matches | Example line |
|---|---|---|
| First | The standard library | import java.time.Instant; |
| Second | Java EE and Jakarta EE | import javax.annotation.Nullable; |
| Third | Third party code and your own packages | import com.toolexe.reports.Reading; |
| Last | Static imports | import static java.util.Comparator.comparing; |
This order is a choice, not a standard. IntelliJ ships with the first three groups reversed, and google-java-format puts static imports first in a single block with no separators at all. None of the three is wrong, and the one your repository already uses is the one to keep. Leave is a click away for exactly that reason.
Tidy touches a run of imports only when nothing but blank lines sits between them. A comment parked between two imports stops the rewrite, because moving lines around a comment changes what the comment points at. Unused imports survive too, since this page never sees the rest of your classpath and a guess there would delete working code.
Attached braces, or Allman for the code you inherited
Java settled this argument early. The Oracle code conventions, the Google Java Style guide, and the default in every mainstream IDE all put the opening brace at the end of the line it belongs to. Attached is the default here and the right answer for new code.
Allman earns its place on files that arrived from somewhere else. Ported C# rarely gets rewritten, generated adapters follow whatever the generator felt like, and a lot of 2005 era enterprise Java came out of a template with the brace on its own line. Reading those is easier at the shape they were written in, and reformatting the whole file to prove a point buries the real diff and makes git blame useless for that region.
Picking an indent width
| Width | Where it comes from |
|---|---|
| 4 spaces | Oracle code conventions, and the out of the box setting in IntelliJ IDEA and Eclipse |
| 2 spaces | Google Java Style, common in Android work and Kotlin adjacent projects |
| 8 spaces | Older Sun sample code and a few long lived Apache projects |
| Tab | Files governed by an .editorconfig with indent_style set to tab |
Continuation lines are a separate rule in both style guides, 8 spaces for Oracle and 4 for Google. This page never wraps a line, so continuation indent never comes up. That gap is the biggest difference between what you read here and what google-java-format writes to disk.
What it leaves exactly as you wrote it
- Text blocks. Everything between the triple quotes survives character for character, closing delimiter included. The position of those closing quotes decides how much incidental whitespace the compiler strips, so moving them would change your string.
- Javadoc and comment wording. Comment lines are re-indented to match their block. The text inside is never touched, rewrapped, or reordered.
- String and character literals, escape sequences included.
- Line breaks inside array and collection initializers. A lookup table you spread over six lines stays over six lines.
- Method chains. A stream pipeline written on one line stays on one line, whatever its width.
- Blank lines between members, collapsed to one each.
Where this stops short
- No line length limit and no wrapping. A 200 character stream chain comes out 200 characters wide.
- No continuation indent rules, so nothing gets aligned under an opening parenthesis.
- Unused imports stay put. Only a compiler with your full classpath knows what is reachable.
- Generic detection is a lookahead heuristic. An unusual comparison written as
a < b, c > dinside an argument list will confuse it. - Annotation members are never split across lines, so a long
@RequestMappingstays on one line. - Files past roughly 2 MB are refused rather than freezing the tab.
- Kotlin, Groovy, and Scala are near enough to look right and far enough to come out wrong. Reach for a formatter built for them.
The output will not match google-java-format line for line, and matching it is not the goal. This page exists for reading code somebody pasted at you. What lands in a commit should go through the config your repository already carries.
Moving the job into the build
Three commands cover almost every Java codebase, and all of them run offline in a pre-commit hook or a CI step.
google-java-format -i src/main/java/com/toolexe/reports/SampleWindow.java
./gradlew spotlessApply
mvn com.spotify.fmt:fmt-maven-plugin:formatCommit the settings so layout stops being a personal preference:
// build.gradle
plugins { id 'com.diffplug.spotless' version '6.25.0' }
spotless {java {googleJavaFormat('1.22.0')removeUnusedImports()importOrder('java', 'javax', '', '\\#')trimTrailingWhitespace()endWithNewline()}}Spotless gives you spotlessCheck in CI, so a badly formatted patch fails the build instead of starting an argument in review. The '\\#' entry in that import order is the static group, and moving it to the front matches google-java-format. Pair the build config with an .editorconfig so editors agree long before the build runs:
# .editorconfig
[*.java]indent_style = space
indent_size = 4
max_line_length = 100
trim_trailing_whitespace = true
insert_final_newline = trueCheckstyle covers the rules a formatter cannot reach, naming, import restrictions, method length. Run it alongside Spotless rather than instead of it, because the two answer different questions.
