For over 20 years, running a Java program meant two steps: compile it, then run it. Even a simple “Hello, World!” required two commands in the terminal. Want to test a quick idea? Open an IDE, create a project, set up a package, write a class, compile, and then run. Compared to languages like Python or JavaScript where you just type python script.py or node script.js, Java felt heavy even for the smallest tasks.
Java 11 changed that. With JEP 330, you can now run a single Java source file directly without compiling it first. One command. No .class files. No project setup.
The old workflow (before Java 11):
// Step 1: Write the file (HelloWorld.java)
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
## Step 2: Compile it javac HelloWorld.java ## Step 3: Run the compiled class java HelloWorld ## Output: Hello, World!
The new workflow (Java 11+):
## One step: Run the source file directly java HelloWorld.java ## Output: Hello, World!
That is it. One command. No javac. No .class file created on disk. The JVM compiles the source code in memory and executes it immediately.
Think of it like the difference between writing a formal letter (old way — draft, print, mail) versus sending a text message (new way — type and send). Both communicate, but one has zero ceremony for quick messages.
Why this matters:
When you run java HelloWorld.java, the JVM detects that the argument ends with .java (a source file, not a class name). It then:
.class file is written to disk)main method in the first class declared in the filemain methodKey detail: The main method must be in the first class in the file. If your file has multiple classes, the first one must contain main.
// File: Demo.java
// The FIRST class must have the main method
public class Demo {
public static void main(String[] args) {
Helper helper = new Helper();
helper.greet("Java 11");
}
}
// Additional classes are allowed in the same file
class Helper {
void greet(String name) {
System.out.println("Hello from " + name + "!");
}
}
// Run: java Demo.java
// Output: Hello from Java 11!
What happens behind the scenes:
| Step | Traditional (javac + java) | Single-File (java only) |
|---|---|---|
| Source file | Read from disk | Read from disk |
| Compilation | javac writes .class to disk |
Compiled in memory (no disk I/O) |
| Class loading | JVM loads .class from disk |
JVM loads bytecode from memory |
| Execution | Runs main in specified class |
Runs main in first class |
| Artifacts | .class files remain on disk |
Nothing written to disk |
| Speed | Slightly faster (pre-compiled) | Adds compile time on each run |
Pay close attention to the difference between these two commands:
## This runs a COMPILED class named HelloWorld (looks for HelloWorld.class) java HelloWorld ## This runs a SOURCE FILE named HelloWorld.java (compiles in memory first) java HelloWorld.java
The .java extension is what tells the JVM to use single-file execution mode. Without it, the JVM assumes you are specifying a class name and looks for a compiled .class file.
// File: Greeting.java
public class Greeting {
public static void main(String[] args) {
String message = "Welcome to Java 11 single-file execution!";
System.out.println(message);
System.out.println("Java version: " + System.getProperty("java.version"));
System.out.println("OS: " + System.getProperty("os.name"));
}
}
// Run: java Greeting.java
// Output:
// Welcome to Java 11 single-file execution!
// Java version: 11.0.2
// OS: Mac OS X
Command-line arguments work exactly like they do with compiled classes. Arguments after the filename are passed to main(String[] args):
// File: Greeter.java
public class Greeter {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java Greeter.java [...]");
System.exit(1);
}
for (String name : args) {
System.out.println("Hello, " + name + "!");
}
System.out.println("Greeted " + args.length + " people.");
}
}
// Run: java Greeter.java Alice Bob Charlie
// Output:
// Hello, Alice!
// Hello, Bob!
// Hello, Charlie!
// Greeted 3 people.
// File: Calculator.java
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter first number: ");
double a = scanner.nextDouble();
System.out.print("Enter second number: ");
double b = scanner.nextDouble();
System.out.print("Enter operator (+, -, *, /): ");
String op = scanner.next();
double result;
switch (op) {
case "+": result = a + b; break;
case "-": result = a - b; break;
case "*": result = a * b; break;
case "/":
if (b == 0) {
System.out.println("Error: Division by zero");
return;
}
result = a / b;
break;
default:
System.out.println("Error: Unknown operator '" + op + "'");
return;
}
System.out.printf("%.2f %s %.2f = %.2f%n", a, op, b, result);
}
}
// Run: java Calculator.java
// Enter first number: 15
// Enter second number: 4
// Enter operator (+, -, *, /): *
// 15.00 * 4.00 = 60.00
You can use any class from the Java standard library. Just import it as usual:
// File: DateInfo.java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.DayOfWeek;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class DateInfo {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy 'at' h:mm a");
System.out.println("Current time: " + now.format(formatter));
System.out.println("Day of year: " + now.getDayOfYear());
System.out.println("Week of year: " +
now.getDayOfYear() / 7 + 1);
// Days until weekend
DayOfWeek today = now.getDayOfWeek();
int daysUntilSaturday = (DayOfWeek.SATURDAY.getValue()
- today.getValue() + 7) % 7;
System.out.println("Days until Saturday: " +
(daysUntilSaturday == 0 ? "It's Saturday!" : daysUntilSaturday));
// Quick stream example
Map> evenOdd = IntStream.rangeClosed(1, 20)
.boxed()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("Even numbers (1-20): " + evenOdd.get(true));
System.out.println("Odd numbers (1-20): " + evenOdd.get(false));
}
}
// Run: java DateInfo.java
// Output:
// Current time: Friday, February 28, 2026 at 3:42 PM
// Day of year: 59
// Week of year: 9
// Days until Saturday: 1
// Even numbers (1-20): [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// Odd numbers (1-20): [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
On Unix-like systems (Linux, macOS), you can make Java source files directly executable using a shebang line (#!). This means you can run a Java file just like a shell script or Python script — by typing its name, without even writing java in front of it.
#!/usr/bin/java --source 11
// File: sysinfo (no .java extension!)
// This file is executable directly on Unix systems
public class SysInfo {
public static void main(String[] args) {
System.out.println("=== System Information ===");
System.out.println("OS: " + System.getProperty("os.name")
+ " " + System.getProperty("os.version"));
System.out.println("Arch: " + System.getProperty("os.arch"));
System.out.println("Java: " + System.getProperty("java.version"));
System.out.println("JVM: " + System.getProperty("java.vm.name"));
System.out.println("User: " + System.getProperty("user.name"));
System.out.println("Home: " + System.getProperty("user.home"));
System.out.println("Directory: " + System.getProperty("user.dir"));
Runtime runtime = Runtime.getRuntime();
long maxMem = runtime.maxMemory() / (1024 * 1024);
long totalMem = runtime.totalMemory() / (1024 * 1024);
long freeMem = runtime.freeMemory() / (1024 * 1024);
System.out.println("Max Memory: " + maxMem + " MB");
System.out.println("Total Memory: " + totalMem + " MB");
System.out.println("Free Memory: " + freeMem + " MB");
System.out.println("Processors: " + runtime.availableProcessors());
}
}
To make it executable:
## Make the file executable chmod +x sysinfo ## Run it directly (no 'java' command needed!) ./sysinfo ## Output: ## === System Information === ## OS: Linux 5.15.0 ## Arch: amd64 ## Java: 11.0.2 ## JVM: OpenJDK 64-Bit Server VM ## User: developer ## Home: /home/developer ## Directory: /home/developer/scripts ## Max Memory: 3641 MB ## Total Memory: 245 MB ## Free Memory: 239 MB ## Processors: 8
Shebang rules:
#!/usr/bin/java --source 11 (specify the source version).java extension — name it like a script (e.g., sysinfo, deploy, cleanup)#!/usr/bin/env java --source 11 for portability (uses the java from PATH)#!/usr/bin/java --source 11
// File: hello (executable shebang script)
public class Hello {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Hello, World!");
} else {
System.out.println("Hello, " + String.join(" and ", args) + "!");
}
}
}
chmod +x hello ./hello ## Output: Hello, World! ./hello Alice Bob ## Output: Hello, Alice and Bob!
Single-file execution is powerful but has clear boundaries. Understanding these limitations helps you know when to use it and when a full project setup is the right choice.
| Rule | Detail |
|---|---|
| Single source file only | You cannot reference classes in other .java files. All code must be in one file. |
| Must have a main method | The first class in the file must have public static void main(String[] args). |
| No external dependencies | You cannot use --classpath to add JARs. Only the Java standard library is available. |
| No package statement needed | The file runs in the unnamed (default) package. You can include a package statement, but it is ignored. |
| File name does not matter | Unlike compiled Java, the filename does not need to match the class name. |
| Compiled every time | No caching — the file is recompiled on every execution. This adds startup overhead. |
| Multiple classes allowed | You can define multiple classes in one file. The first one must have main. |
| Source version | Use --source N flag to specify the Java language version to compile against. |
// File: RulesDemo.java
// The filename does NOT need to match the class name for single-file execution
// (But it must match for traditional javac compilation)
// No package statement needed (it would be ignored anyway)
import java.util.List;
import java.util.stream.Collectors;
// First class MUST have main
public class RulesDemo {
public static void main(String[] args) {
// Using a helper class defined later in this file -- works fine
MathHelper math = new MathHelper();
System.out.println("Factorial of 10: " + math.factorial(10));
System.out.println("Is 17 prime? " + math.isPrime(17));
System.out.println("Is 18 prime? " + math.isPrime(18));
// Using another helper class from this same file
StringHelper strings = new StringHelper();
System.out.println("Reversed: " + strings.reverse("Java 11"));
System.out.println("Palindrome 'racecar': "
+ strings.isPalindrome("racecar"));
System.out.println("Palindrome 'hello': "
+ strings.isPalindrome("hello"));
// Using Java standard library -- all of it is available
List primes = List.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
.stream()
.filter(n -> n > 10)
.collect(Collectors.toList());
System.out.println("Primes > 10: " + primes);
}
}
// Additional classes in the same file -- no problem
class MathHelper {
long factorial(int n) {
long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
boolean isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
}
class StringHelper {
String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
boolean isPalindrome(String s) {
String clean = s.toLowerCase().replaceAll("[^a-z0-9]", "");
return clean.equals(new StringBuilder(clean).reverse().toString());
}
}
// Run: java RulesDemo.java
// Output:
// Factorial of 10: 3628800
// Is 17 prime? true
// Is 18 prime? false
// Reversed: 11 avaJ
// Palindrome 'racecar': true
// Palindrome 'hello': false
// Primes > 10: [11, 13, 17, 19, 23, 29]
## ERROR: Cannot reference classes in other files ## If Helper.java is a separate file, this will NOT work: ## java Main.java (where Main.java imports from Helper.java) ## ERROR: Cannot add classpath dependencies ## java --classpath lib/gson.jar Script.java -- NOT SUPPORTED ## ERROR: If first class has no main method ## The first class MUST contain main(String[] args) ## WORKS: Specifying source version java --source 11 Script.java ## WORKS: Passing JVM options before the filename java -Xmx512m -Denv=prod Script.java arg1 arg2
Here is a side-by-side comparison showing how single-file execution simplifies various workflows:
| Before (Java 10 and earlier) | After (Java 11+) |
|---|---|
1. Write HelloWorld.java |
1. Write HelloWorld.java |
2. javac HelloWorld.java |
2. java HelloWorld.java |
3. Verify HelloWorld.class was created |
Done. |
4. java HelloWorld |
|
5. Clean up .class files |
| Before | After |
|---|---|
| Open IDE | Open any text editor |
| Create new project | Write code in a single file |
| Configure build settings | java Algorithm.java |
| Create package directory | Done. |
| Write code | |
| Build project | |
| Run | |
| Delete project when done |
| Feature | Traditional (javac + java) | Single-File (java only) |
|---|---|---|
| Commands needed | 2 (compile + run) | 1 (run) |
| Files created | .class files on disk |
None (in-memory only) |
| Multiple source files | Yes | No (single file only) |
| External JARs | Yes (–classpath) | No |
| Startup speed | Faster (pre-compiled) | Slower (compiles each run) |
| Shebang support | No | Yes |
| Best for | Production applications | Scripts, prototypes, learning |
Single-file execution is not meant to replace build tools for production applications. It fills a specific niche — situations where creating a full project is overkill. Here are the sweet spots:
Need to rename 500 files, parse a log, or generate test data? Write a Java script and run it directly:
// File: RenameFiles.java
// Quick script to rename files in a directory
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class RenameFiles {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println(
"Usage: java RenameFiles.java ");
System.out.println(
"Example: java RenameFiles.java ./photos vacation_");
return;
}
Path dir = Paths.get(args[0]);
String prefix = args[1];
if (!Files.isDirectory(dir)) {
System.out.println("Error: " + dir + " is not a directory");
return;
}
int[] counter = {1};
try (Stream files = Files.list(dir)) {
files.filter(Files::isRegularFile)
.sorted()
.forEach(file -> {
String ext = getExtension(file.getFileName().toString());
String newName = String.format("%s%03d%s",
prefix, counter[0]++, ext);
Path target = file.resolveSibling(newName);
try {
Files.move(file, target);
System.out.println(
file.getFileName() + " -> " + newName);
} catch (IOException e) {
System.err.println("Failed to rename "
+ file + ": " + e.getMessage());
}
});
}
System.out.println("Renamed " + (counter[0] - 1) + " files.");
}
static String getExtension(String filename) {
int dot = filename.lastIndexOf('.');
return dot > 0 ? filename.substring(dot) : "";
}
}
// Run: java RenameFiles.java ./photos vacation_
// Output:
// IMG_001.jpg -> vacation_001.jpg
// IMG_002.jpg -> vacation_002.jpg
// IMG_003.png -> vacation_003.png
// Renamed 3 files.
Want to test how a new Java feature works? Just write a quick file and run it. No project setup, no IDE required:
// File: TryNewFeatures.java
// Quick prototype to test Java features
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class TryNewFeatures {
public static void main(String[] args) {
// Test List.of() (Java 9+)
List fruits = List.of("Apple", "Banana", "Cherry");
System.out.println("Immutable list: " + fruits);
// Test Map.of() (Java 9+)
Map scores = Map.of(
"Alice", 95,
"Bob", 87,
"Charlie", 92
);
System.out.println("Scores: " + scores);
// Test Optional
Optional found = fruits.stream()
.filter(f -> f.startsWith("B"))
.findFirst();
found.ifPresent(f -> System.out.println("Found: " + f));
// Test String methods (Java 11)
String text = " Hello, Java 11! ";
System.out.println("strip(): '" + text.strip() + "'");
System.out.println("isBlank: " + " ".isBlank());
System.out.println("lines: " + "a\nb\nc".lines().count());
System.out.println("repeat: " + "ha".repeat(3));
}
}
// Run: java TryNewFeatures.java
// Output:
// Immutable list: [Apple, Banana, Cherry]
// Scores: {Alice=95, Charlie=92, Bob=87}
// Found: Banana
// strip(): 'Hello, Java 11!'
// isBlank: true
// lines: 3
// repeat: hahaha
System administrators and DevOps engineers who know Java can write utilities without switching to Bash or Python:
// File: HealthCheck.java
// Quick health check script for web services
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
public class HealthCheck {
public static void main(String[] args) {
List endpoints = args.length > 0
? List.of(args)
: List.of(
"https://www.google.com",
"https://api.github.com",
"https://jsonplaceholder.typicode.com/posts/1"
);
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
System.out.printf("%-45s %-8s %-10s%n",
"ENDPOINT", "STATUS", "TIME(ms)");
System.out.println("-".repeat(65));
for (String url : endpoints) {
checkEndpoint(client, url);
}
}
static void checkEndpoint(HttpClient client, String url) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
.build();
Instant start = Instant.now();
HttpResponse response = client.send(request,
HttpResponse.BodyHandlers.discarding());
long elapsed = Duration.between(start, Instant.now())
.toMillis();
String status = response.statusCode() < 400 ? "OK" : "FAIL";
System.out.printf("%-45s %-8s %d ms%n",
url, status + " (" + response.statusCode() + ")", elapsed);
} catch (Exception e) {
System.out.printf("%-45s %-8s %s%n",
url, "ERROR", e.getClass().getSimpleName());
}
}
}
// Run: java HealthCheck.java
// Output:
// ENDPOINT STATUS TIME(ms)
// -----------------------------------------------------------------
// https://www.google.com OK (200) 142 ms
// https://api.github.com OK (200) 287 ms
// https://jsonplaceholder.typicode.com/posts/1 OK (200) 198 ms
A script that reads a text file and produces statistics -- word count, line count, character frequencies, and the longest line:
// File: FileStats.java
// Analyze a text file and print statistics
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Collectors;
public class FileStats {
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println("Usage: java FileStats.java ");
return;
}
Path file = Path.of(args[0]);
if (!Files.exists(file)) {
System.out.println("Error: File not found: " + file);
return;
}
String content = Files.readString(file);
var lines = content.lines().collect(Collectors.toList());
String[] words = content.split("\\s+");
System.out.println("=== File Statistics: " + file.getFileName()
+ " ===");
System.out.println("Lines: " + lines.size());
System.out.println("Words: " + words.length);
System.out.println("Characters: " + content.length());
System.out.println("Bytes: " + Files.size(file));
// Average word length
double avgWordLen = Arrays.stream(words)
.mapToInt(String::length)
.average()
.orElse(0);
System.out.printf("Avg word length: %.1f chars%n", avgWordLen);
// Longest line
String longestLine = lines.stream()
.max(Comparator.comparingInt(String::length))
.orElse("");
System.out.println("Longest line: " + longestLine.length()
+ " chars");
System.out.println(" > " + longestLine.substring(0,
Math.min(80, longestLine.length()))
+ (longestLine.length() > 80 ? "..." : ""));
// Top 5 most common words
Map wordFreq = Arrays.stream(words)
.map(w -> w.toLowerCase().replaceAll("[^a-z]", ""))
.filter(w -> w.length() > 2)
.collect(Collectors.groupingBy(w -> w,
Collectors.counting()));
System.out.println("\nTop 5 words:");
wordFreq.entrySet().stream()
.sorted(Map.Entry.comparingByValue()
.reversed())
.limit(5)
.forEach(e -> System.out.printf(
" %-15s %d%n", e.getKey(), e.getValue()));
}
}
// Run: java FileStats.java myfile.txt
// Output:
// === File Statistics: myfile.txt ===
// Lines: 42
// Words: 350
// Characters: 2150
// Bytes: 2150
// Avg word length: 5.1 chars
// Longest line: 78 chars
// > This is the longest line in the file containing various words and information
//
// Top 5 words:
// the 23
// java 15
// and 12
// with 8
// for 7
// File: SystemReport.java
// Generate a system information report
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
public class SystemReport {
public static void main(String[] args) {
System.out.println("=" .repeat(50));
System.out.println(" SYSTEM REPORT - " + LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
System.out.println("=".repeat(50));
// Operating System
OperatingSystemMXBean os = ManagementFactory
.getOperatingSystemMXBean();
System.out.println("\n--- Operating System ---");
System.out.println("Name: " + os.getName());
System.out.println("Version: " + os.getVersion());
System.out.println("Architecture: " + os.getArch());
System.out.println("Processors: " + os.getAvailableProcessors());
System.out.printf("Load Average: %.2f%n", os.getSystemLoadAverage());
// JVM Info
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
System.out.println("\n--- JVM ---");
System.out.println("Name: " + runtime.getVmName());
System.out.println("Vendor: " + runtime.getVmVendor());
System.out.println("Version: " + runtime.getVmVersion());
Duration uptime = Duration.ofMillis(runtime.getUptime());
System.out.printf("Uptime: %d min %d sec%n",
uptime.toMinutes(), uptime.toSecondsPart());
// Memory
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
long heapUsed = memory.getHeapMemoryUsage().getUsed() / (1024 * 1024);
long heapMax = memory.getHeapMemoryUsage().getMax() / (1024 * 1024);
long nonHeapUsed = memory.getNonHeapMemoryUsage().getUsed()
/ (1024 * 1024);
System.out.println("\n--- Memory ---");
System.out.println("Heap Used: " + heapUsed + " MB");
System.out.println("Heap Max: " + heapMax + " MB");
System.out.println("Non-Heap Used: " + nonHeapUsed + " MB");
// Environment Variables (selected)
System.out.println("\n--- Environment ---");
printEnv("JAVA_HOME");
printEnv("PATH");
printEnv("HOME");
printEnv("USER");
System.out.println("\n" + "=".repeat(50));
System.out.println("Report complete.");
}
static void printEnv(String name) {
String value = System.getenv(name);
if (value != null) {
// Truncate long values
if (value.length() > 60) {
value = value.substring(0, 57) + "...";
}
System.out.println(name + ": " + value);
}
}
}
// Run: java SystemReport.java
// Output:
// ==================================================
// SYSTEM REPORT - 2026-02-28 15:42:00
// ==================================================
//
// --- Operating System ---
// Name: Mac OS X
// Version: 13.4
// Architecture: aarch64
// Processors: 10
// Load Average: 2.45
//
// --- JVM ---
// Name: OpenJDK 64-Bit Server VM
// Vendor: Eclipse Adoptium
// Version: 11.0.2+9
// Uptime: 0 min 1 sec
//
// --- Memory ---
// Heap Used: 12 MB
// Heap Max: 4096 MB
// Non-Heap Used: 8 MB
//
// --- Environment ---
// JAVA_HOME: /Library/Java/JavaVirtualMachines/temurin-11.jdk/...
// PATH: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library...
// HOME: /Users/developer
// USER: developer
//
// ==================================================
// Report complete.
// File: CsvParser.java
// Parse a CSV file and display formatted results
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;
public class CsvParser {
public static void main(String[] args) throws IOException {
if (args.length == 0) {
// Create a sample CSV for demonstration
String sampleCsv = "Name,Department,Salary,YearsExp\n"
+ "Alice,Engineering,95000,8\n"
+ "Bob,Marketing,72000,5\n"
+ "Charlie,Engineering,105000,12\n"
+ "Diana,HR,68000,3\n"
+ "Eve,Engineering,115000,15\n"
+ "Frank,Marketing,78000,7\n"
+ "Grace,HR,71000,4\n"
+ "Henry,Engineering,92000,6";
Files.writeString(Path.of("sample.csv"), sampleCsv);
System.out.println("Created sample.csv");
System.out.println(
"Run: java CsvParser.java sample.csv\n");
args = new String[]{"sample.csv"};
}
Path csvFile = Path.of(args[0]);
List lines = Files.readAllLines(csvFile);
if (lines.isEmpty()) {
System.out.println("Error: Empty CSV file");
return;
}
// Parse header
String[] headers = lines.get(0).split(",");
// Parse rows
List rows = new ArrayList<>();
for (int i = 1; i < lines.size(); i++) {
rows.add(lines.get(i).split(","));
}
// Print formatted table
System.out.println("\n=== CSV Data (" + rows.size()
+ " rows) ===\n");
System.out.printf("%-12s %-15s %-10s %-10s%n",
(Object[]) headers);
System.out.println("-".repeat(50));
for (String[] row : rows) {
System.out.printf("%-12s %-15s $%-9s %-10s%n",
(Object[]) row);
}
// Statistics (assuming Salary is column 2)
System.out.println("\n=== Statistics ===\n");
DoubleSummaryStatistics salaryStats = rows.stream()
.mapToDouble(row -> Double.parseDouble(row[2]))
.summaryStatistics();
System.out.printf("Total Records: %d%n", salaryStats.getCount());
System.out.printf("Average Salary: $%,.0f%n",
salaryStats.getAverage());
System.out.printf("Min Salary: $%,.0f%n",
salaryStats.getMin());
System.out.printf("Max Salary: $%,.0f%n",
salaryStats.getMax());
System.out.printf("Total Payroll: $%,.0f%n",
salaryStats.getSum());
// Group by department
System.out.println("\n=== By Department ===\n");
rows.stream()
.collect(Collectors.groupingBy(
row -> row[1],
Collectors.averagingDouble(row ->
Double.parseDouble(row[2]))))
.forEach((dept, avg) ->
System.out.printf("%-15s Avg Salary: $%,.0f%n",
dept, avg));
}
}
// Run: java CsvParser.java sample.csv
// Output:
// === CSV Data (8 rows) ===
//
// Name Department Salary YearsExp
// --------------------------------------------------
// Alice Engineering $95000 8
// Bob Marketing $72000 5
// Charlie Engineering $105000 12
// Diana HR $68000 3
// Eve Engineering $115000 15
// Frank Marketing $78000 7
// Grace HR $71000 4
// Henry Engineering $92000 6
//
// === Statistics ===
//
// Total Records: 8
// Average Salary: $87,000
// Min Salary: $68,000
// Max Salary: $115,000
// Total Payroll: $696,000
//
// === By Department ===
//
// Engineering Avg Salary: $101,750
// Marketing Avg Salary: $75,000
// HR Avg Salary: $69,500
Single-file execution is a tool, and like any tool, it works best when you use it for the right job:
| Situation | Use Single-File | Use Full Project |
|---|---|---|
| Quick prototype / test an idea | Yes | |
| One-time script (rename files, parse data) | Yes | |
| Learning / teaching Java | Yes | |
| Code interview practice | Yes | |
| Need external libraries (Gson, JDBC driver) | Yes | |
| Multiple source files / packages | Yes | |
| Production application | Yes | |
| Needs unit tests | Yes | |
| Startup time matters (called frequently) | Yes (pre-compiled) | |
| Shared/reusable code | Yes |
Naming conventions:
.java files: Use PascalCase matching the main class name (e.g., FileStats.java, HealthCheck.java)health-check, file-stats, deploy)~/scripts or ~/bin directory added to your PATHTips for writing good single-file programs:
args[] for input rather than hardcoding values. Print usage instructions when no arguments are given.java.nio.file, java.net.http, java.time, and java.util.stream cover most scripting needs.