Add jep512 module: compact source files and instance main methods on JDK 25 and 27
Runnable sources, broken examples and captured transcripts for the ankurm.com JEP 512 article, including the measured launch order and a private-constructor difference between 25 and 27. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
This commit is contained in:
@@ -56,6 +56,14 @@ Timings, RSS, GC counts and the Vector species are machine dependent; treat them
|
||||
| 16 | [Stream Gatherers (JEP 485)](docs/16-gatherers.md) |
|
||||
| 17 | [Scoped values vs ThreadLocal (JEP 506)](docs/17-scoped-values-migration.md) |
|
||||
|
||||
## Standalone article modules (no `docs/` folder)
|
||||
|
||||
These modules keep only sources, a `run.sh` and their transcripts (`output/`). The explanation lives in the article itself, in collapsible sections.
|
||||
|
||||
| Module | Article | What it demonstrates |
|
||||
|---|---|---|
|
||||
| [`jep512/`](jep512) | Compact Source Files and Instance Main Methods in Java 25 (JEP 512) | `java Hello.java` on JDK 21 vs 25 vs 27, the class `javac` builds around `void main()`, implicit `java.base` imports, `java.lang.IO`, the measured launch order, a private-constructor difference between 25 and 27 |
|
||||
|
||||
## Captured output (`docs/output/`)
|
||||
|
||||
| File | Produced by | What it is |
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# jep512 — Compact Source Files and Instance Main Methods (JEP 512, final in Java 25)
|
||||
|
||||
Companion code for the ankurm.com article **Compact Source Files and Instance Main Methods in Java 25 (JEP 512)**. All explanation lives in the
|
||||
article; this folder holds only the runnable sources and the transcripts they produced.
|
||||
|
||||
```bash
|
||||
JDK25=/path/to/jdk-25 JDK27=/path/to/jdk-27 [JDK21=/path/to/jdk-21] ./run.sh # regenerates output/*.txt
|
||||
java src/Hello.java # or just run one
|
||||
```
|
||||
|
||||
| Folder | What is in it |
|
||||
|---|---|
|
||||
| `src/` | programs that run (`java src/Name.java`) |
|
||||
| `broken/` | sources that fail on purpose; the compiler's own message is captured in `output/06-*.txt` and `output/07-*.txt` |
|
||||
| `multi/` | `App.java` + `Greeter.java`: the source launcher finding a second file in the same directory |
|
||||
| `output/` | every transcript quoted in the article (`01`-`08`) |
|
||||
|
||||
Machine-independent apart from the JDK build lines. Tested on Temurin 25.0.4.1+1 and Temurin 27+35.
|
||||
@@ -0,0 +1,4 @@
|
||||
class CtorWithArgs {
|
||||
CtorWithArgs(int x) {}
|
||||
void main() { IO.println("instance main, but no zero-argument constructor"); }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
println("hello");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void greet() { IO.println("hi"); }
|
||||
@@ -0,0 +1,4 @@
|
||||
void main() {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
IO.println(client);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
void main() {
|
||||
Target t = new Target();
|
||||
t.main();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class PrivateCtor {
|
||||
private PrivateCtor() {}
|
||||
void main() { IO.println("instance main() reached through a PRIVATE zero-argument constructor"); }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class PrivateMain {
|
||||
private static void main() { IO.println("private static main"); }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void main() { IO.println("Target"); }
|
||||
@@ -0,0 +1,5 @@
|
||||
package demo;
|
||||
|
||||
void main() {
|
||||
IO.println("a package declaration in a compact file");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void main(int count) { IO.println("count = " + count); }
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
IO.println(Greeter.greet("multi-file source launcher"));
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class Greeter {
|
||||
static String greet(String who) { return "Hello, " + who + " (from a second file)"; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
$ cat src/Hello.java
|
||||
// The smallest complete Java 25 program. No class, no public, no static, no String[] args.
|
||||
void main() {
|
||||
IO.println("Hello, world");
|
||||
}
|
||||
|
||||
$ java src/Hello.java (JDK 25: OpenJDK Runtime Environment Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS))
|
||||
Hello, world
|
||||
|
||||
$ java src/Hello.java (JDK 27: OpenJDK Runtime Environment Temurin-27+35 (build 27+35))
|
||||
Hello, world
|
||||
|
||||
$ ls src/*.class
|
||||
ls: cannot access 'src/*.class': No such file or directory
|
||||
|
||||
$ java src/Hello.java (JDK 21: OpenJDK Runtime Environment (build 21.0.10+7-Ubuntu-124.04))
|
||||
src/Hello.java:2: error: unnamed classes are a preview feature and are disabled by default.
|
||||
void main() {
|
||||
^
|
||||
(use --enable-preview to enable unnamed classes)
|
||||
1 error
|
||||
error: compilation failed
|
||||
@@ -0,0 +1,22 @@
|
||||
$ javac -d out src/Hello.java && java -cp out Hello
|
||||
Hello, world
|
||||
|
||||
$ javap -p -cp out Hello
|
||||
Compiled from "Hello.java"
|
||||
final class Hello {
|
||||
Hello();
|
||||
void main();
|
||||
}
|
||||
|
||||
$ javap -v -cp out Hello | grep -E 'major|flags'
|
||||
major version: 69
|
||||
flags: (0x0030) ACC_FINAL, ACC_SUPER
|
||||
|
||||
$ java Reflect (asks the loaded class)
|
||||
name : Hello
|
||||
final : true
|
||||
public : false
|
||||
package : '' (unnamed)
|
||||
superclass : java.lang.Object
|
||||
declared : [void Hello.main()]
|
||||
constructors: [Hello()]
|
||||
@@ -0,0 +1,25 @@
|
||||
$ java src/Members.java
|
||||
HELLO!
|
||||
apple, banana, cherry
|
||||
{apple=5, banana=6, cherry=6}
|
||||
2.10
|
||||
Point[x=3, y=4] is 5.0 from the origin
|
||||
calls so far: 1
|
||||
|
||||
$ java src/WithArgs.java one two
|
||||
args.length = 2
|
||||
arg: one
|
||||
arg: two
|
||||
|
||||
$ java src/InstanceMainInClass.java
|
||||
hello from an instance field
|
||||
this class is InstanceMainInClass, final? false
|
||||
|
||||
$ java src/NeedsImport.java (java.net.http is outside java.base: a normal import fixes it)
|
||||
default HTTP version: HTTP_2
|
||||
|
||||
$ java src/NeedsModuleImport.java (or import the whole module)
|
||||
default HTTP version: HTTP_2
|
||||
|
||||
$ java multi/App.java (App.java + Greeter.java in one directory)
|
||||
Hello, multi-file source launcher (from a second file)
|
||||
@@ -0,0 +1,22 @@
|
||||
$ printf 'Ankur\n30\n' | java src/ReadLn.java
|
||||
Your name? Your age? Hello Ankur, 30 is a good age to learn Java.
|
||||
no newline after this, newline after this
|
||||
|
||||
$ java src/IoInAnyClass.java
|
||||
IO.println works in a normal class with no import
|
||||
|
||||
$ java src/StaticImportIO.java (import static java.lang.IO.* makes the bare form work)
|
||||
println without the IO. prefix, after import static java.lang.IO.*
|
||||
|
||||
$ java broken/NoIoPrefix.java (JDK 27: same error as on 25)
|
||||
broken/NoIoPrefix.java:2: error: cannot find symbol
|
||||
println("hello");
|
||||
^
|
||||
symbol: method println(String)
|
||||
location: class NoIoPrefix
|
||||
1 error
|
||||
error: compilation failed
|
||||
|
||||
$ printf '' | java src/ReadLn.java (stdin closed: what does readln return?)
|
||||
Your name? Your age? Hello null, null is a good age to learn Java.
|
||||
no newline after this, newline after this
|
||||
@@ -0,0 +1,22 @@
|
||||
== OpenJDK Runtime Environment Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS)
|
||||
SS_i0 declares: static void main(String[] a) void main() -> ran: static main(String[])
|
||||
S0_iS declares: static void main() void main(String[] a) -> ran: instance main(String[])
|
||||
SS_S0 declares: static void main(String[] a) static void main() -> ran: static main(String[])
|
||||
IS_I0 declares: void main(String[] a) void main() -> ran: instance main(String[])
|
||||
-- same four classes, compiled first and launched as a class (java -cp out NAME)
|
||||
SS_i0 -> ran: static main(String[])
|
||||
S0_iS -> ran: instance main(String[])
|
||||
SS_S0 -> ran: static main(String[])
|
||||
IS_I0 -> ran: instance main(String[])
|
||||
|
||||
== OpenJDK Runtime Environment Temurin-27+35 (build 27+35)
|
||||
SS_i0 declares: static void main(String[] a) void main() -> ran: static main(String[])
|
||||
S0_iS declares: static void main() void main(String[] a) -> ran: instance main(String[])
|
||||
SS_S0 declares: static void main(String[] a) static void main() -> ran: static main(String[])
|
||||
IS_I0 declares: void main(String[] a) void main() -> ran: instance main(String[])
|
||||
-- same four classes, compiled first and launched as a class (java -cp out NAME)
|
||||
SS_i0 -> ran: static main(String[])
|
||||
S0_iS -> ran: instance main(String[])
|
||||
SS_S0 -> ran: static main(String[])
|
||||
IS_I0 -> ran: instance main(String[])
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
$ java broken/NoMain.java (JDK 25)
|
||||
broken/NoMain.java:1: error: compact source file does not have main method in the form of void main() or void main(String[] args)
|
||||
void greet() { IO.println("hi"); }
|
||||
^
|
||||
1 error
|
||||
error: compilation failed
|
||||
|
||||
$ java broken/NoIoPrefix.java (JDK 25)
|
||||
broken/NoIoPrefix.java:2: error: cannot find symbol
|
||||
println("hello");
|
||||
^
|
||||
symbol: method println(String)
|
||||
location: class NoIoPrefix
|
||||
1 error
|
||||
error: compilation failed
|
||||
|
||||
$ java broken/NotInJavaBase.java (JDK 25)
|
||||
broken/NotInJavaBase.java:2: error: cannot find symbol
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
^
|
||||
symbol: class HttpClient
|
||||
location: class NotInJavaBase
|
||||
broken/NotInJavaBase.java:2: error: cannot find symbol
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
^
|
||||
symbol: variable HttpClient
|
||||
location: class NotInJavaBase
|
||||
2 errors
|
||||
error: compilation failed
|
||||
|
||||
$ java broken/WithPackage.java (JDK 25)
|
||||
broken/WithPackage.java:1: error: compact source file should not have package declaration
|
||||
package demo;
|
||||
^
|
||||
1 error
|
||||
error: compilation failed
|
||||
|
||||
$ javac -d out broken/Other.java broken/Target.java (JDK 25; Target.java is a compact file)
|
||||
broken/Other.java:2: error: Target is abstract; cannot be instantiated
|
||||
Target t = new Target();
|
||||
^
|
||||
broken/Other.java:3: error: cannot find symbol
|
||||
t.main();
|
||||
^
|
||||
symbol: method main()
|
||||
location: variable t of type Target
|
||||
2 errors
|
||||
|
||||
$ java broken/Other.java (JDK 25; source launcher, Target.java sits next to it)
|
||||
broken/Other.java:2: error: Target is abstract; cannot be instantiated
|
||||
Target t = new Target();
|
||||
^
|
||||
1 error
|
||||
error: compilation failed
|
||||
|
||||
$ java broken/WrongParam.java (JDK 25)
|
||||
broken/WrongParam.java:1: error: compact source file does not have main method in the form of void main() or void main(String[] args)
|
||||
void main(int count) { IO.println("count = " + count); }
|
||||
^
|
||||
1 error
|
||||
error: compilation failed
|
||||
|
||||
$ java broken/PrivateMain.java (JDK 25)
|
||||
error: can't find main(String[]) or main() method in class: PrivateMain
|
||||
|
||||
$ java broken/CtorWithArgs.java (JDK 25)
|
||||
error: can't find no argument constructor in class: CtorWithArgs
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
$ java broken/PrivateCtor.java (OpenJDK Runtime Environment Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS))
|
||||
instance main() reached through a PRIVATE zero-argument constructor
|
||||
|
||||
$ java broken/PrivateCtor.java (OpenJDK Runtime Environment Temurin-27+35 (build 27+35))
|
||||
error: no non-private zero argument constructor found in class PrivateCtor
|
||||
remove private from existing constructor or define as:
|
||||
public PrivateCtor()
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
$ javap -p java.lang.IO
|
||||
Compiled from "IO.java"
|
||||
public final class java.lang.IO {
|
||||
private static java.io.BufferedReader br;
|
||||
private java.lang.IO();
|
||||
public static void println(java.lang.Object);
|
||||
public static void println();
|
||||
public static void print(java.lang.Object);
|
||||
public static java.lang.String readln();
|
||||
public static java.lang.String readln(java.lang.String);
|
||||
static synchronized java.io.BufferedReader reader();
|
||||
}
|
||||
|
||||
$ javap -p -c java.lang.IO | grep -E 'public static|System.out|readLine|IOError'
|
||||
public static void println(java.lang.Object);
|
||||
0: getstatic #14 // Field java/lang/System.out:Ljava/io/PrintStream;
|
||||
public static void println();
|
||||
0: getstatic #14 // Field java/lang/System.out:Ljava/io/PrintStream;
|
||||
public static void print(java.lang.Object);
|
||||
0: getstatic #14 // Field java/lang/System.out:Ljava/io/PrintStream;
|
||||
10: invokevirtual #31 // Method java/io/PrintStream.flush:()V
|
||||
public static java.lang.String readln();
|
||||
3: invokevirtual #40 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
|
||||
8: new #48 // class java/io/IOError
|
||||
13: invokespecial #50 // Method java/io/IOError."<init>":(Ljava/lang/Throwable;)V
|
||||
public static java.lang.String readln(java.lang.String);
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env bash
|
||||
# JEP 512 (Compact Source Files and Instance Main Methods): final in JDK 25, no --enable-preview.
|
||||
# Regenerates every file in output/. Needs JDK 25 and JDK 27; a JDK 21 is optional (for the "before" transcript).
|
||||
# JDK25=/path JDK27=/path [JDK21=/path] ./run.sh
|
||||
set -uo pipefail
|
||||
unset JAVA_TOOL_OPTIONS
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
JDK25="${JDK25:-/opt/jdks/jdk-25.0.4.1+1}"; JDK27="${JDK27:-/opt/jdks/jdk27}"; JDK21="${JDK21:-}"
|
||||
OUT="$HERE/output"; mkdir -p "$OUT"; B="$(mktemp -d)"; trap 'rm -rf "$B"' EXIT
|
||||
cd "$HERE"
|
||||
v() { "$1/bin/java" -version 2>&1 | sed -n 2p; } # runtime line only, no vendor banner noise
|
||||
|
||||
# 01 - the source launcher: one command, no javac, no class file left behind
|
||||
{
|
||||
echo "\$ cat src/Hello.java"; cat src/Hello.java; echo
|
||||
echo "\$ java src/Hello.java (JDK 25: $(v $JDK25))"; "$JDK25/bin/java" src/Hello.java 2>&1
|
||||
echo; echo "\$ java src/Hello.java (JDK 27: $(v $JDK27))"; "$JDK27/bin/java" src/Hello.java 2>&1
|
||||
echo; echo "\$ ls src/*.class"; ls src/*.class 2>&1 | sed 's/^/ /'
|
||||
if [ -n "$JDK21" ]; then
|
||||
echo; echo "\$ java src/Hello.java (JDK 21: $(v $JDK21))"; "$JDK21/bin/java" src/Hello.java 2>&1
|
||||
fi
|
||||
} > "$OUT/01-source-launcher.txt"
|
||||
|
||||
# 02 - javac produces an ordinary class file; javap shows what the compiler invented
|
||||
{
|
||||
echo "\$ javac -d out src/Hello.java && java -cp out Hello"
|
||||
"$JDK25/bin/javac" -d "$B/o" src/Hello.java 2>&1 && "$JDK25/bin/java" -cp "$B/o" Hello 2>&1
|
||||
echo; echo "\$ javap -p -cp out Hello"; "$JDK25/bin/javap" -p -cp "$B/o" Hello 2>&1
|
||||
echo; echo "\$ javap -v -cp out Hello | grep -E 'major|flags'"
|
||||
"$JDK25/bin/javap" -v -cp "$B/o" Hello 2>&1 | grep -E 'major version|^ flags' | head -2
|
||||
echo; echo "\$ java Reflect (asks the loaded class)"
|
||||
"$JDK25/bin/javac" -cp "$B/o" -d "$B/o" src/Reflect.java 2>&1 && "$JDK25/bin/java" -cp "$B/o" Reflect 2>&1
|
||||
} > "$OUT/02-javac-and-javap.txt"
|
||||
|
||||
# 03 - the class body: members, nested types, implicit imports
|
||||
{
|
||||
echo "\$ java src/Members.java"; "$JDK25/bin/java" src/Members.java 2>&1
|
||||
echo; echo "\$ java src/WithArgs.java one two"; "$JDK25/bin/java" src/WithArgs.java one two 2>&1
|
||||
echo; echo "\$ java src/InstanceMainInClass.java"; "$JDK25/bin/java" src/InstanceMainInClass.java 2>&1
|
||||
echo; echo "\$ java src/NeedsImport.java (java.net.http is outside java.base: a normal import fixes it)"; "$JDK25/bin/java" src/NeedsImport.java 2>&1
|
||||
echo; echo "\$ java src/NeedsModuleImport.java (or import the whole module)"; "$JDK25/bin/java" src/NeedsModuleImport.java 2>&1
|
||||
echo; echo "\$ java multi/App.java (App.java + Greeter.java in one directory)"; "$JDK25/bin/java" multi/App.java 2>&1
|
||||
} > "$OUT/03-members-and-imports.txt"
|
||||
|
||||
# 04 - the IO helper
|
||||
{
|
||||
echo "\$ printf 'Ankur\n30\n' | java src/ReadLn.java"; printf 'Ankur\n30\n' | "$JDK25/bin/java" src/ReadLn.java 2>&1
|
||||
echo; echo "\$ java src/IoInAnyClass.java"; "$JDK25/bin/java" src/IoInAnyClass.java 2>&1
|
||||
echo; echo "\$ java src/StaticImportIO.java (import static java.lang.IO.* makes the bare form work)"; "$JDK25/bin/java" src/StaticImportIO.java 2>&1
|
||||
echo; echo "\$ java broken/NoIoPrefix.java (JDK 27: same error as on 25)"; "$JDK27/bin/java" broken/NoIoPrefix.java 2>&1 | head -8
|
||||
echo; echo "\$ printf '' | java src/ReadLn.java (stdin closed: what does readln return?)"
|
||||
printf '' | "$JDK25/bin/java" src/ReadLn.java 2>&1
|
||||
} > "$OUT/04-io-helper.txt"
|
||||
|
||||
# 05 - which main wins. Generate every pair of mains that can coexist and ask the launcher.
|
||||
gen() { # name, body
|
||||
printf 'class %s {\n%s\n}\n' "$1" "$2" > "$B/$1.java"
|
||||
}
|
||||
gen SS_i0 ' static void main(String[] a) { IO.println("static main(String[])"); }
|
||||
void main() { IO.println("instance main()"); }'
|
||||
gen S0_iS ' static void main() { IO.println("static main()"); }
|
||||
void main(String[] a) { IO.println("instance main(String[])"); }'
|
||||
gen SS_S0 ' static void main(String[] a) { IO.println("static main(String[])"); }
|
||||
static void main() { IO.println("static main()"); }'
|
||||
gen IS_I0 ' void main(String[] a) { IO.println("instance main(String[])"); }
|
||||
void main() { IO.println("instance main()"); }'
|
||||
{
|
||||
for j in "$JDK25" "$JDK27"; do
|
||||
echo "== $(v $j)"
|
||||
for c in SS_i0 S0_iS SS_S0 IS_I0; do
|
||||
printf '%-8s declares: %-58s -> ran: ' "$c" "$(grep -o '\(static \)\?void main([^)]*)' "$B/$c.java" | tr '\n' ' ')"
|
||||
"$j/bin/java" "$B/$c.java" 2>&1 | head -1
|
||||
done
|
||||
echo "-- same four classes, compiled first and launched as a class (java -cp out NAME)"
|
||||
rm -rf "$B/cls"; mkdir -p "$B/cls"; "$j/bin/javac" -d "$B/cls" "$B"/SS_i0.java "$B"/S0_iS.java "$B"/SS_S0.java "$B"/IS_I0.java 2>&1
|
||||
for c in SS_i0 S0_iS SS_S0 IS_I0; do
|
||||
printf '%-8s -> ran: ' "$c"; "$j/bin/java" -cp "$B/cls" "$c" 2>&1 | head -1
|
||||
done
|
||||
echo
|
||||
done
|
||||
} > "$OUT/05-launch-order.txt"
|
||||
|
||||
# 06 - every way to get it wrong, with the compiler's own words
|
||||
{
|
||||
for f in NoMain NoIoPrefix NotInJavaBase WithPackage Other WrongParam PrivateMain CtorWithArgs; do
|
||||
if [ "$f" = Other ]; then
|
||||
echo "\$ javac -d out broken/Other.java broken/Target.java (JDK 25; Target.java is a compact file)"
|
||||
"$JDK25/bin/javac" -d "$B/x" broken/Other.java broken/Target.java 2>&1 | head -12
|
||||
echo; echo "\$ java broken/Other.java (JDK 25; source launcher, Target.java sits next to it)"
|
||||
"$JDK25/bin/java" broken/Other.java 2>&1 | head -12
|
||||
else
|
||||
echo "\$ java broken/$f.java (JDK 25)"; "$JDK25/bin/java" broken/$f.java 2>&1 | head -12
|
||||
fi
|
||||
echo
|
||||
done
|
||||
} > "$OUT/06-compile-errors.txt"
|
||||
|
||||
# 07 - the one behaviour that differs between 25 and 27
|
||||
{
|
||||
for j in "$JDK25" "$JDK27"; do
|
||||
echo "\$ java broken/PrivateCtor.java ($(v $j))"
|
||||
"$j/bin/java" broken/PrivateCtor.java 2>&1 | head -6; echo
|
||||
done
|
||||
} > "$OUT/07-private-constructor-25-vs-27.txt"
|
||||
|
||||
# 08 - what java.lang.IO actually is
|
||||
{
|
||||
echo "\$ javap -p java.lang.IO"; "$JDK25/bin/javap" -p java.lang.IO 2>&1
|
||||
echo; echo "\$ javap -p -c java.lang.IO | grep -E 'public static|System.out|readLine|IOError'"
|
||||
"$JDK25/bin/javap" -p -c java.lang.IO 2>&1 | grep -E 'public static|System.out|readLine|IOError|flush'
|
||||
} > "$OUT/08-io-class.txt"
|
||||
|
||||
echo "wrote $OUT"
|
||||
@@ -0,0 +1,6 @@
|
||||
// The same program the way every tutorial before Java 25 wrote it, for the side-by-side in the post.
|
||||
public class Classic {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// The smallest complete Java 25 program. No class, no public, no static, no String[] args.
|
||||
void main() {
|
||||
IO.println("Hello, world");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Instance main does not need a compact file. An ordinary class works too, and then it can use its own fields.
|
||||
class InstanceMainInClass {
|
||||
private final String greeting = "hello from an instance field";
|
||||
|
||||
void main() {
|
||||
IO.println(greeting);
|
||||
IO.println("this class is " + getClass().getName() + ", final? " + java.lang.reflect.Modifier.isFinal(getClass().getModifiers()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IO lives in java.lang, so IO.println needs no import in an ordinary class either.
|
||||
public class IoInAnyClass {
|
||||
public static void main(String[] args) {
|
||||
IO.println("IO.println works in a normal class with no import");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// A compact source file is a whole class body: fields, methods, and nested types all sit at the top level.
|
||||
// Nothing here is imported. List, Map, TreeMap, Collectors and BigDecimal all live in java.base.
|
||||
String greeting = "hello";
|
||||
int calls = 0;
|
||||
|
||||
record Point(int x, int y) {
|
||||
double distance() { return Math.sqrt(x * x + y * y); }
|
||||
}
|
||||
|
||||
String shout(String s) {
|
||||
calls++;
|
||||
return s.toUpperCase() + "!";
|
||||
}
|
||||
|
||||
void main() {
|
||||
List<String> words = List.of("banana", "apple", "cherry");
|
||||
Map<String, Integer> lengths = new TreeMap<>();
|
||||
for (String w : words) lengths.put(w, w.length());
|
||||
IO.println(shout(greeting));
|
||||
IO.println(words.stream().sorted().collect(Collectors.joining(", ")));
|
||||
IO.println(lengths);
|
||||
IO.println(new BigDecimal("1.10").add(BigDecimal.ONE));
|
||||
IO.println(new Point(3, 4) + " is " + new Point(3, 4).distance() + " from the origin");
|
||||
IO.println("calls so far: " + calls);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// java.net.http is a separate module from java.base, so HttpClient is NOT implicitly imported.
|
||||
// A normal single-type import fixes it, exactly as in any other Java file.
|
||||
import java.net.http.HttpClient;
|
||||
|
||||
void main() {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
IO.println("default HTTP version: " + client.version());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// The other fix: import the whole module (JEP 511, final in Java 25). One line covers java.net.http.
|
||||
import module java.net.http;
|
||||
|
||||
void main() {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
IO.println("default HTTP version: " + client.version());
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// java.lang.IO has three static methods: print, println and readln.
|
||||
void main() {
|
||||
String name = IO.readln("Your name? ");
|
||||
String age = IO.readln("Your age? ");
|
||||
IO.println("Hello " + name + ", " + age + " is a good age to learn Java.");
|
||||
IO.print("no newline after this, ");
|
||||
IO.println("newline after this");
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// What did the compiler make of a compact source file? Ask the class itself.
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Reflect {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Class<?> c = Class.forName("Hello");
|
||||
IO.println("name : " + c.getName());
|
||||
IO.println("final : " + Modifier.isFinal(c.getModifiers()));
|
||||
IO.println("public : " + Modifier.isPublic(c.getModifiers()));
|
||||
IO.println("package : '" + c.getPackageName() + "' (unnamed)");
|
||||
IO.println("superclass : " + c.getSuperclass().getName());
|
||||
IO.println("declared : " + Arrays.stream(c.getDeclaredMethods()).map(m -> m.toString()).sorted().toList());
|
||||
IO.println("constructors: " + Arrays.toString(c.getDeclaredConstructors()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// The bare println form works only if you import IO's static methods yourself.
|
||||
import static java.lang.IO.*;
|
||||
|
||||
void main() {
|
||||
println("println without the IO. prefix, after import static java.lang.IO.*");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// The String[] parameter is still allowed; it is just no longer required.
|
||||
void main(String[] args) {
|
||||
IO.println("args.length = " + args.length);
|
||||
for (String a : args) IO.println(" arg: " + a);
|
||||
}
|
||||
Reference in New Issue
Block a user