Java Decompiler Example
In this article, we are going to explain what a Java Decompiler is and how to decompile classes using examples.
1. What is a Java Decompiler?
As the name suggests, a decompiler is the opposite of a compiler. This reads .java
source files and transforms them into .class
files; a Java decompiler reads .class
files as input and produces equivalent Java source code from them.
A Java decompiler can help you understand or debug executable Java code for which the source code is not available. There are a variety of Java decompilers available, both as plugins for Java IDEs and for standalone use from the command line.
2. Why Would I Need a Decompiler?
In the best of all possible worlds, you wouldn’t need a decompiler. Every bit of source code you ever wrote would be stashed away in version control. The version control archives would be regularly backed up. And just in case the building burns down, backup copies would routinely be taken safely off-site.
But here in the real world, we know the procedures and protocols intended to safeguard source code aren’t always followed rigorously and consistently. And so, in spite of everything, source code still manages to go missing from time to time. Consider this somewhat contrived, but reasonably plausible scenario:
- Colleague A writes
QuadSolver
, a library that solves quadratic equations, and deploys it as ajar
artifact to the corporate Maven repository. However, Colleague A neither commits the source to version control, nor deploys a source-codejar
to the Maven repo. - You write
QuadDemo
, which depends onQuadSolver
; you check your source into version control. - Colleague A’s desktop HD unexpectedly dies, taking with it the only copy of the source of
QuadSolver
. QuadDemo
is now producing some confusing results. BecauseQuadSolver
does all the heavy lifting forQuadDemo
, you’ll need to debugQuadSolver
in order to isolate the problem.
At this point, you would find Java decompiler quite useful.
3. The Example Code
We’ve provided the code for QuadSolver
and QuadDemo
so that you can reproduce the example scenario for yourself.
QuadSolver
The QuadSolver
library consists of a single class, also called QuadSolver
,. QuadSolver
has one static
method, solve
, which accepts the three real coefficients of a quadratic equation and returns the solution as a pair of complex numbers, each represented as a two-element int
array. Here are the source code and the POM file:
QuadSolver.java
package jcg.examples.quadsolver; public class QuadSolver { public static double[][] solve(double a, double b, double c) { double[][] roots = new double[2][2]; double d = b * b - 4 * a * c; if (d > 0) { roots[0][0] = (0 - b + Math.sqrt(d)) / (2 * a); roots[1][0] = (0 - b - Math.sqrt(d)) / (2 * a); } else { roots[0][0] = roots[1][0] = 0 - (b / (2 * a)); if (d != 0) { // d < 0 roots[0][1] = Math.sqrt(0 - d) / (2 * a); roots[1][1] = 0 - roots[0][1]; } } return roots; } }
QuadSolver.pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jcg.examples</groupId> <artifactId>quadsolver</artifactId> <version>3.0.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.8.1</version> <scope>test</scope> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> </project>
QuadDemo
QuadDemo
is a simple command-line application that uses QuadDemo
to solve a few hard-coded equations. Here are the code and POM:
QuadDemo.java
package jcg.examples.quaddemo; import jcg.examples.quadsolver.QuadSolver; public class QuadDemo { public static void main(String[] args) { double[][] eqns = { {2, 3, 5}, {1, 3, 1}, {1, 2, 1} }; for (double[] eq : eqns) { double[][] roots = QuadSolver.solve(eq[0], eq[1], eq[2]); System.out.printf("Equation: %gx^2%+gx%+g = 0, roots: %g%+gi, %g%+gi%n", eq[0], eq[1], eq[2], roots[0][0], roots[0][1], roots[1][0], roots[1][1]); } } }
QuadSolver.pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.kaa</groupId> <artifactId>quaddemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>jcg.examples</groupId> <artifactId>quadsolver</artifactId> <version>3.0.0</version> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>14</maven.compiler.source> <maven.compiler.target>14</maven.compiler.target> </properties> </project>
4. Using a decompiler in Eclipse
Eclipse supports debugging of executable Java code without accompanying source code, using either its inbuilt Class File Editor or a Java decompiler plug-in. The standard Eclipse distribution does not come with a decompiler pre-installed, so you’ll need to choose one from the Eclipse Marketplace and install it if you want decompiler functionality.
For this example, we’ll use the Enhanced Class Decompiler, or ECD, plug-in. It’s not the only decompiler available for Eclipse, but it’s the most popular (by number of downloads), and is actively supported by the Eclipse community. To download it, go the the Eclipse Marketplace and search for “ECD”.
Once you install ECD, you’ll need to configure Eclipse to use it. Go to Window > Preferences, then choose General > Editors > File Associations from the menu on the left. Select Class Decompiler Viewer to be the default editor for both the *.class and *.class without source file types if it isn’t already:
To see ECD in action, first download and prepare the example code as described in Download the source code at the end of this article. Set a breakpoint at line 13 in QuadDemo.java
and start it running:
Now single-step into the solve
method of QuadSolver
. Without ECD, you’d see Eclipse’s standard Class File Editor:
Here, you get a summary of the solve
method’s public API, along with a listing of the disassembled bytecode: interesting, perhaps, but not very helpful. You’re also limited in the amount of debugging you can do: you can’t set breakpoints, single-step or examine variables.
But with ECD installed and configured, it’s an entirely different story. When you step into solve
you see readable Java source code:
What’s more, you can debug the decompiled code just as if it were the original: breakpoints and single-stepping work, you can examine local variables, etc. You can also save the reconstructed source as a .java
file by right-clicking and choosing Export source…
5. Using a decompiler in IntelliJ IDEA
IntelliJ IDEA 2020.2 comes right from the box with JetBrains’ own Java decompiler, Fernflower, already bundled in. There’s nothing more you need to do in order to use it. Just step into any piece of code for which the source is unavailable and the decompiler automatically goes to work.
Here’s a screenshot of Fernflower in action:
As with Eclipse and ECD, you can debug the decompiled source exactly as if it were the original. IntelliJ IDEA doesn’t provide a specific “export source” command, so you’ll have to resort to cut-and-paste if you want to save the decompiled source.
6. Using a decompiler from the command line
Decompiler plugins for IDEs are geared toward making it easy to “peek into” individual classes. But if you need to reconstruct source code on a more industrial scale (say, a JAR file containing hundreds of classes), you may want to consider a command-line-based decompiler.
Just to give you the general flavor, here are examples of using two different CLI-based decompilers, jd-cli and CFR to decompile the example QuadSolver
JAR file which has been deployed to the local Maven repository:
7. Summary
In this article, you’ve learned that a Java decompiler is able to generate Java code from compiled Java classes. You’ve also seen examples of decompilation used in some typical settings. While having original source code available is always preferable, a Java decompiler can be a handy safety net when, for whatever reason, that isn’t possible.
To prepare the example, follow these steps:
- Extract the source code to any convenient directory; the top-level directory contains two Maven project folders:
QuadSolver
andQuadDemo
. - Import the
QuadSolver
project into your IDE and build it up to theinstall
lifecycle phase. - Remove the
QuadSolver
project from the workspace (Eclipse)/close it (IDEA). - Import the
QuadDemo
project and build it. - Open
QuadDemo.java
and set a breakpoint at line 13.