尽管Java 11是一个长期支持版本,且被广泛应用于许多应用程序中,但这里有些重要的理由需要考虑迁移到Java 17:8
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名注册、虚拟主机、营销软件、网站建设、博野网站维护、网站推广。
Java 17引入了一些改进和新功能,这些功能将得到长期支持。
Java引入了文本块功能,这使代码更易读,并避免了不必要的字符串格式化操作。现在,我们可以将文本放在三引号之间,并在其中包含多个双引号字符串,而无需使用转义字符。下面是一个示例:
private static void jsonBlock() {
String text = """
{
"name": "John Doe",
"age": 45,
"address": "Doe Street, 23, Java Town"
}
""";
System.out.println(text);
}
正如所看到的,这样可以很容易地编写需要大量使用转义字符的Json和类似的字符串。
此外,结尾的三个双引号表示文本块的开始或其在输出中的缩进。在上面的示例中,输出中每行的位置都比最后一个字符后面的双引号向后两个空格。
引入了两个新的转义字符,'\s'用于添加空格,'\n'用于删除换行符。在编写长SQL语句时特别有用。
private static void sqlStatement() {
String sql = """
SELECT id, firstName, lastName\s\
FROM Employee
WHERE departmentId = "IT" \
ORDER BY lastName, firstName""";
System.out.println(text);
}
在Java 17中,对Switch语句进行了进一步的改进,可以解决忘记写break关键字导致的bug问题。
Switch表达式是一种更灵活的Switch语法形式,在每个case块中可以返回一个值,并且可以在赋值等操作中使用这些返回值。它使用箭头符号(->)代替冒号(:)来表示返回的表达式。
在此表达式中,当使用switch返回时,不需要使用break关键字来终止每个case块,但是需要使用default case。
这种改进使得Switch语句更加简洁和易读,并且减少了由于忘记写break而导致的错误。
private static void improvedSwitch(Fruit fruit) {
String text = switch (fruit) {
case APPLE, PEAR -> {
System.out.println("the given fruit was: " + fruit);
yield "Common fruit";
}
case ORANGE, AVOCADO -> "Exotic fruit";
default -> "Undefined fruit";
};
System.out.println(text);
}
如果在switch case中进行多个操作,我们可以使用case块,并使用yield关键字表示返回值。yield在这里是上下文相关的关键字,即在函数的其他地方可以有一个变量名yield。
Record类是一种特殊的不可变类,旨在替代数据传输对象(DTO)。
通常情况下,如果我们想在类或方法中使用一些POJO(普通Java对象),我们需要声明该类,并定义所有的getters、setters、equals和hashCode函数。例如,在其他地方使用一个样例 Fruit 类,我们需要用以下方式定义类:
public class Fruit {
private String name;
private int price;
//获取器和设置器方法、equals 和 hashcode 方法
}
尽管可以使用像 lombok 这样的库来减少大部分样板代码,但是如果借助 records,可以进一步减少代码量,无需依赖额外的库。
使用 records 后,相同的代码变为:
public static void doSomething() {
record Fruit(String name, int price) {}
Fruit fruit = new Fruit("Apple", 100);
System.out.println(fruit.getPrice());
}
如我们所见,使用Record类甚至可以在方法内定义一个局部的record对象。这个record对象会自动为其所有字段生成getter、setter、equals和hashCode方法。
Record中的字段是不可变的,只能通过在声明record时提供的参数进行定义。但我们可以定义静态变量。我们还可以定义一个自定义构造函数来验证字段。建议不要重写record的getter和setter,否则会影响其不可变性。
下面是一个具有多个构造函数、静态变量和方法的 record 的示例:
public record Employee(int id, String firstName,
String lastName)
{
static int empToken;
// 紧凑构造函数
public Employee
{
if (id < 100) {
throw new IllegalArgumentException(
"Employee Id cannot be below 100.");
}
if (firstName.length() < 2) {
throw new IllegalArgumentException(
"First name must be 2 characters or more.");
}
}
// 另一种构造函数
public Employee(int id, String firstName)
{
this(id, firstName, null);
}
// 实例方法
public void getFullName()
{
if (lastName == null)
System.out.println(firstName());
else
System.out.println(firstName() + " "
+ lastName());
}
// 静态方法
public static int generateEmployeeToken()
{
return ++empToken;
}
}
record 类的更多特性还包括:
sealed 类为我们提供了更多对扩展类的控制权。
在 Java 11 中,类可以是 final 或可扩展的。如果您想控制哪些类可以扩展您的超类,可以将所有类放在同一个包中,并将超类设为包可见性。然而,从包外部无法访问超类。例如,看下面的代码:
public abstract class Fruit {
}
public final class Apple extends Fruit {
}
public final class Pear extends Fruit {
}
private static void problemSpace() {
Apple apple = new Apple();
Pear pear = new Pear();
Fruit fruit = apple;
class Avocado extends Fruit {};
}
在这里,我们无法阻止 Avocado 扩展 Fruit 类。如果我们将 Fruit 类设置为默认访问级别,那么将无法把 apple 分配给 fruit 对象。
Java 17 引入sealed 类新特性,用于限制类的继承层级。通过使用 sealed 关键字,可以控制哪些类可以扩展或实现一个特定的父类或接口,只允许特定的类扩展我们的超类。以下是一个示例:
public abstract sealed class FruitSealed permits AppleSealed, PearSealed {
}
public non-sealed class AppleSealed extends FruitSealed {
}
public final class PearSealed extends FruitSealed {
}
如我们所见,这里使用了一个新的关键字 sealed 来表示这是一个 sealed 类。使用 permits 关键字定义可以扩展的类。任何扩展 sealed 类的类都可以是 final 类,如 PearSealed,或者可以在声明类时使用 non-sealed 关键扩展的其他类,比如 AppleSealed。
这种实现允许将 AppleSealed 分配给 FruitSealed 类,但不允许 permits 关键字未定义的其他类扩展 FruitSealed 类。
在 Java 11 中,我们通常使用 instanceof 运算符来检查一个对象是否属于某个类。如果我们希望在 instance of 检查返回 true 后对其进行某些操作,需要显式将对象转换为该特定类。以下是一个示例:
private static void oldStyle() {
Object o = new Grape(Color.BLUE, 2);
if (o instanceof GrapeClass) {
Grape grape = (Grape) o;
System.out.println("This grape has " + grape.getPits() + " pits.");
}
}
在这里,我们需要显式将对象转换为 Grape 类型,然后找出核数。
使用 Java 17,可以将其更改为:
private static void patternMatchingInJava17() {
Object o = new Grape(Color.BLUE, 2);
if (o instanceof Grape grape) {
System.out.println("This grape has " + grape.getPits() + " pits.");
}
}
可以将 instance of 检查与 &&(and)条件配对使用,但不能使用 ||(or)条件。因为如果是“or”条件,即使 instance of 检查返回 false,语句也可能达到另一个条件。
如果 instance of 检查返回 true,变量 grape 的作用域甚至可以超出 if 块。在下面的示例中,如果对象不是 Grape 类型,将抛出 RuntimeException 异常,因此编译器在达到打印语句时会确保 grape 对象存在。
private static void patternMatchingScopeException() {
Object o = new Grape(Color.BLUE, 2);
if (!(o instanceof Grape grape)) {
throw new RuntimeException();
}
System.out.println("This grape has " + grape.getPits() + " pits.");
}
在Java 11中,当遇到空指针异常时,我们只能得到异常发生的行号,但无法得知导致空指针异常的方法或变量。
在Java 17中,错误消息得到了改进,空指针异常的消息会告诉我们导致空指针异常的具体方法调用。
public static void main(String[] args) {
HashMap grapes = new HashMap<>();
grapes.put("grape1", new GrapeClass(Color.BLUE, 2));
grapes.put("grape2", new GrapeClass(Color.WHITE, 4));
grapes.put("grape3", null);
var color = ((Grape) grapes.get("grape3")).getColor();
}
正如所看到的,这里试图获取一个为null的"grape3"对象的颜色。当我们比较在Java 11和Java 17中获得的错误消息时,我们可以看到错误消息的差异,现在我们确切地知道在map中对null对象调用get方法导致了异常。
// Java 11
Exception in thread "main" java.lang.NullPointerException
at com.rg.java17.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)
// Java 17
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.rg.java17.Grape.getColor()" because the return value of "java.util.HashMap.get(Object)" is null
at com.rg.java17.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)
在NumberFormat类中添加了一个工厂方法,用于根据Unicode标准以紧凑、人类可读的形式格式化数字。有SHORT和LONG两种格式可用,示例如下:
NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
System.out.println(shortFormat.format(1000))
NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
System.out.println(shortFormat.format(1000))
// 输出
1K
1 thousand
在DateTime模式中添加了一个新的模式"B",用于指定一天中的时间。
DateTimeFormatter timeOfDayFormatter = DateTimeFormatter.ofPattern("B");
System.out.println(timeOfDayFormatter.format(LocalTime.of(8, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(13, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(20, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(23, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(0, 0)));
// 输出
早上
下午
晚上
夜间
午夜
在内存使用和时间复杂度方面,Java 17相对于Java 11也有所改进。其中进行了一项基准测试,对两个版本的代码进行性能测试,让它们执行一系列任务。
一些总体结果如下:
从Java 11迁移到Java 17可带来多项好处,包括新功能和性能改进。然而,在迁移过程中需要注意潜在的问题。许多库也会升级到支持Java 17的新版本,因此在使用外部库时需要谨慎。通过了解可能出现的问题并采取必要的步骤解决,可以确保您顺利地迁移到Java 17。
分享题目:八个从Java11到Java17中的功能提升
转载来于:http://www.shufengxianlan.com/qtweb/news43/254493.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联