当前位置 : 主页 > 编程语言 > 其它开发 >

Java8开始,每个版本重要新特性

来源:互联网 收集:自由互联 发布时间:2022-06-07
注意:本文不是介绍每个版本所有的新特性,只介绍个人感觉 重要 的新特性。 先上图: 除了Java 7经过了五年,Java 8经过了三年。在此之后,就是 每六个月发布一次新版本 。但不是每

  注意:本文不是介绍每个版本所有的新特性,只介绍个人感觉重要的新特性。

  先上图:

 

 

 

 

 

 

 

 

 

  除了Java 7经过了五年,Java 8经过了三年。在此之后,就是每六个月发布一次新版本。但不是每个版本都是 LTS(Long-Term-Support)。按照Oracle的计划,每三年会有一个LTS版本。最近的三个LTS版本是 Java 8、Java 11和Java 17。所以大家生产环境时,请选择Java8、Java11或Java17其中的一个。

  JDK和OpenJDK有啥区别呢?其实OpenJDK是2007年由Sun(现在是Oracle)发布的,是Oracle JDK的开源实现版本,以GPL协议发布。早在JDK 7的时候,Sun JDK就是在Open JDK7基础上发布的,只是替换了少量的源码。在Sun被Oracle收购之后,Sun SDK被称为Oracle JDK,是基于Oracle Binary Code License Agreement协议。本质上,Oracle JDK是基于OpenJDK构建的,技术上基本上没有差异

   下面通过代码来整体说明下:

  

  1、Lambda与Stream

  Java 7之后个人感觉最重要的新特性,一定要会,需要掌握。Java 8推出后,其后多个版本对其进行了功能增强,比如Java 11中Lambda可以使用var,Java 16也对其进行了一些写法的增强,其中也要注意双冒号的使用。

StreamTest.java

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamTest {
    public static void main(String[] args) {
//        lambdaBasic();
//        streamMap();
//        streamReduce();
//        streamFilter();
        streamOtherDemo();
    }

    /**
     * lambda基本使用
     */
    private static void lambdaBasic() {
        // innerClass
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The old runable now is using!");
            }
        }).start();

        new Thread(() -> System.out.println("lambda instead")).start();


        // Lambda List
        List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
        for (String feature : features) {
            System.out.println(feature);
        }
        System.out.println("-----------------");

        features.forEach(feature -> System.out.println(feature));

        System.out.println("-----------------");

        // 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示
        features.forEach(System.out::println);
    }

    /**
     * Stream Map
     */
    private static void streamMap() {
        List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
//        for (Integer cost : costBeforeTax) {
//            double price = cost + 0.12 * cost;
//            System.out.println(price);
//        }

        System.out.println("-----------------");
        // map函数可以说是函数式编程里最重要的一个方法了。map的作用是将一个对象变换为另外一个
        costBeforeTax.stream().map(n->n+n*15).forEach(System.out::println);

        System.out.println("-----------------");
        // reduce与map一样,也是函数式编程里最重要的几个方法之一。。。map的作用是将一个对象变为另外一个,而reduce实现的则是将所有值合并为一个
        double result = costBeforeTax.stream().map(m -> m * 1.1).reduce((sum, m) -> sum + m).get();
        // 110 + 220 + 330 + 440 + 550
        System.out.println(result);

        System.out.println("-----------------");
        List<String> mapTest = Arrays.asList("abcDefg", "higKLsfsd", "test");
        String joinTest = mapTest.stream().map(str -> str.toUpperCase()).collect(Collectors.joining(", "));
        System.out.println(joinTest);
    }

    /**
     * Stream Reduce
     */
    private static void streamReduce() {
//        List<Double> cost = Arrays.asList(10.0, 20.0, 30.0);
//        double sum = 0;
//        for (double each : cost) {
//            each += each * 0.05;
//            sum += each;
//        }
//        System.out.println(sum);

        // Reduce
        List<Double> cost = Arrays.asList(10.0, 20.0, 30.0);
        double allCost = cost.stream().map(x -> x + x * 0.05).reduce((sum, x) -> sum + x).get();
        System.out.println(allCost);
    }

    /**
     * stream filter demo
     */
    private static void streamFilter() {
        List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
//        for (int value : costBeforeTax) {
//            if (value >= 300) {
//                System.out.println(value);
//            }
//        }
        costBeforeTax.stream().filter(n -> n >= 300).forEach(System.out::println);
    }

    /**
     * stream Other Demo
     */
    private static void streamOtherDemo() {
        // 转大写后合并
        List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.", "Canada");
        String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
        System.out.println(G7Countries);

        // 去重
        List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
        List<Integer> distinct = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList());
        System.out.printf("Original List : %s,  Square Without duplicates : %s", numbers, distinct);
    }
}

  

  2、接口的默认实现、静态方法、私有方法

  Java 7之后第二个重要的新特性,也需要掌握。Java 8中,接口可以有默认的实现和静态方法,Java 9中新增了接口里可以有私有的方法。

TestInterface.java

public interface TestInterface {

    /**
     * 接口的默认实现方法
     * @return
     */
    default String defaultMethod() {
        System.out.println(privateMethod());
        return "Default Method";
    }

    /**
     * 接口的静态方法
     * @return
     */
    static String staticMethod() {
        return "Static Method";
    }

    /**
     * 接口的私有方法 (Java 9 特性)
     * @return
     */
    private String privateMethod() {
        return "Private Method";
    }

    /**
     * 一般的接口声明
     * @return
     */
    String normalMethod();
}

TestImplement.java

public class TestImplement implements TestInterface{

    public static void main(String[] args) {

        // 静态方法调用
        System.out.println(TestInterface.staticMethod());

        TestInterface test = new TestImplement();

        // 默认方法调用
        System.out.println(test.defaultMethod());

        // 正常方法调用
        System.out.println(test.normalMethod());
    }

    @Override
    public String normalMethod() {
        return "Normal Method";
    }
}

 

  3、Jigsaw模块化

  Java 9推出的新特性,个人感觉有点类似于OSGI那种感觉。平常使用的并不多,但是解决了Java内部很多关于包管理的历史残留问题,意义很大。 

  

  4、其他特性

  其他特性可能不如上面那些重要,主要是技巧性的。私下里大家可以多使用,提高代码的健壮性和使代码变得更优雅。

 

4.1、Optional解决空指针问题,Java 8新特性,主要是Optional一些API的使用

OptionalTest.java  

        // 常用方法 orElseGet 如果value为空,则抛出自定义异常。
        System.out.println(new OptionalTest().getDefaultCity(null));

        // 常用方法 orElseThrow 如果value为空,则调用Supplier实例返回一个默认值。
//        System.out.println(Optional.ofNullable(null).orElseThrow(NullPointerException::new));

        // Optional写法
        System.out.println(new OptionalTest().getCityUsingOptional(new Person()));

    }

    public String getCityUsingOptional(Person person) {
        // 注意,这里的map是Optional的,不是Stream的map
        String city = Optional.ofNullable(person)
                .map(Person::getHouse)
                .map(House::getAddress)
                .map(Address::getCity).orElse("Unknown city Using Optional");
        return city;
    }

    public String getDefaultCity(String city) {
        // 如果city为空,则调用getDefaultCity方法
        return Optional.ofNullable(city).orElseGet(this::getDefaultCity2);
    }

    private String getDefaultCity2() {
        return "beijing";
    }

    /**
     * 引发空指针
     * @return
     */
    public String getCity() {
        String city = new Person().getHouse().getAddress().getCity();
        return city;
    }

    /**
     * 繁琐的写法
     * @param person
     * @return
     */
    public String getCity2(Person person) {
        if (person != null) {
            House house = person.getHouse();
            if (house != null) {
                Address address = house.getAddress();
                if (address != null) {
                    String city = address.getCity();
                    return city;
                }
            }
        }
        return "unknown2";
    }

    /**
     * 稍微优雅的写法
     * @param person
     * @return
     */
    public String getCity3(Person person) {
        String city = "unknown3";
        if (person == null) {
            return city;
        }
        House house = person.getHouse();
        if (house == null) {
            return city;
        }
        Address address = house.getAddress();
        if (address == null) {
            return city;
        }
        return address.getCity();
    }
}

class Person {
    private String name;
    private int age;
    private House house;
    public House getHouse() {
        return house;
    }
}
class House {
    private long price;
    private Address address;
    public Address getAddress() {
        return address;
    }
}
class Address {
    private String country;
    private String city;
    public String getCity() {
        return city;
    }
}

 

 4.2、日期时间API,Java8新特性,主要是LocalDate、LocalTime、LocalDateTime、Period的使用

 LocalDate.java 

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Locale;

public class DateApi {
    public static void main(String[] args) {
//        LocalDateMethod();
//        LocalTimeMethod();
//        LocalDateTimeMethod();
//        durationMethod();
//        periodMethod();
//        DateModifyMethod();
        DateFormatMethod();
    }

    /**
     * LocalDate
     */
    public static void LocalDateMethod() {
        LocalDate localDate = LocalDate.of(2017, 1, 4);     // 初始化一个日期:2017-01-04
        int year = localDate.getYear();
        System.out.println(year);   // 年份:2017
        Month month = localDate.getMonth();
        System.out.println(month);  // 月份:JANUARY
        int dayOfMonth = localDate.getDayOfMonth();
        System.out.println(dayOfMonth); // 月份中的第几天:4
        DayOfWeek dayOfWeek = localDate.getDayOfWeek();
        System.out.println(dayOfWeek);  // 一周的第几天:WEDNESDAY
        int length = localDate.lengthOfMonth();
        System.out.println(length); // 月份的天数:31
        boolean leapYear = localDate.isLeapYear();
        System.out.println(leapYear);   // 是否为闰年:false

        // 获取当前日期
//        LocalDate now = LocalDate.now();
    }


    /**
     * LocalTime
     */
    public static void LocalTimeMethod() {
        LocalTime localTime = LocalTime.of(17, 23, 52);     // 初始化一个时间:17:23:52
        int hour = localTime.getHour();
        System.out.println(hour);       // 时:17
        int minute = localTime.getMinute();
        System.out.println(minute);     // 分:23
        int second = localTime.getSecond();
        System.out.println(second);     // 秒:52
    }

    /**
     * LocalDateTime    LocalDate和LocalTime的结合体
     */
    public static void LocalDateTimeMethod() {
        LocalDateTime ldt1 = LocalDateTime.of(2017, Month.JANUARY, 4, 17, 23, 52);
        // 组合
        LocalDate localDate = LocalDate.of(2017, Month.JANUARY, 4);
        LocalTime localTime = LocalTime.of(17, 23, 52);
        LocalDateTime ldt2 = localDate.atTime(localTime);

        // 拆分
        LocalDate date = ldt1.toLocalDate();
        LocalTime time = ldt1.toLocalTime();
    }

    /**
     * 时间差
     */
    public static void durationMethod() {
        LocalDateTime from = LocalDateTime.of(2017, Month.JANUARY, 5, 10, 7, 0);    // 2017-01-05 10:07:00
        LocalDateTime to = LocalDateTime.of(2017, Month.FEBRUARY, 5, 10, 7, 0);     // 2017-02-05 10:07:00
        Duration duration = Duration.between(from, to);     // 表示从 2017-01-05 10:07:00 到 2017-02-05 10:07:00 这段时间

        long days = duration.toDays();              // 这段时间的总天数
        System.out.println(days);
        long hours = duration.toHours();            // 这段时间的小时数
        System.out.println(hours);
        long minutes = duration.toMinutes();        // 这段时间的分钟数
        System.out.println(minutes);
        long seconds = duration.getSeconds();       // 这段时间的秒数
        System.out.println(seconds);
        long milliSeconds = duration.toMillis();    // 这段时间的毫秒数
        System.out.println(milliSeconds);
        long nanoSeconds = duration.toNanos();      // 这段时间的纳秒数
        System.out.println(nanoSeconds);
    }

    /**
     * 时间差 和Duration类似,区别在于Period是以年月日来衡量一个时间段
     */
    public static void periodMethod() {
        Period period = Period.between(
                LocalDate.of(2017, 1, 9),
                LocalDate.of(2021, 4, 5));
        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println(period.getDays());
    }

    /**
     * 日期加减
     */
    public static void DateModifyMethod() {
        LocalDate date = LocalDate.of(2017, 1, 5);

        LocalDate date1 = date.withYear(2016);
        System.out.println(date1);      // 修改为 2016-01-05
        LocalDate date2 = date.withMonth(2);
        System.out.println(date2);      // 修改为 2017-02-05
        LocalDate date3 = date.withDayOfMonth(1);
        System.out.println(date3);      // 修改为 2017-01-01

        LocalDate date4 = date.plusYears(1);
        System.out.println(date4);      // 增加一年 2018-01-05
        LocalDate date5 = date.minusMonths(2);
        System.out.println(date5);      // 减少两个月 2016-11-05
        LocalDate date6 = date.plus(5, ChronoUnit.DAYS);
        System.out.println(date6);      // 增加5天 2017-01-10
    }

    /**
     * 日期格式化
     */
    public static void DateFormatMethod() {
        LocalDateTime dateTime = LocalDateTime.now();
        String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println(strDate1);       // 20220509
        String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE);
        System.out.println(strDate2);       // 2022-05-09
        String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME);
        System.out.println(strDate3);       // 22:02:04.875902
        String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        System.out.println(strDate4);       // 2022-05-09
        String strDate5 = dateTime.format(DateTimeFormatter.ofPattern("今天是:YYYY年 MMMM dd日 E",
                Locale.CHINESE));
        System.out.println(strDate5);       // 今天是:2022年 五月 09日 周一
    }
}

 

 4.3、Java 9的创建不可变集合,Java 10的var推导类型,Java 11的新增一些字符串处理方法

 Java11Demo.java 

import java.util.List;

public class Java11Demo {

    public static void main(String[] args) {
        // Java 9,创建不可变集合
        List list = List.of("A","B","C");
        list.forEach(System.out::println);

        // Java 10,推导类型,目前仅用于局部变量以及 for 循环变量声明中
        var test = "123";
        System.out.println(test);

        // Java11,新增一些字符串处理方法
        System.out.println(" ".isBlank());
        // strip和trim区别:trim()可以去除字符串前后的半角空白字符,strip()可以去除字符串前后的全角和半角空白字符
        System.out.println(" Javastack ".stripTrailing()); // " Javastack" 去后空格
        System.out.println(" Javastack ".stripLeading()); // "Javastack " 去前空格
        System.out.println(" Javastack ".strip()); // "Javastack" 去空格

    }
}

 

 4.4、record 记录类,Java 14新特性,有点像Lombok插件,但是又不完全一样,主要用于在对象之间传递不可变数据,常用语编写POJO类

 User1.java 

public class User1 {
    String name;
    Integer age;

    public User1(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User1[" +
                "name='" + name + '\'' +
                ", age=" + age +
                ']';
    }
}

 User2.java 

package java17.record;

/**
 * Java 14: record 记录类, 也可以重写equals() hashCode() toString()方法
 *
 * 在许多 java 应用程序中,在对象之间传递不可变数据是很常见的,常用语编写POJO类。
 * 在Java14之前,我们需要创建一个带有字段和方法的类,
 * 随着Java14的发布,我们现在可以使用 record 关键字来解决这些问题。
 *
 *
 * 1. 每个字段是private并且是final的
 * 2. 每个字段需要有个getter方法
 * 3. 需要有个构造方法并且参数包含所有字段
 * 4. 还需要包含所有字段的equals方法,以及对应的hashcode方法和toString方法
 */
public record User2(String name, Integer age) {
//    @Override
//    public String toString() {
//        return "User2[" +
//                "name='" + name + '\'' +
//                ", age=" + age +
//                ']';
//    }
//    @Override
//    public boolean equals(Object obj) {
//        return false;
//    }
//    @Override
//    public int hashCode() {
//        return 0;
//    }

}

RecordTest.java 

public class RecordTest {

    public static void main(String[] args) {
        //传统的方式
        User1 user1 = new User1("大阳", 1990);
        System.out.println(user1);

        // Java 14 record 记录类
        User2 user2 = new User2("月亮", 2000);
        System.out.println(user2.age());
        System.out.println(user2);
    }
}

 

 4.5、Text Blocks文本块,Java 13新特性,无需多说,直接上代码

 TextBlocksDemo.java 

public class TextBlocksDemo {

    public static void main(String[] args) {
        /*
            使用特殊的语法进行文本块的开始和结束:"""
            注意:在第一个开引号"""后需要进行换行操作,否则编译会报错。
         */
        String htmlContent =
                """
                <!DOCTYPE html>
                <html>
                    <head>
                        <title>Example</title>
                    </head>
                    <body>
                        <p>This is an example of a simple HTML 
                        page with one paragraph.</p>
                    </body>
                </html>      
                """;
        System.out.println(htmlContent);

        String sqlContent =
                """
                  SELECT NAME, AGE FROM EMP 
                  WHERE NAME='CLX';       
                """;
        System.out.println(sqlContent);
    }
}

 

 4.6、Switch与yield,Java 12与13的新特性,引入了 yield 语句来从块中返回值,而不是使用 break。这意味着,switch 表达式(返回值)应该使用 yield,而 switch 语句(不返回值)应该使用 break

 SwitchDemo.java 

public class SwitchDemo {
    public static void main(String[] args) {

        /* Java 12与13的新特性,它引入了 yield 语句来从块中返回值,而不是使用 break。
         这意味着,switch 表达式(返回值)应该使用 yield,而 switch 语句(不返回值)应该使用 break */
        System.out.println(switchMethod1(1));
        System.out.println(switchMethod1(5));

        System.out.println(switchMethod2(2));
        System.out.println(switchMethod2(10));
    }

    private static String switchMethod1(int value) {
        return switch(value) {
            case 1:
                yield "value1";
            case 2:
                yield "value2";
            default: {
                yield "test";
            }
        };
    }

    private static String switchMethod2(int value) {
        String returnValue = switch (value) {
            case 1 -> {
                yield "value1";
            }
            case 2 -> {
                yield "value2";
            }
            default -> {
                yield "test";
            }
        };
        return returnValue;
    }


}

 

 4.7、instanceof模式识别,Java 14新特性,instanceof 如果是String类型,直接将其值赋给变量str,该str的作用域仅限于if语句

 InstanceOfDemo.java 

public class InstanceOfDemo {
    public static void main(String[] args) {
        Object obj1 = "好好学习,天天向上";
//        Object obj1 = 1;
        // 传统代码
        if (obj1 instanceof String) {
            String str = (String) obj1;
            System.out.println(str);
        } else {
            System.out.println("不是字符串类型");
        }

        // Java 14: instanceof 如果是String类型,直接将其值赋给变量str,该str的作用域仅限于if语句
        if (obj1 instanceof String str) {
            System.out.println(str);
        } else {
            System.out.println("不是字符串类型");
        }
    }
}

 

 4.8、sealed密封类,用的很少,Java 15新特性,密封类可以控制哪些模型、类等可以实现或扩展该类/接口。

 Cat.java

final class Cat extends Animal {
}

Dog.java

non-sealed class Dog extends Animal {
}

Animal.java

/**
 * Java 15 sealed class: 密封类可以控制哪些模型、类等可以实现或扩展该类/接口。
 * 允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。
 * 而且 sealed 修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,
 * 且只能是 final 、sealed 、non-sealed 三者之一
 *
 * 在Java中如果想让一个类不能被继承和修改,这时我们应该使用final关键字对类进行修饰,
 * 不过这种要么可以继承,要么不能继承的机制不够灵活,
 * 有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承。
 */
public sealed class Animal permits Cat, Dog {

}

Tiger.java

/**
 * 报错,无权限继承
 */
//non-sealed class Tiger extends Animal {
//}

 

 4.9、JShell,Java9新特性,提供命令行输出,也不怎么常用。

 

 

 

  还有一些没有特殊说明,大家可以结合上面思维导图去看。整体上就介绍那么多,希望对大家有所帮助。

 

【文章转自高防服务器 http://www.558idc.com 复制请保留原URL】
网友评论