Java21手册(十):脚本式开发

avatar
@比心

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脚本了!

image.png