IntelliJ IDEA
IntelliJ IDEA – the IDE for Professional Development in Java and Kotlin
Java 24: Build Games, Prototypes, Utilities, and More—With Less Boilerplate
At ten, I sat in my school’s computer lab, in awe of a line drawing on the screen—my own code had brought it to life. “Yay! I coded that! I can code anything!”, I thought. My teacher, surprised, asked, “Wow! You did that?” It felt like magic, but it was more than that—I realized I could turn ideas into reality through code. At that moment, I knew I was going to be a developer.
Now, imagine sparking that same excitement for new Java developers with simple, fun programs (such as games)—made easier by Java’s modern features that cut boilerplate code. These new features are not just for beginners; experienced developers can write cleaner, faster code for tasks like building prototypes, filtering logs, scraping stock prices, or creating powerful utilities.
Newer features, such as, Simple Source Files & Instance Main Methods (JEP 495) and Module Import Declarations (JEP 494) cut down unnecessary complexity. In this blog post, I’ll demonstrate these features through practical use cases. If you’re new to simple source files and instance main methods, check out my introductory blog post on this topic.
Let’s get started.
1. Build Games and create interesting graphics
In this section, I’ll talk about building classic games and generating patterns, such as:
- Drawing Concentric circles – A fun animation to watch in action.
- Hangman – Guess a word, one letter at a time.
- Treasure hunt – Find the location of the hidden treasure.
- Rock-Papper-Scissors – Who hasn’t played this classic?
- Dots and Boxes – Join lines to complete boxes and claim territory.
The gif below shows these games and patterns in action. If you like them, feel free to try them out using the source code hosted here. I deliberately didn’t include the full source code for all these games in this blog post, as it’s quite lengthy and could distract from the main point I’m trying to make.
I’ve included these games to highlight how easy it is to get started with Java—and how you can build games like these and much more.
Some of these games are terminal-based, such as Hangman and Treasure Hunt (implicit classes), while others have a Swing-based UI (regular classes). If you ask me, my favorite game during my growing-up years was Dots and Boxes—I have fond memories of playing it with my friends at school in a notebook.
The codebase of these games demonstrates that implicit classes and instance main methods can be used not just to get started but also to build much more complex applications. These classes aren’t overly simple; they include sophisticated algorithm implementations using control flow statements.
Speaking of algorithms, I don’t think the code for the Dots and Boxes game currently implements a strong algorithm for the computer opponent. I’m accepting pull requests if you’d like to try improving it—the game is hosted here.
2. Processing log files
Imagine you need to extract error messages (containing the text ‘ERROR’) from a log file and save them to a separate file for later review. Here’s a section of the log file, with the target lines highlighted:

You could accomplish this task by using (just) the following executable code:
void main() throws IOException { List<String> errors = Files.readAllLines(Path.of("src/main/logs/app.log")) .stream() .filter(line -> line.contains("ERROR")) .collect(Collectors.toList()); Files.write(Path.of("src/main/logs/error.log"), errors); }
The following gif shows the contents of app.log, execution of the preceding code in IntelliJ IDEA (.java file without declaration of a class) and the contents of the newly created file, that is, errors.log:

Reading a file, filtering its contents, and creating another file—all with just a few lines of code. Isn’t that great? This example reads a .log file, but you could easily adapt it to read other formats, such as .csv, .json, and more.
The next section covers what happens behind the scenes and why. If you’re already familiar with these details, feel free to skip ahead to the next practical example.
Behind the scenes
As a developer, the code shown in the previous section offers multiple benefits:
- You can focus on the logic of reading app.log and creating error.log.
- You no longer need to create an explicit enclosing class to execute this code. It is automatically defined in an implicit class (similar to how a default package works)
- The signature of the implicit main method is shorter.
- You don’t need to write explicit import statements for Java I/O classes. This also applies to all classes or interfaces from the java.base module, as an implicit class automatically imports any package exported by java.base (on demand).
- Overall, the code is shorter, making it easier to read and write.
If you prefer command prompt, you could also avail the benefits of skipping compilation of this class and execute the code using the command java <name of your source file>. If you are using your favorite IDE, such as IntelliJ IDEA, you can execute your code using a single click by using the Run feature (by either using the icon or the related shortcut).
Are you wondering how the code will change without these newer Java features-implicit class, instance main method, and module import declaration). IntelliJ IDEA includes context actions to convert an implicit class to a regular class and vice versa.
The following gif shows what happens when you convert the preceding code implicit class to a regular class by using the context action ‘Convert implicitly declared class into a regular class’ on method main():

You might wonder how significant these changes are and why you would even want to convert an implicit class to a regular class. Converting an implicit class to a regular class makes sense when you’re ready to work with classes more formally, integrate the code into another project, or package it for reuse.
The conversion might not seem like a big change at first, especially since it imports the java.base module by default, instead of importing individual classes or interfaces from packages like io, nio, or util, as shown below:
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.stream.Collectors;
The module java.base is automatically imported in implicit classes, so they don’t need import statements for using the classes and interfaces mentioned in the preceding code. Just in case you are curious to find out which modules are exported by the module java.base, you could access that information by navigating to its declaration, as shown in the following gif:

Importing a single module vc. Loading individual classes and interfaces also lowers the cognitive load when you are reading and writing your code.
3. Building utilities, such as a Stock Price Scraper
Imagine you need to look up some stock prices. You could access a website to do so, or, you could also spin up a single method (defined in an implicit class) to do that, as follows:
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; void main() throws IOException { Document doc = Jsoup.connect("https://finance.yahoo.com/quote/AAPL") .get(); String stockPrice = doc.select(".price").text(); println("Stock Price: " + stockPrice); }
Here’s the output of executing this code snippet:

I created an additional method in the same source file to format the text received from the pinging the website:
public static String formatAndDisplayStockPrice(String stockPrice) { if (stockPrice == null || stockPrice.isEmpty()) { System.out.println("No stock price data available."); return null; } String[] parts = stockPrice.split("\\s+"); StringBuilder formattedOutput = new StringBuilder(); try { formattedOutput.append(String.format(""" ---------- Stock Price Information ---------- Stock Price (At close): %s Change: %s (%s%%) Closing Time: %s %s %s %s After Hours Price: %s Change (After hours): %s (%s) After Hours Time: %s %s %s ----------- Additional Information ----------- """, parts[2], parts[3], parts[4], parts[6], parts[7], parts[8], parts[9], parts[10], parts[11], parts[12], parts[14], parts[15], parts[16])); for (int i = 17; i < parts.length; i++) { formattedOutput.append(String.format("Metric %d: %s%n", (i - 16), parts[i])); } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Unexpected format in stock price data. Raw data: " + stockPrice); } return formattedOutput.toString(); }
To enable your runtime to find the classes imported from the packages org.jsoup
and org.jsoup.nodes, you can either add them to your classpath or include them as dependencies in your Maven or Gradle build configuration.
The examples shown in this blog post are just the tip of the iceberg—you can create many other utilities using these features.
Summary
Newer features in Java 24, such as, Simple Source Files & Instance Main Methods (JEP 495) and Module Import Declarations (JEP 494) aren't just about cutting down boilerplate code. They also reduce the cognitive load of getting started with Java, making it easier to dive into coding—whether you're creating fun games, building practical scripts, or developing prototypes and utilities faster.
If you haven’t tried these features yet, you’re missing out on a lot of fun!