学Java的时候,最烦的就是写个Hello World还得声明个类,main方法还得是static和public,参数还得是String[] args,一堆模板代码,看着就烦。JDK 23的JEP 477整了个新活,可以不用显式声明类,main方法也不用static了,写简单程序省事多了。
鹏磊我刚开始学Java那会儿,写个最简单的程序都得这样:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
现在有了JEP 477,直接这样写就完事了:
void main() {
println("Hello, World!");
}
简单多了,特别是对初学者来说,门槛降低了不少。这个特性主要是为了让写单类程序更方便,不用再写那些模板代码了。
JEP 477 的核心特性
JEP 477引入了两个主要特性:隐式声明的类和实例主方法。这两个特性配合起来,让写简单Java程序变得更容易了。
隐式声明的类
隐式声明的类就是编译器自动帮你创建的类。如果你在源文件里写了方法和字段,但是没有显式声明类,编译器就会自动创建一个隐式类,把这些方法和字段都放进去。
这个隐式类有几个特点:
- 自动继承Object:隐式类自动继承java.lang.Object,不用你写extends
- 不实现接口:隐式类不实现任何接口,就是最基础的类
- 不能通过名称引用:隐式类没有名字,你不能在代码里写它的名字来引用它
- 自动导入java.base:隐式类自动导入java.base模块的所有公共类,不用写import
// 传统方式,得显式声明类
public class Calculator {
int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(1, 2));
}
}
// 新方式,不用声明类了
int add(int a, int b) {
return a + b;
}
void main() {
println(add(1, 2)); // 直接调用,不用创建对象
}
编译器会自动创建一个隐式类,把add方法和main方法都放进去。main方法可以直接调用add方法,因为它们都在同一个隐式类里。
实例主方法
实例主方法就是不用static的main方法。传统的main方法必须是static的,因为JVM调用的时候还没有对象实例。但是有了隐式类,JVM可以先创建隐式类的实例,然后调用实例的main方法。
实例主方法的特点:
- 不需要static:main方法可以是实例方法,不用static修饰
- 不需要public:main方法可以是包私有的,不用public修饰
- 可以没有参数:main方法可以没有String[] args参数,如果不需要命令行参数的话
- 可以直接访问实例成员:因为是实例方法,可以直接访问类的字段和其他实例方法
// 传统方式,main必须是static
public class Counter {
private int count = 0;
void increment() {
count++;
}
public static void main(String[] args) {
Counter c = new Counter(); // 得先创建对象
c.increment();
System.out.println(c.count);
}
}
// 新方式,main可以是实例方法
int count = 0; // 实例字段
void increment() {
count++; // 直接访问实例字段
}
void main() {
increment(); // 直接调用实例方法
println(count); // 直接访问实例字段
}
这样写简单多了,不用创建对象,不用static,代码更直观。
自动导入和简化的I/O
自动导入java.base模块
隐式声明的类自动导入java.base模块的所有公共类,这意味着你可以直接使用java.lang、java.util、java.io等包里的类,不用写import。
// 传统方式,得写import
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
public class Example {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
}
}
// 新方式,自动导入,不用写import
void main() {
List<String> list = new ArrayList<>(); // 直接用,不用import
Map<String, Integer> map = new HashMap<>(); // 直接用,不用import
}
这样写代码的时候,不用再写一堆import语句了,特别是用java.base模块里的类的时候。
简化的控制台I/O
JEP 477还引入了新的java.io.IO类,提供了简化的控制台I/O方法。这个类提供了静态方法,可以直接调用,不用写System.out.println这么长了。
// 传统方式
System.out.println("Hello"); // 太长了
System.out.print("World");
String line = System.console().readLine(); // 读取输入也麻烦
// 新方式,用IO类的静态方法
println("Hello"); // 简洁多了
print("World");
String line = readLine(); // 读取输入也简单了
IO类提供了这些静态方法:
println(Object):打印对象并换行print(Object):打印对象不换行readLine():读取一行输入readLine(String prompt):带提示的读取一行输入
void main() {
println("请输入你的名字:"); // 打印提示
String name = readLine(); // 读取输入
println("你好," + name + "!"); // 打印问候
}
这样写控制台程序就简单多了,不用再写System.out.println这么长的代码了。
实际应用场景
场景1:简单的计算器
写个简单的计算器,传统方式得声明类,main得是static:
// 传统方式
public class Calculator {
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("5 + 3 = " + calc.add(5, 3));
System.out.println("5 - 3 = " + calc.subtract(5, 3));
}
}
用JEP 477就简单多了:
// 新方式,不用声明类,main不用static
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
void main() {
println("5 + 3 = " + add(5, 3)); // 直接调用,不用创建对象
println("5 - 3 = " + subtract(5, 3));
}
代码简洁多了,逻辑也更清晰。
场景2:简单的文本处理
处理文本的时候,经常要用到String、List这些类:
// 传统方式
import java.util.List;
import java.util.ArrayList;
public class TextProcessor {
public static void main(String[] args) {
List<String> words = new ArrayList<>();
words.add("Hello");
words.add("World");
for (String word : words) {
System.out.println(word.toUpperCase());
}
}
}
用JEP 477:
// 新方式,自动导入,不用写import
void main() {
List<String> words = new ArrayList<>(); // 直接用,不用import
words.add("Hello");
words.add("World");
for (String word : words) {
println(word.toUpperCase()); // 用println,不用System.out.println
}
}
场景3:交互式程序
写交互式程序,传统方式读取输入比较麻烦:
// 传统方式
import java.util.Scanner;
public class Interactive {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入你的名字:");
String name = scanner.nextLine();
System.out.println("你好," + name + "!");
scanner.close();
}
}
用JEP 477:
// 新方式,用IO类的readLine方法
void main() {
print("请输入你的名字:"); // 用print,不换行
String name = readLine(); // 用readLine,不用Scanner
println("你好," + name + "!");
}
简单多了,不用创建Scanner对象,不用记得close。
场景4:快速原型开发
写快速原型的时候,用JEP 477能省不少事:
// 快速测试一个算法
void main() {
int[] numbers = {3, 1, 4, 1, 5, 9, 2, 6};
// 排序
Arrays.sort(numbers);
// 打印结果
for (int num : numbers) {
print(num + " ");
}
println();
// 计算平均值
double sum = 0;
for (int num : numbers) {
sum += num;
}
double avg = sum / numbers.length;
println("平均值: " + avg);
}
不用声明类,不用static main,不用import,直接写逻辑就行,特别适合快速测试和原型开发。
与传统方式的对比
代码量对比
看个完整的例子,对比一下代码量:
// 传统方式:Hello World程序
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
// 5行代码,还得记住public、static、String[] args这些
// JEP 477方式
void main() {
println("Hello, World!");
}
// 3行代码,简单明了
代码量减少了,而且更容易理解。
学习曲线对比
对初学者来说,JEP 477降低了学习门槛:
// 传统方式,初学者得理解这些概念:
// - 什么是类(class)
// - 什么是public
// - 什么是static
// - 什么是String[] args
// - 什么是System.out.println
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
// JEP 477方式,初学者只需要理解:
// - 什么是方法(method)
// - 方法怎么调用
void main() {
println("Hello, World!");
}
概念更少,更容易上手。
适用场景对比
JEP 477适合写简单程序,但是复杂程序还是用传统方式好:
// 简单程序,用JEP 477
void main() {
println("Hello, World!");
}
// 复杂程序,还是用传统方式
public class ComplexApp {
private Service service;
private Repository repository;
public ComplexApp(Service service, Repository repository) {
this.service = service;
this.repository = repository;
}
public static void main(String[] args) {
// 依赖注入、配置加载等复杂逻辑
Service service = new ServiceImpl();
Repository repo = new RepositoryImpl();
ComplexApp app = new ComplexApp(service, repo);
app.run();
}
void run() {
// 业务逻辑
}
}
简单程序用JEP 477,复杂程序用传统方式,各取所长。
编译和运行
启用预览特性
JEP 477是预览特性,得先启用才能用:
# 编译时启用预览特性
javac --enable-preview --release 23 HelloWorld.java
# 运行时启用预览特性
java --enable-preview HelloWorld
Maven配置
如果用Maven,可以在pom.xml里配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>23</release>
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>HelloWorld</mainClass>
<jvmArgs>--enable-preview</jvmArgs>
</configuration>
</plugin>
</plugins>
</build>
Gradle配置
如果用Gradle,可以在build.gradle里配置:
tasks.withType(JavaCompile) {
options.compilerArgs += '--enable-preview'
options.release = 23
}
tasks.withType(JavaExec) {
jvmArgs += '--enable-preview'
}
限制和注意事项
隐式类的限制
隐式类有一些限制,得注意:
- 不能有显式的类声明:如果文件里有显式的类声明,就不能用隐式类了
- 不能有多个隐式类:一个文件只能有一个隐式类
- 不能继承或实现:隐式类不能显式继承其他类或实现接口
- 不能有包声明:隐式类必须在默认包里,不能有package声明
// 错误:不能有package声明
package com.example; // 编译错误
void main() {
println("Hello");
}
// 错误:不能有显式类声明
public class MyClass { // 如果有了这个,就不能用隐式类了
}
void main() { // 编译错误
println("Hello");
}
实例主方法的限制
实例主方法也有一些限制:
- 只能有一个main方法:一个隐式类只能有一个main方法
- 参数类型有限制:如果有参数,必须是String[]类型
- 返回类型必须是void:main方法不能有返回值
// 错误:多个main方法
void main() {
println("Hello");
}
void main(String[] args) { // 编译错误,不能有多个main
println("World");
}
// 错误:main方法有返回值
int main() { // 编译错误,main必须是void
return 0;
}
预览特性的风险
JEP 477是预览特性,可能会在未来的版本里变化或者被移除,所以:
- 不要在生产环境用:预览特性还不稳定,生产环境用有风险
- 关注版本更新:如果JDK版本升级,预览特性可能会变化
- 做好迁移准备:如果预览特性被移除或者语法变了,得准备好迁移方案
最佳实践
1. 适合的场景
JEP 477适合这些场景:
- 学习Java:初学者写简单程序,降低学习门槛
- 快速原型:快速测试想法,不用写模板代码
- 脚本程序:写简单的脚本程序,不用复杂的类结构
- 单文件程序:只有一个文件的简单程序
// 适合用JEP 477的场景:简单的工具脚本
void main() {
// 读取文件
String content = Files.readString(Path.of("input.txt"));
// 处理内容
String processed = content.toUpperCase();
// 写入文件
Files.writeString(Path.of("output.txt"), processed);
println("处理完成!");
}
2. 不适合的场景
JEP 477不适合这些场景:
- 大型项目:需要多个类、包结构复杂的项目
- 企业应用:需要依赖注入、框架集成的应用
- 库开发:开发给别人用的库,需要明确的类声明
- 需要继承或实现接口:需要继承其他类或实现接口的场景
// 不适合用JEP 477的场景:需要继承的类
// 这种场景还是用传统方式
public class MyList extends ArrayList<String> {
// 需要继承,不能用隐式类
}
3. 代码组织建议
即使用JEP 477,也要注意代码组织:
// 好的做法:方法组织清晰
// 工具方法
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
// 主方法
void main() {
println("5 + 3 = " + add(5, 3));
println("5 * 3 = " + multiply(5, 3));
}
// 不好的做法:所有逻辑都堆在main里
void main() {
// 100行代码都堆在这里,不好维护
}
4. 迁移建议
如果以后JEP 477稳定了,想从预览特性迁移到正式特性,建议:
- 保持代码简洁:即使特性稳定了,也保持代码简洁,不要过度使用
- 逐步迁移:如果项目里用了JEP 477,可以逐步迁移到正式特性
- 测试充分:迁移后要充分测试,确保功能正常
总结
JEP 477引入的隐式类和实例主方法,确实让写简单Java程序变得更容易了。特别是对初学者来说,不用再理解public、static这些概念,直接写逻辑就行,学习门槛降低了不少。
鹏磊我觉得这个特性还是挺有用的,特别是写快速原型、简单脚本的时候,能省不少事。但是也别过度使用,复杂程序还是用传统方式好,代码结构更清晰,维护起来也方便。
总的来说,JEP 477是Java降低学习门槛、简化开发的一个很好的尝试。虽然现在还是预览特性,但是未来应该会稳定下来,成为Java开发的一个常用特性,特别是对初学者和快速开发场景来说。