likes
comments
collection
share

JDK 1.8高级特性以及教程

作者站长头像
站长
· 阅读数 15

Java Development Kit (JDK) 1.8是Java平台的一个版本,它引入了许多新的高级特性。以下是JDK 1.8及之后的一些高级特性:

  1. Lambda表达式:Lambda表达式是一种匿名函数,它可以传递给方法作为参数,并且可以简化代码。使用Lambda表达式可以写出更加简洁、易于理解的代码。
  2. Stream API:Stream API是一种新的API,它可以让开发人员更加方便地处理集合数据。使用Stream API可以实现更加简洁、易于理解的代码。
  3. 函数式接口:函数式接口是一种只有一个抽象方法的接口,它可以用于Lambda表达式和方法引用。Java 8提供了许多预定义的函数式接口,例如Function、Predicate和Supplier。
  4. 默认方法:默认方法是一种在接口中定义的具有默认实现的方法。默认方法可以使接口更加灵活,因为它们可以在不破坏现有实现的情况下添加新的方法。
  5. 重复注解:重复注解是一种在同一个元素上多次使用相同注解的方式。Java 8允许在同一个元素上使用多个相同的注解,这使得代码更加简洁。
  6. 类型注解:类型注解是一种在代码中使用注解来说明类型的方式。Java 8允许在类型上使用注解,这使得代码更加清晰和易于理解。
  7. 新的日期和时间API:Java 8引入了新的日期和时间API,它提供了更加简单、易于使用的日期和时间处理方式。
  8. Optional类:Optional类是一种新的类,它可以用来表示一个可能为空的值。这样一来,开发者就可以更加方便地处理可能为空的值了。

JDK 1.8高级特性以及教程

Stream API 代码示例

  1. 流的创建

创建流最常用的方法是将一个集合转换成流。例如,可以通过以下代码将一个List转换为流:

iniCopy code
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream();

除此之外,还可以使用Stream.of()方法创建流:

arduinoCopy code
Stream<String> stream = Stream.of("apple", "banana", "orange");
  1. 中间操作

中间操作是指在流上执行的操作,这些操作返回一个新的流,可以链式调用。以下是一些常用的中间操作:

  • filter():根据指定条件过滤元素。
  • map():对每个元素执行指定操作,并返回一个新的流。
  • flatMap():将一个流中的每个元素转换为另一个流,并将所有流合并成一个新的流。
  • distinct():去重,返回一个包含不同元素的新流。
  • sorted():根据指定条件对元素进行排序。

例如,可以使用filter()和map()方法来从一个字符串列表中获取长度大于5的字符串,并将它们转换为大写字母:

iniCopy code
List<String> list = Arrays.asList("apple", "banana", "orange", "watermelon");
Stream<String> stream = list.stream()
                            .filter(s -> s.length() > 5)
                            .map(String::toUpperCase);
  1. 终端操作

终端操作是指在流上执行的最终操作,这些操作会返回一个结果或者执行一个副作用。以下是一些常用的终端操作:

  • forEach():对每个元素执行指定操作。
  • collect():将流中的元素收集到一个集合中。
  • reduce():使用指定操作将流中的元素合并成一个结果。
  • count():返回流中元素的数量。
  • anyMatch()、allMatch()、noneMatch():判断流中是否存在满足指定条件的元素。

下面是一些Stream API的高级用法及代码示例:

  1. Map和FlatMap操作

Map操作可以将一个流中的元素按照指定的规则进行转换,例如将一个字符串流中的每个字符串转换为大写形式。

javaCopy code
List<String> list = Arrays.asList("apple", "banana", "orange");
List<String> upperList = list.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());
System.out.println(upperList); // [APPLE, BANANA, ORANGE]

FlatMap操作可以将多个流合并成一个流,例如将多个字符串流合并成一个字符串流。

javaCopy code
List<List<String>> lists = Arrays.asList(
        Arrays.asList("apple", "banana"),
        Arrays.asList("orange", "peach"),
        Arrays.asList("watermelon", "grape")
);
List<String> flatList = lists.stream()
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
System.out.println(flatList); // [apple, banana, orange, peach, watermelon, grape]
  1. Filter操作

Filter操作可以根据指定的条件过滤流中的元素,例如过滤出一个整数流中的所有偶数。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenList = list.stream()
        .filter(x -> x % 2 == 0)
        .collect(Collectors.toList());
System.out.println(evenList); // [2, 4, 6]
  1. Reduce操作

Reduce操作可以将流中的元素按照指定的规则进行计算,例如求一个整数流中的所有元素的和。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = list.stream()
        .reduce(0, Integer::sum);
System.out.println(sum); // 21
  1. Collect操作

Collect操作可以将流中的元素收集到一个集合中,例如将一个字符串流中的元素收集到一个List中。

javaCopy code
List<String> list = Arrays.asList("apple", "banana", "orange");
List<String> newList = list.stream()
        .collect(Collectors.toList());
System.out.println(newList); // [apple, banana, orange]
  1. GroupingBy操作

GroupingBy操作可以将流中的元素按照指定的规则进行分组,例如将一个字符串流中的元素按照首字母进行分组。

javaCopy code
List<String> list = Arrays.asList("apple", "banana", "orange", "avocado");
Map<Character, List<String>> map = list.stream()
        .collect(Collectors.groupingBy(s -> s.charAt(0)));
System.out.println(map); // {a=[apple, avocado], b=[banana], o=[orange]}
  1. Sorted操作

Sorted操作可以对流中的元素进行排序,例如按照字符串长度对一个字符串流进行排序。

javaCopy code
List<String> list = Arrays.asList("apple", "banana", "orange", "avocado");
List<String> sortedList = list.stream()
        .sorted(Comparator.comparing(String::length))
        .collect(Collectors.toList());
System.out.println(sortedList); // [apple, banana, orange, avocado]
  1. Peek操作

Peek操作可以对流中的元素进行操作,例如打印出一个整数流中的每个元素。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> newList = list.stream()
        .peek(System.out::println)
        .collect(Collectors.toList());
  1. Distinct操作

Distinct操作可以对流中的元素进行去重,例如去重一个整数流中的元素。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 2, 4, 5, 4, 6);
List<Integer> distinctList = list.stream()
        .distinct()
        .collect(Collectors.toList());
System.out.println(distinctList); // [1, 2, 3, 4, 5, 6]
  1. Limit和Skip操作

Limit操作可以对流中的元素进行限制,例如限制一个整数流中的元素个数。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> newList = list.stream()
        .limit(3)
        .collect(Collectors.toList());
System.out.println(newList); // [1, 2, 3]

Skip操作可以对流中的元素进行跳过,例如跳过一个整数流中的前两个元素。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> newList = list.stream()
        .skip(2)
        .collect(Collectors.toList());
System.out.println(newList); // [3, 4, 5, 6]
  1. Parallel操作

Parallel操作可以将流中的元素进行并行处理,从而提高处理效率。例如对一个整数流中的元素进行并行处理。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = list.parallelStream()
        .reduce(0, Integer::sum);
System.out.println(sum); // 21
  1. Max和Min操作

Max和Min操作可以求出流中的最大值和最小值,例如求一个整数流中的最大值和最小值。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int max = list.stream()
        .max(Integer::compare)
        .orElse(0);
int min = list.stream()
        .min(Integer::compare)
        .orElse(0);
System.out.println("max: " + max + ", min: " + min); // max: 6, min: 1
  1. Match操作

Match操作可以判断流中的元素是否满足指定的条件,例如判断一个整数流中是否存在大于10的元素。

javaCopy code
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
boolean anyMatch = list.stream()
        .anyMatch(x -> x > 10);
boolean allMatch = list.stream()
        .allMatch(x -> x > 0);
boolean noneMatch = list.stream()
        .noneMatch(x -> x < 0);
System.out.println("anyMatch: " + anyMatch + ", allMatch: " + allMatch + ", noneMatch: " + noneMatch); 

新的日期和时间API 代码示例

当涉及到日期和时间操作时,Java以前的Date和Calendar类不够灵活,而且使用起来比较麻烦。JDK 1.8引入了一组新的日期和时间API,提供了更加简单易用的日期和时间操作方式。下面是一些新的日期和时间API的示例代码:

  1. LocalDate类

LocalDate类表示一个不可变的日期对象,它只包含日期信息,不包含时间和时区信息。它的实例可以通过静态工厂方法LocalDate.of()创建。下面是一些示例代码:

javaCopy code// 创建LocalDate对象
LocalDate date = LocalDate.of(2022, 1, 1);

// 获取年、月、日信息
int year = date.getYear();
int month = date.getMonthValue();
int day = date.getDayOfMonth();

// 获取星期信息
DayOfWeek dow = date.getDayOfWeek();
int dowValue = dow.getValue(); // 1表示星期一,7表示星期日

// 比较日期
LocalDate today = LocalDate.now();
if (date.isBefore(today)) {
    System.out.println("日期在今天之前");
} else if (date.isAfter(today)) {
    System.out.println("日期在今天之后");
} else {
    System.out.println("日期就是今天");
}
  1. LocalTime类

LocalTime类表示一个不可变的时间对象,它只包含时间信息,不包含日期和时区信息。它的实例可以通过静态工厂方法LocalTime.of()创建。下面是一些示例代码:

javaCopy code// 创建LocalTime对象
LocalTime time = LocalTime.of(12, 30, 0);

// 获取小时、分钟、秒信息
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();

// 比较时间
LocalTime now = LocalTime.now();
if (time.isBefore(now)) {
    System.out.println("时间在现在之前");
} else if (time.isAfter(now)) {
    System.out.println("时间在现在之后");
} else {
    System.out.println("时间就是现在");
}
  1. LocalDateTime类

LocalDateTime类表示一个不可变的日期时间对象,它包含日期和时间信息,但不包含时区信息。它的实例可以通过静态工厂方法LocalDateTime.of()创建。下面是一些示例代码:

javaCopy code// 创建LocalDateTime对象
LocalDateTime dateTime = LocalDateTime.of(2022, 1, 1, 12, 30, 0);

// 获取年、月、日、小时、分钟、秒信息
int year = dateTime.getYear();
int month = dateTime.getMonthValue();
int day = dateTime.getDayOfMonth();
int hour = dateTime.getHour();
int minute = dateTime.getMinute();
int second = dateTime.getSecond();

// 比较日期时间
LocalDateTime now = LocalDateTime.now();
if (dateTime.isBefore(now)) {
    System.out.println("日期时间在现在之前");
} else if (dateTime.isAfter(now)) {
    System.out.println("日期时间在现在之后");
} else {
    System.out.println("日期时间就是现在");
}
  1. ZonedDateTime类

ZonedDateTime类表示一个带时区的日期时间对象,它包含日期、时间和时区信息。它的实例可以通过静态工厂方法ZonedDateTime.of()创建。下面是一些示例代码:

javaCopy code// 创建ZonedDateTime对象
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime zonedDateTime = ZonedDateTime.of(2022, 1, 1, 12, 30, 0, 0, zoneId);

// 获取年、月、日、小时、分钟、秒、时区信息
int year = zonedDateTime.getYear();
int month = zonedDateTime.getMonthValue();
int day = zonedDateTime.getDayOfMonth();
int hour = zonedDateTime.getHour();
int minute = zonedDateTime.getMinute();
int second = zonedDateTime.getSecond();
ZoneId zone = zonedDateTime.getZone();

// 转换时区
ZonedDateTime newYorkTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));

// 比较日期时间
ZonedDateTime now = ZonedDateTime.now();
if (zonedDateTime.isBefore(now)) {
    System.out.println("日期时间在现在之前");
} else if (zonedDateTime.isAfter(now)) {
    System.out.println("日期时间在现在之后");
} else {
    System.out.println("日期时间就是现在");
}
  1. DateTimeFormatter类

DateTimeFormatter类用于格式化和解析日期时间字符串。它的实例可以通过静态工厂方法DateTimeFormatter.ofPattern()创建。下面是一些示例代码:

用于将日期时间格式化为指定的字符串:

javaCopy code
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterDemo {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDateTime = now.format(formatter);
        System.out.println("Formatted date-time: " + formattedDateTime);
    }
}

这个示例程序中,获取了当前的日期和时间,然后创建了一个 DateTimeFormatter 对象,使用 ofPattern() 方法指定了要格式化的日期时间字符串的格式。在最后一行代码中,将当前的日期时间格式化为指定的字符串,并将其打印到控制台上。

其他示例:

  1. 解析字符串为日期时间
javaCopy code
String strDateTime = "2022-01-01 12:30:45";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(strDateTime, formatter);
System.out.println("Parsed date-time: " + dateTime);

在这个示例中,首先创建了一个日期时间字符串,然后使用 DateTimeFormatter 类的 parse() 方法将其解析为一个 LocalDateTime 对象,并将其打印到控制台上。

  1. 自定义本地化格式化器
javaCopy code
Locale locale = Locale.CHINA;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss", locale);
String formattedDateTime = LocalDateTime.now().format(formatter);
System.out.println("Formatted date-time: " + formattedDateTime);

在这个示例中,首先创建了一个 Locale 对象,用于指定本地化信息,然后创建了一个 DateTimeFormatter 对象,使用 ofPattern() 方法指定了要格式化的日期时间字符串的格式,并将 Locale 对象传递给它。在最后一行代码中,将当前的日期时间格式化为指定的字符串,并将其打印到控制台上。

  1. 使用预定义的格式化器
javaCopy code
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String formattedDateTime = formatter.format(now);
System.out.println("Formatted date-time: " + formattedDateTime);

在这个示例中,使用了 DateTimeFormatter 类提供的一个预定义的格式化器 ISO_LOCAL_DATE_TIME,它可以将日期时间格式化为 ISO 8601 格式。将当前的日期时间格式化为指定的字符串,并将其打印到控制台上。

函数式接口 代码示例

下面是一些常用的函数式接口及其代码示例:

  1. Consumer 接口:接受一个参数并返回 void,用于执行一些操作。
javaCopy code
Consumer<String> print = str -> System.out.println(str);
System.out.println("Hello world!"); // 输出 "Hello world!"
  1. Supplier 接口:不接受参数,返回一个值。
javaCopy code
Supplier<Integer> randomNumber = () -> (int) (Math.random() * 100);
System.out.println(randomNumber.get()); // 输出一个随机数
  1. Function 接口:接受一个参数并返回一个值。
javaCopy code
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 输出 25
  1. Predicate 接口:接受一个参数并返回一个 boolean 值,用于判断条件是否成立。
javaCopy code
Predicate<Integer> isEven = x -> x % 2 == 0;
System.out.println(isEven.test(4)); // 输出 true
  1. BiFunction 接口:接受两个参数并返回一个值。
javaCopy code
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
System.out.println(add.apply(2, 3)); // 输出 5

以上是一些常用的函数式接口及其代码示例。这些接口可以用于Lambda表达式、方法引用等语法中,使得Java中的函数式编程更加方便。

假设有一个 User 类,其中包含 name 和 age 两个属性,需要根据用户的年龄对用户列表进行排序,并输出排序后的结果。使用函数式接口可以让代码更加简洁易懂。

javaCopy code
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User("张三", 20));
        userList.add(new User("李四", 18));
        userList.add(new User("王五", 25));
        userList.add(new User("赵六", 22));

        // 根据年龄排序
        userList.sort(Comparator.comparingInt(User::getAge));

        // 输出排序结果
        userList.forEach(user -> System.out.println(user.getName() + " " + user.getAge()));
    }
}

在上面的示例中,使用了 Comparator 函数式接口来定义排序规则,并使用 forEach 方法输出排序后的结果。这样的代码更加简洁易懂,也更加符合函数式编程的思想。

再比如:

  1. BiConsumer 接口:接受两个参数并返回 void,用于执行一些操作。
javaCopy code
BiConsumer<String, Integer> print = (str, num) -> System.out.println(str + num);
print.accept("The answer is ", 42); // 输出 "The answer is 42"
  1. UnaryOperator 接口:接受一个参数并返回一个与参数类型相同的值。
javaCopy code
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 输出 25
  1. BinaryOperator 接口:接受两个参数并返回一个与参数类型相同的值。
javaCopy code
BinaryOperator<Integer> add = (x, y) -> x + y;
System.out.println(add.apply(2, 3)); // 输出 5
  1. BiPredicate 接口:接受两个参数并返回一个 boolean 值,用于判断条件是否成立。
javaCopy code
BiPredicate<Integer, Integer> isGreaterThan = (x, y) -> x > y;
System.out.println(isGreaterThan.test(5, 3)); // 输出 true
  1. BiFunction 接口:接受两个参数并返回一个值。
javaCopy code
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
System.out.println(add.apply(2, 3)); // 输出 5

这些函数式接口可以让代码更加简洁易懂,并且可以避免一些冗长的代码。在实际开发中,可以根据具体的需求选择合适的函数式接口来使用。

Lambda 表达式 代码示例

Lambda 表达式的语法:

iniCopy code
(parameter1, parameter2, ..., parameterN) -> { statement1; statement2; ... statementN; }

Lambda 表达式由三部分组成:

  1. 参数列表:由逗号分隔的形式参数列表,可以为空。
  2. 箭头符号:由连字符和大于号组成,用于分隔参数列表和 Lambda 表达式的主体。
  3. Lambda 表达式主体:可以是一个表达式或一个代码块,用于执行一些操作。

Lambda 表达式可以有零个、一个或多个参数,参数类型可以显式指定,也可以根据上下文推断出来。Lambda 表达式主体可以是一个表达式或一个代码块,如果是一个表达式,则可以省略大括号和 return 关键字。

下面是一些示例:

javaCopy code// 无参数
Runnable runnable = () -> System.out.println("Hello world!");

// 一个参数
Consumer<String> print = str -> System.out.println(str);

// 多个参数
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;

// 多条语句
Runnable runnable = () -> {
    System.out.println("Hello");
    System.out.println("World");
};

// 表达式主体
Function<Integer, Integer> square = x -> x * x;

Lambda 表达式是 Java 8 中引入的一个重要特性,它可以让代码更加简洁易懂,并且可以避免一些冗长的代码。

表达式的代码示例:

  1. 使用 Lambda 表达式实现 Runnable 接口
javaCopy code
Runnable runnable = () -> System.out.println("Hello world!");
new Thread(runnable).start(); // 输出 "Hello world!"
  1. 使用 Lambda 表达式实现 ActionListener 接口
javaCopy code
JButton button = new JButton("Click me!");
button.addActionListener(event -> System.out.println("Button clicked!"));
  1. 使用 Lambda 表达式实现 Comparator 接口
javaCopy codeList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");

// 根据字符串长度排序
list.sort((str1, str2) -> str1.length() - str2.length());

// 输出排序结果
list.forEach(str -> System.out.println(str));
  1. 使用 Lambda 表达式实现 Predicate 接口
javaCopy code
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

// 打印出所有偶数
list.stream().filter(num -> num % 2 == 0).forEach(num -> System.out.println(num));
  1. 使用 Lambda 表达式实现 Consumer 接口
javaCopy code
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");

// 输出每个字符串的长度
list.forEach(str -> System.out.println(str.length()));

以上是一些常用的 JDK Lambda 表达式的示例。这些示例可以帮助你更好地理解 Lambda 表达式的使用方法和语法。在实际开发中,可以根据具体的需求使用 Lambda 表达式来简化代码。

Optional 类 示例

Optional 类的代码示例:

  1. 创建 Optional 对象
javaCopy code
Optional<String> optional = Optional.of("Hello world!");
  1. 判断 Optional 对象是否为空
javaCopy code
Optional<String> optional = Optional.ofNullable(null);
if (optional.isPresent()) {
    System.out.println(optional.get());
} else {
    System.out.println("Optional is empty");
}
  1. 获取 Optional 对象的值
javaCopy code
Optional<String> optional = Optional.of("Hello world!");
System.out.println(optional.get());
  1. 如果 Optional 对象不为空,则执行一些操作
javaCopy code
Optional<String> optional = Optional.of("Hello world!");
optional.ifPresent(str -> System.out.println(str));
  1. 如果 Optional 对象不为空,则返回它的值,否则返回一个默认值
javaCopy code
Optional<String> optional = Optional.ofNullable(null);
String str = optional.orElse("Default value");
System.out.println(str);
  1. 如果 Optional 对象不为空,则返回它的值,否则返回一个由 Supplier 接口提供的默认值
javaCopy code
Optional<String> optional = Optional.ofNullable(null);
String str = optional.orElseGet(() -> "Default value");
System.out.println(str);
  1. 如果 Optional 对象不为空,则返回它的值,否则抛出一个由 Supplier 接口提供的异常
javaCopy code
Optional<String> optional = Optional.ofNullable(null);
String str = optional.orElseThrow(() -> new RuntimeException("Value is null"));

以上是一些常用的 JDK Optional 类的示例。这些示例可以帮助你更好地理解 Optional 类的使用方法和语法。在实际开发中,可以根据具体的需求使用 Optional 类来处理可能为空的值,避免空指针异常的出现。

JDK 1.8高级特性以及教程

结语

JDK 1.8 的新特性和改进可以帮助开发人员更高效地编写 Java 应用程序,从而提高生产力和产能。下期再会!

转载自:https://juejin.cn/post/7211467514412220471
评论
请登录