java8简记

2017-11-12 15:46:14

使用java8有一段时间了, 简单记录一下

Lambda

Lambda类似于匿名内部类,一个简单的小栗子:

new Thread(() -> {
            System.out.println("hello, Lambda");
        }).start();

可以看到使用Lambda更简洁

lambda表达式和内部类有点类似,可以访问类属性,final方法局部变量.
注意 :更改lambda表达式中的变量不是线程安全的.
对于只包含一个抽象方法的接口, 可以通过lambda表达式创建该接口对象.这种接口称为函数式接口.
(接口可以重新声明Object类的方法,如ToString, clone, equals, 这些不是抽象方法, java8的default方法也不是) :

Comparator<Integer> c = (final Integer t1, final Integer t2) -> {return Integer.compare(t1, t2);};

上面栗子中, final是为了演示添加的,可以省略, 参数类型Integer java可以推导出来.
方法体中, 因为只要一句代码,所以return和{}可以省略

Comparator<Integer> c = (t1, t2) ->  Integer.compare(t1, t2);

甚至是

Comparator<Integer> c = Integer::compare;

上面使用了方法引用, 使用::操作符引用一个方法,有三种方式

  • 类::静态方法, 调用参数作为静态方法的参数 Integer::parseInt 等价于 s -> Integer.parseInt(s) , 对象对象s作为parseInt参数
  • 类::实例方法, 第一个参数就是执行方法的对象 Object::toString 等价于 o -> o.toString(), 直接调用调用对象o的toString方法
  • 构造方法

java8提供了许多常用的函数式接口

这些函数式接口可以作为类属性,方法参数,甚至返回值
如, 提供一个解析xml的接口, 由用户判断一个节点是否要处理, 如果要处理, 就交由用户进行处理. 可以使用如下方法定义:

public void handle(Predicate<Node> p, Consumer<Node> c)

而不用只定义NodeCheck, NodeHanler等接口

Optional

使用Optional可以有效的减小null的判断,特别是map的取值,如从一个Map<String, Object> map获取”amount”的值并转化为int(假定map或map.get(“amount”)都可能为null),
以往的写法:

    Integer i = null;
    Object o = map.get("amount");
    if(o != null) {
        String s = o.toString();
        if(s != null) {
            i = Integer.parseInt(s);
        }
    }

而使用Optional则非常简洁

Integer i = optional.map(k -> k.get("amount")).map(o -> o.toString()).map(s -> Integer.parseInt(s)).orElse(null);

参考: 使用 Java8 Optional 的正确姿势

个人觉得:虽然Optional使用方便,但始终觉得方法定义Optional<User> getUser(String code)
没有User getUser(String code)直观

stream

对于stream, 先来看个小栗子, 对一个List<Integer> list, 过滤掉其中的奇数:

list  = list.stream().filter(i -> i % 2 == 0).collect(Collectors.toList());

上面栗子中,可以分为3步:

  1. list.stream()创建一个stream
  2. filter(Predicate<? super T> predicate)进行过滤操作, 仅保留predicate返回true的元素
  3. collect(Collector<? super T, A, R> collector)进行结果收集

除了filter, java8还提供了常用的元素处理方案:

  • map 对stream中元素的类型转换
  • limit 取前n个元素
  • skip 丢弃前n个元素
  • distinct 丢弃重复的方法
  • sort

收集结果

  • toArray 到数组
  • collect(Collectors.toList()) 到List
  • collect(Collectors.toMap) 到Map

还有其他的聚合方案

  • count
  • max
  • min
  • findFirst
  • findAny
  • allMatch
  • noneMatch

还可以使用stream分组
对于上个栗子中的list, 现在按奇偶数分成两组:

Map<Boolean, List<Integer>> oddMap = list.stream().collect(Collectors.partitioningBy(i -> i % 2 == 0));

这是个很有用的特性, 因为我们常常要进行分组操作, 如对于一组user, 可以按地区, 用户等级等属性进行分组.

注意
可以很方便将list转成map

Map<String, User> users = userList.stream().collect(Collectors.toMap(User::getCode, c -> c));

这里如果userList中用两个User.getCode()返回结果相同, 则会抛出异常java.lang.IllegalStateException: Duplicate key 1

时间api

java8还有个比较大的改动,就是时间API。
java8加入了LocalDate和LocalTime, 它们使用的是ISO8601标准时间格式,和时区无关,和unix时间戳类似.
unix时间戳表示从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数。 也是说, 一个unix时间戳n, 对于每个地区, 它都表示该地区从1970年1月1日(UTC/GMT的午夜)开始经过n秒后的时间。
如1507564800表示2017/10/10 00:00:00, 如果在北京, 则表示北京时间2017/10/10 00:00:00, 如果在伦敦, 则表示伦敦时间2017/10/10 00:00:00(北京时间和伦敦时间时差八小时, 此时北京时间是2017/10/10 08:00:00)
LocalDate和LocalTime同理。

// 当前时间
LocalDate now = LocalDate.now();
// 当前时间加一天, 并按常用格式yyyy-MM-dd格式化
now.plusDays(1).format(DateTimeFormatter.BASIC_ISO_DATE);
// 当前时间减一天, 并按指定格式格式化
now.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

// 字符串转时间
LocalDate date = LocalDate.parse("2019-01-26", DateTimeFormatter.ISO_DATE);
// 时间比较
now.isAfter(date);

LocalDate与Date转换

// 时区ZoneId LocalDate时区不相关, Date时区, 两者转化需使用ZoneId
ZoneId zoneId = ZoneId.systemDefault();

LocalDate localDate = LocalDate.now();
// localDate转ZonedDateTime
ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
// ZonedDateTime转Date
Date date = Date.from(zdt.toInstant());

Date nowDate = new Date();
// Instant:nanosecond表示的时间戳
Instant instant = nowDate.toInstant();
// instant转ZonedDateTime
ZonedDateTime nowZonedDate = instant.atZone(zoneId);
// ZonedDateTime转LocalDate
LocalDate nowLocalDate = nowZonedDate.toLocalDate();

java7的新特性

简单记录一下java7比较重要的新特性

Path

java7新增了Path类, 可以便捷的对目录进行操作
当前resources目录结构为

|-- resources
    |-- local.properties
    |-- xml
        |-- local.xml

看一个小栗子

@Test
public void testPath() throws URISyntaxException {
    // 获取local.properties文件
    URI localProUri = this.getClass().getClassLoader().getResource("local.properties").toURI();

    // 转换为Path对象, 指向 .../resources/test/local.properties
    Path localProPath = Paths.get(localProUri);

    // 解析localProPath兄弟目录, 指向 .../resources/test/xml
    Path xmlPath = localProPath.resolveSibling("xml");

    // 解析xmlPath目录目录, 指向 .../resources/test/xml/local.xml
    Path localXmlPath = xmlPath.resolve("local.xml");

    // 转换为绝对路径
    localXmlPath.toAbsolutePath();

    // 获取文件名
    localXmlPath.getFileName();

    // 获取根目录
    localXmlPath.getRoot();

    // 获取父目录
    localXmlPath.getParent();
}

Files

java7也新增了Files类, 可以对文件进行读取,写入, 复制,移动等操作


@Test
public void testFileRead() throws URISyntaxException, IOException {
    Path localProPath = Paths.get(this.getClass().getClassLoader().getResource("local.properties").toURI());
    // 读取文件中所以的字节
    byte[] localProBytes = Files.readAllBytes(localProPath);
}


@Test
public void testFileCopy() throws URISyntaxException, IOException {
    Path localProPath = Paths.get(this.getClass().getClassLoader().getResource("local.properties").toURI());
    // 复制文件, StandardCopyOption.REPLACE_EXISTING表示替换已存在的文件
    Path targetPath = Files.copy(localProPath, localProPath.getParent().resolve("dev.properties"), StandardCopyOption.REPLACE_EXISTING);
}

@Test
public void testAppend() throws URISyntaxException, IOException {
    Path localProPath = Paths.get(this.getClass().getClassLoader().getResource("local.properties").toURI());

    // 追加文件,StandardOpenOption.APPEND表示追加
    Files.write(localProPath, "group=local".getBytes(), StandardOpenOption.APPEND);
}

这个有点类似于org.apache.commons.io.FileUtils类了。

try-with-resources

AutoCloseable是java7添加的接口, 实现了该接口的资源类可以使用try-with-resources语法

try (InputStream inputStream = new FileInputStream("local.properties")) {
    ...
} catch (IOException e) {
    e.printStackTrace();
}

对比一下原来的写法

InputStream inputStream = null;
try {
     inputStream = new FileInputStream("local.properties");
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if(inputStream != null) {
        try {
            inputStream.close();
        } catch (IOException e) {
        }
    }
}

捕获多个异常

java7中可以在同一个catch分支中捕获多个异常类型。引用《写给大忙人看的JavaSE8》中的一个栗子:

try {
    ...
} catch(FileNotFoundException | UnknownHostException ex) {
    // 处理丢失文件和未知主机的异常
} catch() {
    // 处理所有的IO异常
}

Objects

java7新增了Objects类, 提供一些常用的Object操作, 如deepEquals,isNull,nonNull,requireNonNull等。

参考:
Java8学习笔记
《写给大忙人看的Java SE 8》