Java 8 (又称 jdk8) 是 Java 开发的主要版本,Oracle 公司于 2014 年 3 月 18 日发布 Java 8.
使用 Lambda 表达式需要注意的地方
什么是方法引用
不同方法的引用
实例
// 方法引用
public class PracticeTest {
@Test
public void test() {
// 构造器引用
final Car car = Car.create(Car::new);
final List<Car> cars = new ArrayList<>(Collections.singletonList(car));
// 静态方法引用
cars.forEach(Car::collide);
// 特定类的任意对象的方法引用
cars.forEach(Car::repair);
// 特定对象的方法引用
final Car police = Car.create(Car::new);
cars.forEach(police::follow);
}
}
class Car {
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collide " + car.toString());
}
public void follow(final Car anthor) {
System.out.println("Follow the " + anthor.toString());
}
public void repair() {
System.out.println("Repaird " + this.toString());
}
}
什么是函数式接口
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
函数式接口可以被隐式转换为 lambda 表达式。
JDK 1.8 新增加的函数接口:
实例
public class PracticeTest {
@Test
public void test() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
eval(list, n -> n % 2 == 0);
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for (Integer n : list){
if (predicate.test(n)){
System.out.println(n + " ");
}
}
}
}
什么是默认方法
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个 default 关键字即可实现默认方法。
为什么要有这个特性?
首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在 JDK 里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
实例
public class PracticeTest implements Vehicle, FourWheeler {
@Test
public void test() {
this.print();
}
@Override
public void print() {
System.out.println("我是一辆四轮汽车");
Vehicle.super.print();
// 静态默认方法
Vehicle.blowHorn();
FourWheeler.super.print();
}
}
interface Vehicle {
default void print() {
System.out.println("我是第一辆车");
}
static void blowHorn() {
System.out.println("按喇叭");
}
}
interface FourWheeler {
default void print() {
System.out.println("我是一辆四轮车");
}
}
什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
和以前的 Collection 操作不同, Stream 操作还有两个基础的特征:
生成流
在 Java 8 中, 集合接口有两个方法来生成流:
实例
public class PracticeTest {
@Test
public void test() {
// 计算空字符串
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.stream().filter(String::isEmpty).count();
count = strings.stream().filter(i -> i.length() == 3).count();
List<String> stringList = strings.stream().filter(i -> !i.isEmpty()).collect(Collectors.toList());
String s = strings.stream().filter(i -> !i.isEmpty()).collect(Collectors.joining(","));
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> numberCollect = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList());
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
IntSummaryStatistics intSummaryStatistics = integers.stream().mapToInt(i -> i).summaryStatistics();
intSummaryStatistics.getMax();
intSummaryStatistics.getMin();
intSummaryStatistics.getSum();
intSummaryStatistics.getAverage();
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
}
}
引入 Optional 类的目的是什么?
Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent() 方法会返回 true,调用 get() 方法会返回该对象。
Optional 是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
建议
实例
**public class PracticeTest {
@Test
public void test() {
Integer value1 = null;
Integer value2 = 10;
System.out.println(this.sum(value1, value2));
}
public Integer sum(Integer a, Integer b) {
// 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
Optional<Integer> ao = Optional.ofNullable(a);
Optional<Integer> bo = Optional.ofNullable(b);
// Optional.isPresent - 判断值是否存在
System.out.println("第一个参数值存在:" + ao.isPresent());
System.out.println("第二个参数值存在:" + bo.isPresent());
// Optional.orElse - 如果值存在,返回它,否则返回默认值
Integer value1 = ao.orElse(0);
Integer value2 = bo.orElse(0);
//Optional.get - 获取值,值需要存在
return value1 + value2;
}
}**
N a s h o r n J a v a S c r i p t E n g i n e 在 J a v a 15 已经不可用了。 \color{red}{Nashorn JavaScript Engine 在 Java 15 已经不可用了。} NashornJavaScriptEngine在Java15已经不可用了。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
新的 java.time 包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
本地化日期时间 API
LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。
实例
public class PracticeTest {
@Test
public void test() {
// 获取当前的日期时间
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("当前时间:" + currentTime);
LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1:" + date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int second = currentTime.getSecond();
System.out.println("月:" + month + ",日:" + day + ",秒:" + second);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2:" + date2);
LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3:" + date3);
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4:" + date4);
// 解析字符串
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5:" + date5);
LocalDateTime date6 = LocalDateTime.parse("2012-07-10 14:05:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("date6:" + date6);
}
}
使用时区的日期时间API
需要考虑到时区,就可以使用时区的日期时间 API
实例
public class PracticeTest {
@Test
public void test() {
// 获取当前的日期时间
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
System.out.println("date1:" + date1);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId:" + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("当前时区:" + currentZone);
}
}
在 Java 8 中,Base64 编码已经成为 Java 类库的标准。
Java 8 内置了 Base64 编码的编码器和解码器。
Base64 工具类提供了一套静态方法获取下面三种 BASE64 编解码器:
实例
public class PracticeTest {
@Test
public void test() {
// 获取当前的日期时间
String base64encodeString = Base64.getEncoder().encodeToString("runoob?java8".getBytes(StandardCharsets.UTF_8));
System.out.println("Base64 编码字符串(基本):" + base64encodeString);
byte[] base64decodeBytes = Base64.getDecoder().decode(base64encodeString);
System.out.println("原始字符串: " + new String(base64decodeBytes));
base64encodeString = Base64.getUrlEncoder().encodeToString("runoob?java8".getBytes(StandardCharsets.UTF_8));
System.out.println("Base64 编码字符串 (URL):" + base64encodeString);
// StringBuilder 是线程不安全的,不能同步访问
// 使用 StringBuffer 时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新对象
// 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder。
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; i++) {
stringBuilder.append(UUID.randomUUID().toString());
}
byte[] mimeBytes = stringBuilder.toString().getBytes(StandardCharsets.UTF_8);
String mimeEncodeString = Base64.getMimeEncoder().encodeToString(mimeBytes);
System.out.println("Base64 编码字符串(mime):" + mimeEncodeString);
}
}
Oracle 官网
菜鸟教程