From Script to Superpower: Modern Java with JBang Explained
Discover how to run Java instantly, manage dependencies inline, and build real CLI tools in one file.
Prologue: The Year Is 2015
You’re a Java developer with a simple idea. Maybe you want to call an API, parse JSON, and print something useful.
In Python, that’s a two-line script and a smug sense of superiority.
In Java… your inner monologue sounds like this:
“Fine, I’ll generate a Maven archetype.”
“Yes, Maven, please download the entire internet again.”
“Of course I need three directories before I can type anything.”
“Sure, let’s modify the POM. Again.”
“Why is my simple script a fat JAR now?”
“Oh great, the weather changed while Maven was building.”
You sigh. But you persist. Because you believe in Java.
Fast-forward to 2025.
Java has evolved.
The JVM is fast.
Java 21 is beautiful.
And someone finally said:
“Why don’t we make Java behave like every other scripting language?”
That someone was Max Andersen, and the answer was JBang.
Welcome to the future.
Your First JBang Script (The ‘Hello World’ Dimension)
Let’s start with something tiny. Create hello.java:
///usr/bin/env jbang “$0” “$@” ; exit $?
class hello {
public static void main(String[] args) {
System.out.println(”Hello from “ + java.time.Year.now());
}
}That first line?
It’s a shebang in disguise, placed inside a Java comment so the compiler ignores it but your shell doesn’t. This trick makes .java files executable on Unix-like systems.
Run it:
chmod +x hello.java
./hello.javaOr more commonly:
jbang hello.javaOutput:
Hello from 2025No project.
No POM.
No Gradle.
Just Java, launching instantly.
JBang compiles the script behind the scenes, caches it, and reuses the compiled output until the file changes. It feels like scripting, but it’s full Java.
Dependencies From the Future (AKA Maven Central Without XML)
Now let’s move beyond toy examples.
Create github.java:
///usr/bin/env jbang “$0” “$@”“ ; exit $?
//DEPS com.google.code.gson:gson:2.10.1
//DEPS com.squareup.okhttp3:okhttp:4.12.0
import com.google.gson.*;
import okhttp3.*;
import java.io.IOException;
class github {
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println(”Usage: ./github.java <username>”);
return;
}
String username = args[0];
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(”https://api.github.com/users/” + username)
.build();
try (Response response = client.newCall(request).execute()) {
String json = response.body().string();
JsonObject user = JsonParser.parseString(json).getAsJsonObject();
System.out.println(”\n=== GitHub User Info ===”);
System.out.println(”Name: “ + user.get(”name”));
System.out.println(”Bio: “ + user.get(”bio”));
System.out.println(”Public Repos: “ + user.get(”public_repos”));
System.out.println(”Followers: “ + user.get(”followers”));
System.out.println(”Location: “ + user.get(”location”));
}
}
}Run it:
jbang github.java maxandersenWhat happened:
JBang scanned the file for
//DEPSentriesIt downloaded Gson and OkHttp from Maven Central
It compiled your script with the correct classpath
It executed your program—fast
No POM.
No build tool.
No XML.
Just code.
A Professional CLI Tool in a Single File
Time to evolve. Let’s add:
Help text
Subcommands
Flags
Output formats
Enter PicoCLI.
Create ghuser.java:
///usr/bin/env jbang “$0” “$@” ; exit $?
//DEPS info.picocli:picocli:4.7.5
//DEPS com.google.code.gson:gson:2.10.1
//DEPS com.squareup.okhttp3:okhttp:4.12.0
import picocli.CommandLine;
import picocli.CommandLine.*;
import com.google.gson.*;
import okhttp3.*;
import java.util.concurrent.Callable;
@Command(name = “ghuser”, mixinStandardHelpOptions = true, version = “ghuser 1.0”, description = “Fetches GitHub user information”)
class ghuser implements Callable<Integer> {
@Parameters(index = “0”, description = “GitHub username”)
private String username;
@Option(names = { “-f”, “--format” }, description = “Output format: text or json”, defaultValue = “text”)
private String format;
@Option(names = { “-v”, “--verbose” }, description = “Show detailed information”)
private boolean verbose;
public static void main(String[] args) {
int exitCode = new CommandLine(new ghuser()).execute(args);
System.exit(exitCode);
}
@Override
public Integer call() throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(”https://api.github.com/users/” + username)
.build();
try (Response response = client.newCall(request).execute()) {
String json = response.body().string();
if (”json”.equals(format)) {
System.out.println(json);
return 0;
}
JsonObject user = JsonParser.parseString(json).getAsJsonObject();
System.out.println(”\n╔═══════════════════════════════╗”);
System.out.println(”║ GitHub User: “ + pad(username, 15) + “║”);
System.out.println(”╠═══════════════════════════════╣”);
System.out.println(”║ Name: “ + pad(get(user, “name”), 22) + “║”);
System.out.println(”║ Repos: “ + pad(get(user, “public_repos”), 21) + “║”);
System.out.println(”║ Followers: “ + pad(get(user, “followers”), 17) + “║”);
if (verbose) {
System.out.println(”║ Location: “ + pad(get(user, “location”), 17) + “║”);
System.out.println(”║ Bio: “ + pad(get(user, “bio”), 22) + “║”);
}
System.out.println(”╚═══════════════════════════════╝\n”);
return 0;
}
}
private String get(JsonObject obj, String key) {
JsonElement e = obj.get(key);
return e == null || e.isJsonNull() ? “N/A” : e.getAsString();
}
private String pad(String s, int length) {
return String.format(”%-” + length + “s”, s.length() > length ? s.substring(0, length) : s);
}
}Run it:
jbang ghuser.java --help
jbang ghuser.java maxandersen
jbang ghuser.java -v maxandersen
jbang ghuser.java --format=json maxandersenInstall it system-wide:
jbang app install ghuser.java
ghuser dukelangYes — your script is now a real command.
The Moment You Realize This Is Not “Just a Script”
JBang gives you full IDE support.
jbang edit --open=idea ghuser.javaJBang generates a temporary project containing:
Proper sources
Classpath metadata
Dependencies
IDE configuration
You get refactoring, debugging, and completion. Everything you’d expect from a full Java project.
This works with IntelliJ, VS Code, Eclipse, NetBeans, and even Vim if you enjoy pain.
Templates (Instant Apps Without Fuss)
Let’s generate a Quarkus REST API—inside a single file.
jbang init --template=qrest api.javaThis produces:
///usr/bin/env jbang “$0” “$@” ; exit $?
//JAVA 17+
// Update the Quarkus version to what you want here or run jbang with
// `-Dquarkus.version=<version>` to override it.
//DEPS io.quarkus:quarkus-bom:${quarkus.version:3.15.1}@pom
//DEPS io.quarkus:quarkus-rest
// //DEPS io.quarkus:quarkus-smallrye-openapi
// //DEPS io.quarkus:quarkus-swagger-ui
//JAVAC_OPTIONS -parameters
import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path(”/hello”)
@ApplicationScoped
public class api {
@GET
public String sayHello() {
return “Hello from Quarkus with jbang.dev”;
}
}Run:
jbang api.javaVisit:
You just wrote a Quarkus service without a project structure.
Yes, really.
List all templates:
jbang template listAdvanced Tricks (Because You’re a Time-Traveling Java Wizard Now)
Run scripts directly from GitHub
jbang https://raw.githubusercontent.com/jbangdev/jbang-examples/refs/heads/main/examples/helloworld.javaAliases = custom commands
jbang alias add --name gh ghuser.java
gh dukelangNative executables
jbang --native ghuser.javaRequires GraalVM or a distribution with native-image support installed.
Run with a specific Java version
jbang --java 21 hello.javaOr inside the script:
//JAVA 21Export to a full Maven project
jbang export portable ghuser.javaGreat for moving prototypes into production.
Real-World Uses
DevOps scripts that aren’t 400-character bash puzzles
//DEPS info.picocli:picocli:4.7.5
//DEPS org.yaml:snakeyaml:2.2Data processing with CSVs that doesn’t require summoning awk spirits
//DEPS org.apache.commons:commons-csv:1.10.0
//DEPS com.opencsv:opencsv:5.9Quick API test scripts without spinning up JUnit
//DEPS io.rest-assured:rest-assured:5.4.0Prototyping libraries instantly
Try an idea in seconds.
Share a reproducible demo by sending someone a single file.
Epilogue: What JBang Really Represents
JBang stands on a simple truth:
Java is powerful, but sometimes the ceremony gets in the way of creativity.
JBang removes the ceremony.
It gives you:
The speed of scripting
The tooling of a modern JVM
The entire Maven Central ecosystem
IDE integration
Native image builds
Zero-friction experimentation
It is not here to replace Maven or Gradle.
It is here to make Java delightful for quick tasks.
So the next time someone smirks and says:
“Why not just use Python for that?”
Smile.
And reply:
“Because I can use Java.”
Then run your single-file script and watch them blink.
Now go forth and script. The JVM awaits. ⚡
For more, visit: https://jbang.dev and the JBang community.



