Java21手册(十):脚本式开发
10.1 JShell
在命令行中输入jshell,即可进入JShell程序,让我们先来写一个Hello World!:
$ jshell
| 欢迎使用 JShell -- 版本 20
| 要大致了解该版本, 请键入: /help intro
jshell> System.out.println("Hello World!")
Hello World!
JShell就是一个Java语言版本的交互式shell程序,类似于python、node等,在jshell中可以不必定义main方法,Java语句都会直接执行。例如:
jshell> class Person {
...> private String name;
...> public Person(String name) {
...> this.name = name;
...> }
...> public void sayHello() {
...> System.out.println("Hello World! " + name);
...> }
...> }
| 已创建 类 Person
jshell> Person person = new Person("Liu liming");
person ==> Person@133314b
jshell> person.sayHello();
Hello World! Liu liming
jshell>
Jshell还支持一些常用命令,通过/help可以查看,例如 /list 可以查看上下文代码:
jshell> /list
1 : class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello World! " + name);
}
}
2 : Person person = new Person("Liu liming");
3 : person.sayHello();
jshell>
我们通过/save保存这个JShell脚本,再通过jshell命令直接执行:
jshell> /save HelloWorld.jsh
jshell> /exit
| 再见
$ ls
HelloWorld.jsh
$ jshell HelloWorld.jsh
Hello World! Liu liming
| 欢迎使用 JShell -- 版本 20
| 要大致了解该版本, 请键入: /help intro
jshell>
通过以上的例子,我们演示了如何使用JShell编写Java程序,相信你已经感受到了这个工具带来的便利。JShell还有很多方便的特性,包含方法延迟定义、代码补全等功能,还能够实现引入新模块、import等操作。关于JShell的更多使用说明,建议大家阅读官方文档:docs.oracle.com/en/java/jav…
最后以第5章静态HTTP文件服务器的例子,演示一下用jshell方式启动web:
jshell> import com.sun.net.httpserver.*;
jshell> var server = SimpleFileServer.createFileServer(new InetSocketAddress(8080),
...> Path.of(System.getProperty("user.dir")), SimpleFileServer.OutputLevel.VERBOSE);
server ==> sun.net.httpserver.HttpServerImpl@7382f612
jshell> server.start();
执行完这几行语句,即可快速启动文件服务器,浏览器URL 127.0.0.1:8080,访问本地目录文件如下图:
熟练使用JShell,可以帮助我们快速实现简单程序的编写和执行,还可以通过jshell脚本的形式低成本地执行java程序。总之,JShell工具无论对开发者的本地开发工作还是生产环境的功能部署,都会有一定的帮助。
10.2 Java 单文件执行
Java单文件执行的目的是,如果我们只有一个类似HelloJavaScripts.java这样一个包含main方法的文件时,代码如:
public class HelloJavaScripts {
public static void main(String[] args) {
System.out.println("Hello, Java scripts!");
}
}
我们可以通过直接这样的方式来启动程序:
$ java HelloJavaScripts.java
Hello, Java scripts!
我们还可以通过--source来指定对应的Java版本,并可以通过 --enable-preview来开启preview特性:
$ java --source 11 HelloJavaScripts.java
$ java --source 12 --enable-preview Switch.java
也可以像Java正常启动一样向main方法传递参数:
$ java --source 11 Greetings.java hello java scripts
# main receives [ "hello", "java", "scripts" ]
可执行的单文件和JShell一样可以理解为Java脚本,但区别在于这是一个符合正常Java语法的类文件,我们可以用IDE来编写。例如我们在第9篇JFR中给到的 HealthReport 这个项目的例子,这个脚本的逻辑比较复杂,用单文件的方式,既能使用IDE方便地编写代码,又能直接使用源文件启动,方便地执行程序。
10.3 Java Shebang 脚本
Shebang(也称为Hashbang)是一个由井号和叹号构成的字符序列#!,其出现在文本文件的第一行的前两个字符。 在文件中存在shebang的情况下,类Unix操作系统的程序加载器会分析Shebang后面的内容,将这些内容作为解释器指令,并调用该指令,并将载有shebang的文件路径作为该解释器的参数。
我们可以把可执行的Java单文件,改成shebang脚本来执行,例如创建一个新文件hello-java-scripts:
#!/usr/bin/java --source 20
public class HelloWorldScript {
public static void main(String[] args) {
System.out.println("Hello, Java scripts!");
}
}
添加文件的执行权限后(chmod +x hello-java-scripts),我们就可以直接执行这个文件了,需要注意的是,shebang文件是不能以 .java 来结尾的:
$ chmod +x hello-java-scripts
$ ./hello-java-scripts
> Hello, Java scripts!
Java shebang脚本赋予了我们Java开发者非常强大的linux脚本编写能力,尤其在与管道集成时,下面这个例子,这个shebang脚本可以打印从System.in(即stdin)传入的内容:
#!/usr/bin/java --source 20
// [... imports ...]
public class Echo {
public static void main(String[] args) throws IOException {
var lines = readInput();
lines.forEach(System.out::println);
}
private static Stream<String> readInput() throws IOException {
var reader = new BufferedReader(new InputStreamReader(System.in));
if (!reader.ready())
return Stream.empty();
else
return reader.lines();
}
}
然后我们可以把Echo作为管道命令来试用:
$ chmod +x Echo
$ echo 鱼耳语音 | ./Echo
> 鱼耳语音
让我们把这个脚本功能做得复杂一点,增加两个参数:支持中文排序和去重,代码如下:
#!/usr/bin/java --source 20
// [... imports ...]
public class Echo {
public static void main(String[] args) throws IOException {
var lines = readInput();
for (var arg : args)
lines = modifyStream(arg, lines);
lines.forEach(System.out::println);
}
private static Stream<String> modifyStream(String arg, Stream<String> input) {
return switch (arg) {
case "--sort" -> input.sorted(Collator.getInstance(java.util.Locale.CHINA));
case "--unique" -> input.distinct();
default -> {
System.out.println("Unknown argument '" + arg + "'.");
yield input;
}
};
}
private static Stream<String> readInput() throws IOException {
var reader = new BufferedReader(new InputStreamReader(System.in));
if (!reader.ready())
return Stream.empty();
else
return reader.lines();
}
}
新建一个文件“电话簿”,里面有重复数据,内容如下:
张三 13333333333 男 1990
李四 14444444444 女 1991
王五 15555555555 男 1992
赵六 16666666666 女 1993
田七 17777777777 男 1994
朱八 18888888888 女 1995
张三 13333333333 男 1990
李四 14444444444 女 1991
何九 19999999999 男 1996
再来使用去重和排序功能:
$ cat 电话簿| ./Echo --unique
张三 13333333333 男 1990
李四 14444444444 女 1991
王五 15555555555 男 1992
赵六 16666666666 女 1993
田七 17777777777 男 1994
朱八 18888888888 女 1995
何九 19999999999 男 1996
$ cat 电话簿| ./Echo --unique --sort
何九 19999999999 男 1996
李四 14444444444 女 1991
田七 17777777777 男 1994
王五 15555555555 男 1992
张三 13333333333 男 1990
赵六 16666666666 女 1993
朱八 18888888888 女 1995
Java脚本式开发,实际上简化了Java的编译到执行两步启动方式,利用之前几章我们介绍的Java语法优化和开发效率的提高,这一章的内容可以让我们更充分地利用Java语法的便捷性,实现Java语言在开发不同阶段和场景下更加广泛的应用。作为Java程序员,你也可以做到精通linux脚本了!