文章目录
Java 8(1.8 版本)相对于 Java 7(1.7 版本)带来了一些重要的变化和新特性。以下是 Java 8 相对于 Java 7 的主要区别:
1. Lambda 表达式
Java 8 引入了 Lambda 表达式,它是一种简洁的语法用于表示匿名函数。Lambda 表达式可以提供更简洁、更灵活的代码编写方式,并且可以与函数式接口一起使用,使得函数式编程能够在 Java 中更加便捷。
下面是一个使用Lambda表达式的简单代码案例:
import java.util.ArrayList;
import java.util.List;
public class LambdaExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("David");
// 使用Lambda表达式对列表进行遍历并输出每个元素
names.forEach(name -> System.out.println(name));
// 使用Lambda表达式对列表进行筛选,并输出筛选结果
names.stream()
.filter(name -> name.startsWith("A"))
.forEach(name -> System.out.println(name));
}
}
这个例子演示了如何使用Lambda表达式对一个字符串列表进行遍历和筛选操作。首先,我们创建一个names
列表,并添加一些字符串元素。然后,我们使用forEach()
方法结合Lambda表达式来遍历列表的每个元素,并将其输出到控制台上。
接下来,我们使用stream()
方法将列表转化为一个流,并使用filter()
方法结合Lambda表达式来筛选以"A"开头的字符串。最后,我们再次使用forEach()
方法来遍历筛选结果,并将其输出到控制台上。
通过Lambda表达式,我们可以简洁地编写出具有功能性的代码,避免了传统的匿名内部类的冗余和繁琐。它使得代码更加易读、易写,并且提供了更高效的函数式编程方式。
2. 函数式接口
Java 8 引入了函数式接口的概念,即只包含一个抽象方法的接口。函数式接口可以与 Lambda 表达式结合使用,从而实现函数式编程的特性。
下面是一个使用函数式接口的简单代码案例:
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
public class FunctionalInterfaceExample {
public static void main(String[] args) {
// 使用Lambda表达式创建函数式接口的实现
MathOperation addition = (a, b) -> a + b;
// 调用函数式接口的方法
int result = addition.operate(5, 3);
System.out.println("5 + 3 = " + result);
}
}
这个例子演示了如何定义和使用一个函数式接口。首先,我们使用@FunctionalInterface
注解标记MathOperation
接口,确保其只包含一个抽象方法。然后,我们定义了一个使用两个整数作为参数并返回它们之和的 operate()
方法。
接下来,在main()
方法中,我们使用Lambda表达式创建了一个函数式接口的实现,即将两个整数相加的操作。最后,我们调用函数式接口实例的operate()
方法,并将结果输出到控制台上。
通过函数式接口,我们可以以更简洁的方式定义和使用具有功能性的接口。它允许我们以函数的形式传递行为,并且使代码更加灵活、可读和可维护。在Java 8中,函数式接口被广泛应用于Lambda表达式和Stream API等功能。
3. 流式 API(Stream API)
Java 8 引入了新的流式 API,它提供了一种更便捷的集合操作方式。通过流式 API,可以对集合进行过滤、映射、聚合等各种操作,简化了代码的编写,并提升了代码的可读性和性能。
下面是一个使用流式API(Stream API)的简单代码案例:
import java.util.ArrayList;
import java.util.List;
public class StreamAPIExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("David");
// 使用流式API对列表进行筛选,并输出筛选结果
names.stream()
.filter(name -> name.length() > 4)
.forEach(System.out::println);
// 使用流式API对列表进行转换,并输出转换结果
names.stream()
.map(name -> name.toUpperCase())
.forEach(System.out::println);
}
}
这个例子演示了如何使用流式API对一个字符串列表进行筛选和转换操作。首先,我们创建一个names
列表,并添加一些字符串元素。
接下来,我们使用stream()
方法将列表转化为一个流,并使用filter()
方法结合Lambda表达式来筛选出长度大于4的字符串。然后,我们使用forEach()
方法将筛选结果输出到控制台上。
接着,我们再次使用stream()
方法将列表转化为一个新的流,并使用map()
方法结合Lambda表达式将每个字符串转换为大写。最后,我们使用forEach()
方法将转换结果输出到控制台上。
通过流式API,我们可以以一种流畅、声明性的方式处理集合数据。它提供了一系列的中间操作(如filter()
和map()
)和最终操作(如forEach()
和collect()
),使我们能够轻松地对数据进行处理和转换。这种函数式风格的编程方式可以使代码更加简洁和易读,并且能够有效地利用多核处理器提高性能。
4. 方法引用
Java 8 引入了方法引用的概念,它允许直接引用已有方法作为 Lambda 表达式的替代,进一步简化了代码的编写。
下面是一个使用方法引用(Method Reference)的简单代码案例:
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 使用方法引用输出每个字符串的长度
names.forEach(System.out::println);
// 使用方法引用将每个字符串转换为大写并输出
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
}
}
这个例子演示了如何使用方法引用来引用已存在的方法。首先,我们创建一个names
列表,并使用Arrays.asList()
方法将一些字符串添加到列表中。
在第一个例子中,我们使用方法引用System.out::println
来引用System.out
对象的println()
方法。这样,我们可以通过forEach()
方法直接输出每个字符串。
在第二个例子中,我们使用方法引用String::toUpperCase
来引用String
类的toUpperCase()
方法。这样,我们可以通过map()
方法将每个字符串转换为大写形式,并通过forEach()
方法输出转换结果。
方法引用可以使代码更加简洁和可读。它提供了一种简化Lambda表达式的方式,允许我们直接引用已经存在的方法,而不需要重复编写匿名函数的逻辑。通过方法引用,我们可以直接利用现有的方法来定义行为,提高代码的可维护性和复用性。
5. 默认方法(Default Methods)
Java 8 允许接口中包含默认方法(即带有方法体的接口方法)。这样一来,接口可以直接提供一些默认实现,而不需要所有实现类都去重写这些方法。
下面是一个使用默认方法(Default Methods)的简单代码案例:
interface Shape {
void draw();
default void printInfo() {
System.out.println("This is a shape.");
}
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle.");
}
@Override
public void printInfo() {
System.out.println("This is a rectangle.");
}
}
public class DefaultMethodExample {
public static void main(String[] args) {
Shape circle = new Circle();
circle.draw();
circle.printInfo();
Shape rectangle = new Rectangle();
rectangle.draw();
rectangle.printInfo();
}
}
这个例子演示了如何在接口中定义并使用默认方法。首先,我们创建了一个Shape
接口,其中包含一个抽象方法draw()
和一个默认方法printInfo()
。
接着,我们创建了Circle
类和Rectangle
类,它们都实现了Shape
接口并重写了draw()
方法。Rectangle
类还重写了printInfo()
方法。
在main()
方法中,我们实例化了一个Circle
对象和一个Rectangle
对象,并调用它们的draw()
方法和printInfo()
方法。
通过默认方法,我们可以在接口中提供具体的方法实现,而不需要在所有实现类中重新实现相同的方法逻辑。这使得接口能够更灵活地扩展,而不会破坏已有的实现类。默认方法可以在接口的所有实现类中共享和使用,并且可以被子类重写以提供特定的行为。
6. Optional 类型
Java 8 引入了 Optional 类型,它可以用来表示一个值存在或者不存在。通过使用 Optional 类型,可以更加明确地处理可能为空的对象,避免空指针异常。
下面是一个使用Optional类的简单代码案例:
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
String str = "Hello, World!";
// 创建一个包含非空值的Optional对象
Optional<String> optionalValue = Optional.of(str);
// 判断Optional对象是否包含值
boolean isPresent = optionalValue.isPresent();
System.out.println("Is present: " + isPresent);
// 获取Optional对象中的值
String value = optionalValue.get();
System.out.println("Value: " + value);
// 创建一个空的Optional对象
Optional<String> emptyOptional = Optional.empty();
// 使用orElse()方法获取Optional对象中的值,如果为空则返回默认值
String orElseValue = emptyOptional.orElse("Default Value");
System.out.println("OrElse Value: " + orElseValue);
// 使用orElseGet()方法获取Optional对象中的值,如果为空则通过Supplier提供的逻辑生成默认值
String orElseGetValue = emptyOptional.orElseGet(() -> "Generated Default Value");
System.out.println("OrElseGet Value: " + orElseGetValue);
// 使用map()方法对Optional对象中的值进行转换
Optional<String> mappedOptional = optionalValue.map(strValue -> strValue.toUpperCase());
System.out.println("Mapped Optional Value: " + mappedOptional.orElse(""));
}
}
这个例子演示了如何使用Optional类来处理可能为空的值。首先,我们创建一个str
字符串变量。
我们使用Optional.of()
方法创建一个包含非空值的Optional对象,并使用isPresent()
方法判断Optional对象是否包含值。然后,我们使用get()
方法获取Optional对象中的值。
接着,我们使用Optional.empty()
方法创建一个空的Optional对象,并使用orElse()
方法和orElseGet()
方法来获取Optional对象中的值或提供默认值。
最后,我们使用map()
方法对Optional对象中的值进行转换,将字符串值转换为大写形式。
Optional类提供了一种优雅的方式来处理可能为空的值,避免了空指针异常。它提供了一系列的方法来判断Optional对象是否包含值、获取Optional对象中的值、获取默认值等操作。通过使用Optional类,我们可以编写更加健壮、可读性更高的代码。
7. 新的日期时间 API
Java 8 替换了旧的 Date 和 Calendar 类,引入了全新的日期时间 API。新的日期时间 API 提供了更加简洁、易用和线程安全的方式来处理日期、时间和时间间隔。
下面是一个使用新的日期时间API(java.time包)的简单代码案例:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeAPIExample {
public static void main(String[] args) {
// 获取当前日期
LocalDate currentDate = LocalDate.now();
System.out.println("Current Date: " + currentDate);
// 获取当前时间
LocalTime currentTime = LocalTime.now();
System.out.println("Current Time: " + currentTime);
// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current Date and Time: " + currentDateTime);
// 格式化日期和时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = currentDateTime.format(formatter);
System.out.println("Formatted Date and Time: " + formattedDateTime);
// 解析字符串为日期和时间
String dateTimeStr = "2023-07-04 10:30:15";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter);
System.out.println("Parsed Date and Time: " + parsedDateTime);
}
}
这个例子演示了如何使用新的日期时间API。首先,我们使用LocalDate.now()
方法获取当前日期,并使用LocalTime.now()
方法获取当前时间。使用LocalDateTime.now()
方法可以同时获取当前日期和时间。
我们使用DateTimeFormatter.ofPattern()
方法创建一个格式化器,将日期和时间格式化为指定的格式,并使用format()
方法将LocalDateTime
对象格式化为字符串。
接着,我们使用LocalDateTime.parse()
方法将字符串解析为LocalDateTime
对象,需要传入相应的格式化器。
新的日期时间API提供了一组强大且易于使用的类和方法来处理日期和时间。它们提供了更好的可读性和线程安全性,并且支持更丰富的操作,例如日期比较、日期计算、时区转换等。通过使用新的日期时间API,我们可以更轻松地处理日期和时间相关的任务。
除了上述主要的变化和新特性,Java 8 还包含了一些其他的改进,例如并发 API 的增强、新的重复注解支持等等。这些变化和特性使得 Java 8 成为一个更加强大和现代化的版本,帮助开发者可以更方便地编写高效、可读性更好的代码。