public static class Car {
public static Car create( final Supplier supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "Collided " + car.toString() );
}
public void follow( final Car another ) {
System.out.println( "Following the " + another.toString() );
}
public void repair() {
System.out.println( "Repaired " + this.toString() );
}
}
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
1
2
运行上述例子,可以在控制台看到如下输出(Car实例可能不同):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
package com.javacodegeeks.java8.type.inference;
public class Value {
public static T defaultValue() {
return null;
}
public T getOrDefault(T value, T defaultValue ) {
return ( value != null ) ? value : defaultValue;
}
}
1
2
3
4
5
6
7
8
9
10
11
下列代码是 Value< String> 类型的应用:
package com.javacodegeeks.java8.type.inference;
public class TypeInference {
public static void main(String[] args) {
final Value value = new Value<>();
value.getOrDefault("22", Value.defaultValue() );
}
}
package java.util;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* A container object which may or may not contain a non-null value.
* If a value is present, {@code isPresent()} will return {@code true} and
* {@code get()} will return the value.
*
*
Additional methods that depend on the presence or absence of a contained
* value are provided, such as {@link #orElse(java.lang.Object) orElse()}
* (return a default value if value not present) and
* {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
* of code if the value is present).
*
*
This is a value-based
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code Optional} may have unpredictable results and should be avoided.
*
* @since 1.8
*/
public final class Optional {
/**
* Common instance for {@code empty()}.
*/
private static final Optional> EMPTY = new Optional<>();
/**
* If non-null, the value; if null, indicates no value is present
*/
private final T value;
/**
* Constructs an empty instance.
*
* @implNote Generally only one empty instance, {@link Optional#EMPTY},
* should exist per VM.
*
* 无参构造
*/
private Optional() {
this.value = null;
}
/**
* Returns an empty {@code Optional} instance. No value is present for this
* Optional.
*
* @apiNote Though it may be tempting to do so, avoid testing if an object
* is empty by comparing with {@code ==} against instances returned by
* {@code Option.empty()}. There is no guarantee that it is a singleton.
* Instead, use {@link #isPresent()}.
*
* @param Type of the non-existent value
* @return an empty {@code Optional}
*
* 返回一个空Optional实例。
*/
public static Optional empty() {
@SuppressWarnings("unchecked")
Optional t = (Optional) EMPTY;
return t;
}
/**
* Constructs an instance with the value present.
*
* @param value the non-null value to be present
* @throws NullPointerException if value is null
*
* 有参构造
*/
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
/**
* Returns an {@code Optional} with the specified present non-null value.
*
* @param the class of the value
* @param value the value to be present, which must be non-null
* @return an {@code Optional} with the value present
* @throws NullPointerException if value is null
*
* 返回一个具有指定当前非空值的Optional实例。
*/
public static Optional of(T value) {
return new Optional<>(value);
}
/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*
* 如果非空,返回一个指定值Optional实例,否则返回一个空Optional。
*/
public static Optional ofNullable(T value) {
return value == null ? empty() : of(value);
}
/**
* If a value is present in this {@code Optional}, returns the value,
* otherwise throws {@code NoSuchElementException}.
*
* @return the non-null value held by this {@code Optional}
* @throws NoSuchElementException if there is no value present
*
* @see Optional#isPresent()
*
* 如果 this 中存在Optional值,则返回该值,否则抛出NoSuchElementException。
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
/**
* Return {@code true} if there is a value present, otherwise {@code false}.
*
* @return {@code true} if there is a value present, otherwise {@code false}
*
* 如果存在值(不为null)则返回true,否则返回false。
*/
public boolean isPresent() {
return value != null;
}
/**
* If a value is present, invoke the specified consumer with the value,
* otherwise do nothing.
*
* @param consumer block to be executed if a value is present
* @throws NullPointerException if value is present and {@code consumer} is
* null
*
* 如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。
*/
public void ifPresent(Consumer super T> consumer) {
if (value != null)
consumer.accept(value);
}
/**
* If a value is present, and the value matches the given predicate,
* return an {@code Optional} describing the value, otherwise return an
* empty {@code Optional}.
*
* @param predicate a predicate to apply to the value, if present
* @return an {@code Optional} describing the value of this {@code Optional}
* if a value is present and the value matches the given predicate,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the predicate is null
*
* 如果存在一个值,并且该值与给定的谓词匹配,则返回一个Optional描述该值的值,否则返回一个空值Optional。
*/
public Optional filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
/**
* If a value is present, apply the provided mapping function to it,
* and if the result is non-null, return an {@code Optional} describing the
* result. Otherwise return an empty {@code Optional}.
*
* @apiNote This method supports post-processing on optional values, without
* the need to explicitly check for a return status. For example, the
* following code traverses a stream of file names, selects one that has
* not yet been processed, and then opens that file, returning an
* {@code Optional}:
*
*
*
* Here, {@code findFirst} returns an {@code Optional}, and then
* {@code map} returns an {@code Optional} for the desired
* file if one exists.
*
* @param The type of the result of the mapping function
* @param mapper a mapping function to apply to the value, if present
* @return an {@code Optional} describing the result of applying a mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null
*
* 如果存在值,则对其应用提供的映射函数,如果结果为非 null,则返回Optional描述结果。
*/
public Optional map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
/**
* If a value is present, apply the provided {@code Optional}-bearing
* mapping function to it, return that result, otherwise return an empty
* {@code Optional}. This method is similar to {@link #map(Function)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code flatMap} does not wrap it with an additional
* {@code Optional}.
*
* @param The type parameter to the {@code Optional} returned by
* @param mapper a mapping function to apply to the value, if present
* the mapping function
* @return the result of applying an {@code Optional}-bearing mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null or returns
* a null result
*
* 如果存在值,则将提供的Optional-bearing 映射函数应用于它,返回该结果,否则返回空 Optional。
*/
public Optional flatMap(Function super T, Optional> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*
* 对自身判断,如果存在则返回自身,否则返回other。
* 对自身判断,如果不为null则返回自身,否则返回一个指定的值。
*/
public T orElse(T other) {
return value != null ? value : other;
}
/**
* Return the value if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* @param other a {@code Supplier} whose result is returned if no value
* is present
* @return the value if present otherwise the result of {@code other.get()}
* @throws NullPointerException if value is not present and {@code other} is
* null
*
* 如果存在则返回该值,否则调用other并返回该调用的结果。
*/
public T orElseGet(Supplier extends T> other) {
return value != null ? value : other.get();
}
/**
* Return the contained value, if present, otherwise throw an exception
* to be created by the provided supplier.
*
* @apiNote A method reference to the exception constructor with an empty
* argument list can be used as the supplier. For example,
* {@code IllegalStateException::new}
*
* @param Type of the exception to be thrown
* @param exceptionSupplier The supplier which will return the exception to
* be thrown
* @return the present value
* @throws X if there is no value present
* @throws NullPointerException if no value is present and
* {@code exceptionSupplier} is null
*
* 如果存在(不为null),则返回该值,否则抛出由提供的供应商创建的异常。
*/
public T orElseThrow(Supplier extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
/**
* Indicates whether some other object is "equal to" this Optional. The
* other object is considered equal if:
*
*
it is also an {@code Optional} and;
*
both instances have no value present or;
*
the present values are "equal to" each other via {@code equals()}.
*
*
* @param obj an object to be tested for equality
* @return {code true} if the other object is "equal to" this object
* otherwise {@code false}
*
* 判断某个其他对象是否“等于”此 Optional。 相等返回true,不想等返回false
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Optional)) {
return false;
}
Optional> other = (Optional>) obj;
return Objects.equals(value, other.value);
}
/**
* Returns the hash code value of the present value, if any, or 0 (zero) if
* no value is present.
*
* @return hash code value of the present value or 0 if no value is present
*
* 返回当前值的哈希码值(如果有);如果不存在值,则返回 0(零)。
*/
@Override
public int hashCode() {
return Objects.hashCode(value);
}
/**
* Returns a non-empty string representation of this Optional suitable for
* debugging. The exact presentation format is unspecified and may vary
* between implementations and versions.
*
* @implSpec If a value is present the result must include its string
* representation in the result. Empty and present Optionals must be
* unambiguously differentiable.
*
* @return the string representation of this instance
*
* 返回此 Optional 适合调试的非空字符串表示形式。
*/
@Override
public String toString() {
return value != null
? String.format("Optional[%s]", value)
: "Optional.empty";
}
}
public class Streams {
private enum Status {
OPEN, CLOSED
};
private static final class Task {
private final Status status;
private final Integer points;
Task( final Status status, final Integer points ) {
this.status = status;
this.points = points;
}
public Integer getPoints() {
return points;
}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format( "[%s, %d]", status, points );
}
}
}
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println( "Total points: " + totalPointsOfOpenTasks );
// Group tasks by their status
final Map< Status, List< Task > > map = tasks
.stream()
.collect( Collectors.groupingBy(Task::getStatus));
System.out.println( map );
// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );
System.out.println( dateFromClock );
// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time );
System.out.println( timeFromClock );
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime );
System.out.println( datetimeFromClock );
// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
就像上面说的,Java 调整了发布策略,为了适应这种发布节奏,随着改变的还有 Java 版本号的记录方式。 版本号的新模式是:F E A T U R E . FEATURE.FEATURE.INTERIM.U P D A T E . UPDATE.UPDATE.PATCH
$FEATURE :基于发布版本,如 Java 10 的 10 。
$INTERIM :问题修复和功能增强时 + 1,默认是 0 。
$UPDATE :在进行兼容更新,修复新功能安全问题时 +1。
$PATCH :特殊问题修复时 +1。
查看自己的 Java 10 版本。
$ java -version
java version "10.0.1" 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
1
2
3
4
局部类型推断
JEP 286 提案让 Java 增加了局部类型推断(Local-Variable Type Inference)功能,这让 Java 可以像 Js 里的 var 或者其他语言的 auto 一样可以自动推断数据类型。这其实只是一个新的语法糖,底层并没有变化,在编译时就已经把 var 转化成具体的数据类型了,但是这样可以减少代码的编写。 你可以像下面这样使用 var 语法。
var hashMap = new HashMap();
hashMap.put("CSDN","阿提说说");
var string = "hello java 10";
var stream = Stream.of(1, 2, 3, 4);
var list = new ArrayList();
1
2
3
4
5
如果你反编译编译后的这段代码,你会发现还是熟悉的代码片段。
HashMap hashMap = new HashMap();
hashMap.put("微信", "wlw");
String string = "hello java 10";
Stream stream = Stream.of(1, 2, 3, 4);
ArrayList list = new ArrayList();
1
2
3
4
5
var 看似好用,其实也有很多限制,官方介绍了 var 只能用于下面的几种情况。
仅限带有初始化的程序的局部变量。
for 循环或者增强for 循环中。
for 循环中的声明。
public static void testVar() {
// 情况1,没有初始化会报错
// var list;
var list = List.of(1, 2, 3, 4);
// 情况2
for (var integer : list) {
System.out.println(integer);
}
// 情况3
for (var i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
尽管对 var 的使用场景增加了很多限制,但在实际使用时你还是要注意,就像下面的代码,你可能一眼并不能看出 result 的数据类型。
var query = "xxx";
var result = dbUtil.executeQuery(query);
var list = new ArrayList();
list.add("wechat");
list.add("wlw");
List copyList = List.copyOf(list);
list.add("test");
System.out.println(copyList);
// result
// [wechat, wn8398]
1
2
3
4
5
6
7
8
也为 Optional 增加了一个新的方法 orElseThrow。调用这个方法也可以获取到 optional 中的 value , 但是如果 value 为 null ,就会抛出异常。 另外在 Stream 最后收集数据的时候,Collectors 可以直接指定收集的集合为不可变集合,像下面这样。
// 通过传入月份,输出月份所属的季节
public static void switchJava12Before(String day) {
switch (day) {
case "march":
case "april":
case "may":
System.out.println("春天");
break;
case "june":
case "july":
case "august":
System.out.println("夏天");
break;
case "september":
case "october":
case "november":
System.out.println("秋天");
break;
case "december":
case "january":
case "february":
System.out.println("冬天");
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Java 12的写法
public static void switchJava12(String day) {
switch (day) {
case "march", "april", "may" -> System.out.println("春天");
case "june", "july", "august" -> System.out.println("夏天");
case "september", "october", "november" -> System.out.println("秋天");
case "december", "january", "february" -> System.out.println("冬天");
}
}
1
2
3
4
5
6
7
8
另外还可使用返回值赋值
String season = switch (day) {
case "march", "april", "may" -> "春天";
case "june", "july", "august" -> "夏天";
case "september", "october", "november" -> "秋天";
case "december", "january", "february" -> "冬天";
default -> {
//throw new RuntimeException("day error")
System.out.println("day error");
break "day error";
}
};
System.out.println("当前季节是:" + season);
public class Java14BeaforInstanceof {
public static void main(String[] args) {
Object obj = new ArrayList<>();
if (obj instanceof ArrayList) {
ArrayList list = (ArrayList)obj;
list.add("www.baidu.com");
}
System.out.println(obj);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
而在 Java 14 中,可以在判断类型时指定变量名称进行类型转换,方便了使用。
public class Java14Instanceof {
public static void main(String[] args) {
Object obj = new ArrayList<>();
if (obj instanceof ArrayList list) {
list.add("www.baidu.com");
}
System.out.println(obj);
}
}
1
2
3
4
5
6
7
8
9
在使用 instanceof 判断类型成立后,会自动强制转换类型为指定类型。
打包工具(孵化)
在 Java 14 中,引入了打包工具,命令是 jpackage,使用 jpackage 命令可以把 JAR 包打包成不同操作系统支持的软件格式。
java.lang.NullPointerException
at other.Other.java14NullPointerExceptions(Other.java:88)
1
2
但是在 Java 14 中,会清晰的告诉你 because “content2” is null 。
Records (预览)
record 是一种全新的类型,它本质上是一个 final 类,同时所有的属性都是 final 修饰,它会自动编译出 public get hashcode 、equals、toString 等方法,减少了代码编写量。 示例:编写一个 Dog record 类,定义 name 和 age 属性。
public record Dog(String name, Integer age) {
}
1
2
使用
Dog dog1 = new Dog("牧羊犬", 1);
Dog dog2 = new Dog("田园犬", 2);
Dog dog3 = new Dog("哈士奇", 3);
System.out.println(dog1);
System.out.println(dog2);
System.out.println(dog3);
文本块在 Java 12 JEP 326 原始字符串文字 (opens new window)中引入, 在 Java 13 JEP 355:文本块(预览) (opens new window)中开始预览, 在 Java 14 JEP 368:文本块(第二次预览) (opens new window), 而现在,在 Java 15 ,文本块是正式的功能特性了。
但是 openJDK 15 中默认是没有 Shenandoah 收集器,想要使用此功能可以下载 AdoptOpenJDK (opens new window)。
为什么 openJDK 中没有 Shenandoah 垃圾收集器? Shenandoah 是一个高性能、低暂停时间的垃圾收集器,它是 Red Hat 主导的项目。当 Red Hat 第一次提议将 Shenandoah 贡献给 OpenJDK 时,Oracle 明确表示不想支持它,OpenJDK 作为一个免费软件,不想支持 Red Hat 的 Shenandoah 完全没有问题。 最后 Red Hat 选择和 Oracle 合作设计一个真正干净的可插拔垃圾收集器接口,允许任何人轻松选择垃圾收集器以包含在他们的构建中。最终 Shenandoah 进入了 JDK 12,但是没有构建进 OpenJDK。
Records(二次预览)
在 Java 14 中引入了 Record 类,Java 15 中对 Record 进行了增强。使它可以支持密封类型、Record 注解以及相关的反射 API 等。 示例:Record 支持密封(sealed)类型。
public sealed interface DataBase permits DataBaseSelect, DataBaseUpdate {
}
final record DataBaseSelect(@Deprecated String table, String sql) implements DataBase {
}
final record DataBaseUpdate() implements DataBase {
}
1
2
3
4
5
6
7
8
在 java.lang.Class 增加了两个公共方法用于获取 Record 类信息:
RecordComponent[] getRecordComponents()
boolean isRecord()
其他更新
JEP 381:删除 Solaris 和 SPARC 端口
Java 14 JEP 362 (opens new window)弃用了 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口,现在它在 Java 15 中被正式删除。
if (obj instanceof String) {
String s = (String) obj; // grr...
...
}
1
2
3
4
多余的类型强制转换,而现在
if (obj instanceof String s) {
// Let pattern matching do the work!
...
}
1
2
3
4
Records
Record 成为 Java 16 的正式功能,下面是介绍 Java 14 时关于 Record 的介绍。 record 是一种全新的类型,它本质上是一个 final 类,同时所有的属性都是 final 修饰,它会自动编译出 public get hashcode 、equals、toString 等方法,减少了代码编写量。
Dog dog1 = new Dog("牧羊犬", 1);
Dog dog2 = new Dog("田园犬", 2);
Dog dog3 = new Dog("哈士奇", 3);
System.out.println(dog1);
System.out.println(dog2);
System.out.println(dog3);
可以看到 Legacy:Random 也在其中,新的 API 兼容了老的 Random 方式,所以你也可以使用新的 API 调用 Random 类生成随机数。
// 使用 Random
RandomGeneratorFactory l128X256MixRandom = RandomGeneratorFactory.of("Random");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
System.out.println(randomGenerator.nextInt(10));
}
1
2
3
4
5
6
7
使用新的 macOS 渲染库
macOS 为了提高图形的渲染性能,在 2018 年 9 月抛弃了之前的 OpenGL 渲染库 ,而使用了 Apple Metal 进行代替。Java 17 这次更新开始支持 Apple Metal,不过对于 API 没有任何改变,这一些都是内部修改。
支持 macOS/AArch64 架构
起因是 Apple 在 2020 年 6 月的 WWDC 演讲中宣布,将开启一项长期的将 Macintosh 计算机系列从 x64 过度到 AArch64 的长期计划,因此需要尽快的让 JDK 支持 macOS/AArch64 。 Linux 上的 AArch64 支持以及在 Java 16 时已经支持。
删除已弃用的 Applet API
Applet 是使用 Java 编写的可以嵌入到 HTML 中的小应用程序,嵌入方式是通过普通的 HTML 标记语法,由于早已过时,几乎没有场景在使用了。
1
Applet API 在 Java 9 时已经标记了废弃,现在 Java 17 中将彻底删除。
更强的 JDK 内部封装
如 Java 16 的 JEP 396 中描述的一样,为了提高 JDK 的安全性,使 --illegal-access 选项的默认模式从允许更改为拒绝。通过此更改,JDK 的内部包和 API(关键内部 API (opens new window)除外)将不再默认打开。 但是在 Java 17 中,除了 sun.misc.Unsafe ,使用 --illegal-access 命令也不能打开 JDK 内部的强封装模式了,除了 sun.misc.Unsafe API 。 在 Java 17 中使用 --illegal-access 选项将会得到一个命令已经移除的警告。
➜ bin ./java -version
openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)
➜ bin ./java --illegal-access=warn
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0
if (obj instanceof String) {
String s = (String) obj; // grr...
...
}
1
2
3
4
多余的类型强制转换,而现在:
if (obj instanceof String s) {
// Let pattern matching do the work!
...
}
1
2
3
4
也可以使用如下的方式:
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
1
2
3
4
5
6
7
8
9
对null也有新的方式:
// Java 17 之前
static void testFooBar(String s) {
if (s == null) {
System.out.println("oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
// Java 17
static void testFooBar(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
在 Java 18 中,提供了一个新命令 jwebserver,运行这个命令可以启动一个简单的 、最小化的静态 Web 服务器,它不支持 CGI 和 Servlet,所以最好的使用场景是用来测试、教育、演示等需求。 其实在如 Python、Ruby、PHP、Erlang 等许多平台都提供了开箱即用的 Web 服务器,可见一个简单的 Web 服务器是一个常见的需求,Java 一直没有这方面的支持,现在可以了。 在 Java 18 中,使用 jwebserver 启动一个 Web 服务器,默认发布的是当前目录。
在当前目录创建一个网页文件 index.html
CSDN-阿提说说
1
2
3
4
5
6
7
8
启动 jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving D:\javaprojects\java18-example\src\test\java\other and subdirectories on
127.0.0.1 port 8000
URL http://127.0.0.1:8000/
1
2
3
4
通过 help 参数可以查看 jwebserver 支持的参数。
➜ bin ./jwebserver --help
Usage: jwebserver [-b bind address] [-p port] [-d directory]
[-o none|info|verbose] [-h to show options]
[-version to show version information]
Options:
-b, --bind-address - 绑定地址. Default: 127.0.0.1 (loopback).
For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory - 指定目录. Default: current directory.
-o, --output - Output format. none|info|verbose. Default: info.
-p, --port - 绑定端口. Default: 8000.
-h, -?, --help - Prints this help message and exits.
-version, --version - Prints version information and exits.
To stop the server, press Ctrl + C.