java 8 新特性

基于项目中使用的例子说明java8的新特性

Posted by Sun Jianjiao on March 1, 2015

Java 8让变成变得更容易,代码变得更简洁。有lambda表达式,接口的默认方法,流,新的日期和时间API,Optional还有CompletableFuture。

1 lambda表达式

使用匿名类表示不同的行为并不令人满意,代码啰嗦,它可以让我们很简单的表示一个行为或者代码传递。

例如启动线程读取阻塞队列里面的数据。

   private void messageProcess() {
        Runnable runnable = () -> {
            // ... ...
            while (true) {
                // ... ...
                // 这里是使用drainTo的一个最佳实践,
                // - drainTo是非阻塞的
                // - take是阻塞的
                // 利用take保证有数据才进行后续操作,然后通过drainTo批量读取提高效率
                entity = mqttMsgBlockingQueue.take();
                if (entity == null) {
                    continue;
                }
                size = mqttMsgBlockingQueue.drainTo(entities, 32);
                if (size == 512) {
                    log.warn("entity number is 512, system is busy");
                }
                entities.add(entity);
                // ... ...
            }
        };

        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(runnable);    // 这里通过lambda表达式出入Runnable
            thread.setName("process-parameter-thread-" + i);
            thread.start();
        }
    }

lambda表达式格式: () -> {}

()中是参数,{}中是lambda的主题。

2 默认方法

Java 8以前,实现接口的类必须为借口中定义的每所有方法提供实现,或者从父类中继承他的实现。如果一个接口增加了新方法了,实现了所有这个接口的类全都需要跟着修改,这是多么让人抓狂的事情。 Java 8的接口中现在支持声明方法的同时提供实现。如果实现接口的类不显示的提供该方法的具体实现,就会继承默认的实现。这种机制不仅可以简化实现类的开发,并不用实现所有的接口;更能保证接口平滑的进行优化和演进。

如采集线程需要实现的接口实现:

public interface DataAcquisitionWorker {
    void execute(final ScheduleEvent event);
    String genKey(String deviceName);

    default int getThreadSize(ScheduleEvent event) {
        return 10;
    }

    default int getQueueSize(ScheduleEvent event) {
        return 40;
    }
}

通过default定义了默认的采集线程的工作线程数量和队列的长度。这样就可以保证很多实现可以使用默认的数值,不用自己定制。

3 流

流是JavaAPI的新成员,提供声明式处理数据集合的方式,并且可以透明的进行并行处理。

工艺参数返回的时候,需要通过分级的方式返回数据,数据格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"设备ID:
    { "工艺参数1": 
        [
            {
                "value": "120",
                "acquisitionTime": "2019-06-25 16:35:20"
            },
            {
                "value": "121",
                "acquisitionTime": "2019-06-25 16:35:25"
            }
        ]
    }

通过java流就可以很方便的对数据进行分组和过滤。

public Map<String, Map<String, List<ProcessParameterCompleteDTO>>> listOutLimit(String startTime, String endTime) {
    List<ProcessParameterCompleteDTO> processParameterEntities = dao.listOutLimit(startTime, endTime);

    Map<String, Map<String, List<ProcessParameterCompleteDTO>>> outLLimitMap = processParameterEntities.stream().collect(
            groupingBy(ProcessParameterCompleteDTO::getDeviceName,
            groupingBy(ProcessParameterCompleteDTO::getName)));

    return outLLimitMap;
}

4 新的日期和时间API

Java 8之前的时间日期的API非常的不理想,如DateFormat不是线程安全的,Date非常难用不说,Date和Calendar的类是可变的,所以也不是线程安全的。所以Joda Time在Java日期/时间需求中扮演了高质量替换的重要角色。java 8再java.time中整合了很多Joda-Time的特性。Java 8支持如下类:

  • LocalDate: 不可变对象,提供简单的日期
  • LocalTime: 表示1天中的时间
  • LocalDateTime: 日期和时间的合体
  • Instant:机器的日期和时间格式。
  • Duration:用来表示对象间的持续时间
  • Period: 表示时间间隔

我们分表时按照时间分表的,通过Java 8的LocalDateTime很方便的进行时间的加减(如minusHours)。也可以很方获取年和月等(getYear,getMonth)。

public String doSharding(final Collection<String> tableNames, final PreciseShardingValue<Timestamp> shardingValue) {
    Timestamp acquisition_time = shardingValue.getValue();

    LocalDateTime localDateTime = acquisition_time.toLocalDateTime().minusHours(offset);

    /*
    * 按照季度分表, 工作班次是有偏移的, 需要将同一个班次放在一张表中,便于分析和查询
    * */
    String tableName =shardingValue.getLogicTableName() +  "_" +
            localDateTime.getYear() + "_" +
            ((localDateTime.getMonth().getValue() - 1) / 3 + 1);

    return tableName;
}