美文网首页
java8简记

java8简记

作者: binecy | 来源:发表于2019-03-23 18:22 被阅读0次

    使用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));
    

    根据属性分组

    Map<String, List<User>> cityGroup = list.stream().collect(Collectors.groupingBy(User::getCity));
    

    这是个很有用的特性, 因为我们常常要进行分组操作, 如对于一组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
    可以使用userList.stream().collect(Collectors.toMap(User::getCode, c -> c, (a, b) -> a))解决这个问题,它表示,当两个User.getCode()结果相同时,取第一个User。

    时间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》

    相关文章

      网友评论

          本文标题:java8简记

          本文链接:https://www.haomeiwen.com/subject/xvosvqtx.html