目录

  • static?default? lamda表达式?
  • 常见的函数式接口
  • 方法引用
  • Stream总结
  • Optinal用法介绍
  • 新的日期API LocalDate | LocalTime | LocalDateTime
  • CompletableFuture异步编程
  • jdk1.8-ForkJoin框架剖析

static?default? lamda表达式?

package com.wanke.microgrid.mgcc;@FunctionalInterface
public interface NullAnnotation {//抽象方法public void getName(String a);boolean equals(Object obj);//default方法default void getAge(){System.out.println("age");}default void getAge1(){}//静态方法 static  void static1(){System.out.println("static1");}static  void static2(){}
}
package com.wanke.microgrid.mgcc;public class Test{public static void main(String[] args) {String a = "aaaa";//Lambda表达式NullAnnotation annotation = (f) -> System.out.println(f);annotation.getName(a);//defalut方法属于对象的默认方法annotation.getAge();//static属于类的方法NullAnnotation.static1();}
}

运行结果:

aaaa
age
static1

@FunctionalInterface标记接口为函数式接口,函数式接口有且只能有一个抽象方法,或许大家会问你不是写了两个抽象方法吗?java.lang.Object根对象有equals方法吧,所以实现该类的时候是不是不必须要实现这个接口(所有的类都是实现java.lang.Object的)。Lambda表达式是基于函数式接口的

常见的函数式接口

package com.wanke.microgrid.mgcc;import java.util.Comparator;
import java.util.Objects;
import java.util.function.*;public class Test {//Consumer消费型接口,有参无返回static void testConsumer(Integer x, Consumer<Integer> consumer) {consumer.accept(x);}//suplier供给型接口,无参有返回值static void testSupplier(Supplier<Integer> supplier) {System.out.println(supplier.get());}static Integer getInteger() {return 2;}//函数式接口,有参有返回值static void testFunction(Integer num, Function<Integer, Integer> function) {System.out.println(function.apply(num));}//断言型接口,有参有返回值,返回值是boolean类型static void testPredicate(Integer num, Predicate<Integer> predicate) {System.out.println(predicate.test(num));}//介绍部分拓展接口//BinaryOperator (R apply(T t, U u) ->两个输入,一个输出,而且类型一样)public static void binaryOperatorCompute(Integer para1, Integer para2, BinaryOperator<Integer> binaryOperator) {//使用自定义BinaryOperatorSystem.out.println(binaryOperator.apply(para1, para2));Comparator<Integer> cpt2 = (x, y) -> Integer.compare(x, y);//静态方法传入比较器生成BinaryOperatorBinaryOperator<Integer> min = BinaryOperator.minBy(cpt2);System.out.println(min.apply(para1, para2));}//toIntFunction (int applyAsInt(T t, U u) ->两个输入,一个int输出)public static void testToIntFunction(Integer para1, Integer para2, ToIntBiFunction<Integer, Integer> toIntBiFunction) {System.out.println(toIntBiFunction.applyAsInt(para1, para2));}//BiFunction (R apply(T t, U u) ->两个输入,一个输出)public static void testBi(Integer para1,Integer para2,BiFunction<Integer, Integer, Integer> biFunction, Function<Integer, Integer> function) {BiFunction<Integer, Integer, Integer> newBi = biFunction.andThen(function);System.out.println(newBi.apply(para1,para2));}//如何看出输入输出,以BiFunction为例//源码如下:
//package java.util.function;
//
//import java.util.Objects;
//
//    /**
//     * Represents a function that accepts two arguments and produces a result.
//     * This is the two-arity specialization of {@link Function}.
//     *
//     * <p>This is a <a href="package-summary.html">functional interface</a>
//     * whose functional method is {@link #apply(Object, Object)}.
//     *
//     * @param <T> the type of the first argument to the function
//     * @param <U> the type of the second argument to the function
//     * @param <R> the type of the result of the function
//     *
//     * @see Function
//     * @since 1.8
//     */
//    @FunctionalInterface
//    public interface BiFunction<T, U, R> {//
//        /**
//         * Applies this function to the given arguments.
//         *
//         * @param t the first function argument
//         * @param u the second function argument
//         * @return the function result
//         */
//        R apply(T t, U u);
//
//        /**
//         * Returns a composed function that first applies this function to
//         * its input, and then applies the {@code after} function to the result.
//         * If evaluation of either function throws an exception, it is relayed to
//         * the caller of the composed function.
//         *
//         * @param <V> the type of output of the {@code after} function, and of the
//         *           composed function
//         * @param after the function to apply after this function is applied
//         * @return a composed function that first applies this function and then
//         * applies the {@code after} function
//         * @throws NullPointerException if after is null
//         */
//        default <V> java.util.function.BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {//            Objects.requireNonNull(after);
//            return (T t, U u) -> after.apply(apply(t, u));
//        }
//    }
//函数式接口类型的唯一抽象方法 R apply(T t, U u);显然是量输入一输出,而且是泛型public static void main(String[] args) {testConsumer(1, x -> System.out.println(x));testSupplier(() -> getInteger());testFunction(100, x -> x + 100);testPredicate(100, x -> x.equals(100));binaryOperatorCompute(1, 2, (x, y) -> x + y);testToIntFunction(3, 4, (x, y) -> x + y + 1);testBi(5,9,(x,y)->{int z = x+y;return z;},x->x + 3);}}

运行结果:

1
2
200
true
3
1
8
17

总之jdk8提供了一种方便的方式让我们传递函数,就像传递参数一样,大家对照着可以看看js的回调。

方法引用

定义了4个方法的Car这个类作为例子,区分Java中支持的4种不同的方法引用。

public static class Car {public static Car create( final Supplier< Car > supplier ) {return supplier.get();}                       public static void collide( final Car car ) {System.out.println( "Collided " + car.toString() );}         public void follow( final Car another ) {System.out.println( "Following the " + another.toString() );}         public void repair() {  System.out.println( "Repaired " + this.toString() );}}

第一种方法引用是构造器引用,它的语法是Class::new,或者更一般的Class< T >::new。请注意构造器没有参数。

 final Car car = Car.create( Car::new );final List< Car > cars = Arrays.asList( car );

第二种方法引用是静态方法引用,它的语法是Class::static_method。请注意这个方法接受一个Car类型的参数

   cars.forEach( Car::collide );

第三种方法引用是特定类的任意对象的方法引用,它的语法是Class::method。请注意,这个方法没有参数。

   cars.forEach( Car::repair );

第四种方法引用是特定对象的方法引用,它的语法是instance::method。请注意,这个方法接受一个Car类型的参数

   final Car police = Car.create( Car::new );cars.forEach( police::follow )

方法和函数都有输入输出,本质上是类似的,所以我们可以引用方法来代替lamda表达式,这样或许好理解一点。

Stream总结

Stream的操作符大体上分为两种:中间操作符和终止操作符

中间操作符
对于数据流来说,中间操作符在执行制定处理程序后,数据流依然可以传递给下一级的操作符。

中间操作符包含8种(排除了parallel,sequential,这两个操作并不涉及到对数据流的加工操作):

1.map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符。

2.flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 变成 2,3,4 也就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符。

3.limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。

4.distint 去重操作,对重复元素去重,底层使用了equals方法。

5.filter 过滤操作,把不想要的数据过滤。

6.peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
skip 跳过操作,跳过某些元素。

7.sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。

终止操作符

数据经过中间加工操作,就轮到终止操作符上场了;终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。

1.collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的
Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors。

2.count 统计操作,统计最终的数据个数。

3.findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。

4.noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。

5.min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值。

6.reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。

7.forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。

8.toArray 数组操作,将数据流的元素转换成数组。

parallel会利用多核,但要注意线程安全(利用外部变量时可能会造成线程安全问题,外部变量指的是流操作外面申明的变量)

Optinal用法介绍

  //Optional.of()或者Optional.ofNullable():创建Optional对象,差别在于of不允许参数是null,而ofNullable则无限制。// 参数不能是nullOptional<Integer> optional1 = Optional.of(1);// 参数可以是nullOptional<Integer> optional2 = Optional.ofNullable(null);// 参数可以是非nullOptional<Integer> optional3 = Optional.ofNullable(2);//Optional.empty():所有null包装成的Optional对象:Optional<Integer> optional4 = Optional.ofNullable(null);Optional<Integer> optional5 = Optional.ofNullable(null);System.out.println(optional4 == optional5);// trueSystem.out.println(optional5 == Optional.<Integer>empty());// trueObject o1 = Optional.<Integer>empty();Object o2 = Optional.<String>empty();System.out.println(o1 == o2);// true//isPresent():判断值是否存在Optional<Integer> optional6 = Optional.ofNullable(1);Optional<Integer> optional7 = Optional.ofNullable(null);// isPresent判断值是否存在System.out.println(optional6.isPresent() == true);System.out.println(optional7.isPresent() == false);Optional<Integer> optional8 = Optional.ofNullable(1);Optional<Integer> optional9 = Optional.ofNullable(null);// 如果不是null,调用Consumeroptional8.ifPresent(new Consumer<Integer>() {@Overridepublic void accept(Integer t) {System.out.println("value is " + t);}});// null,不调用Consumeroptional9.ifPresent(new Consumer<Integer>() {@Overridepublic void accept(Integer t) {System.out.println("value is " + t);}});//orElse(value):如果optional对象保存的值不是null,则返回原来的值,否则返回valueOptional<Integer> optional10 = Optional.ofNullable(1);Optional<Integer> optional11 = Optional.ofNullable(null);// orElseSystem.out.println(optional10.orElse(1000) == 1);// trueSystem.out.println(optional11.orElse(1000) == 1000);// true//orElseGet(Supplier supplier):功能与orElse一样,只不过orElseGet参数是一个对象Optional<Integer> optional12 = Optional.ofNullable(1);Optional<Integer> optional13 = Optional.ofNullable(null);System.out.println(optional12.orElseGet(() -> {return 1000;}) == 1);//trueSystem.out.println(optional3.orElseGet(() -> {return 1000;}) == 1000);//true//orElseThrow():值不存在则抛出异常,存在则什么不做,有点类似Guava的PrecoditionsOptional<Integer> optional14 = Optional.ofNullable(1);Optional<Integer> optional15 = Optional.ofNullable(null);optional14.orElseThrow(()->{throw new IllegalStateException();});try{// 抛出异常optional15.orElseThrow(()->{throw new IllegalStateException();});}catch(IllegalStateException e ){e.printStackTrace();}//filter(Predicate):判断Optional对象中保存的值是否满足Predicate,并返回新的Optional。Optional<Integer> optional16 = Optional.ofNullable(1);Optional<Integer> optional17 = Optional.ofNullable(null);Optional<Integer> filter1 = optional16.filter((a) -> a == null);Optional<Integer> filter2 = optional16.filter((a) -> a == 1);Optional<Integer> filter3 = optional17.filter((a) -> a == null);System.out.println(filter1.isPresent());// falseSystem.out.println(filter2.isPresent());// trueSystem.out.println(filter2.get().intValue() == 1);// trueSystem.out.println(filter3.isPresent());// false//map(Function):对Optional中保存的值进行函数运算,并返回新的Optional(可以是任何类型)Optional<Integer> optional18 = Optional.ofNullable(1);Optional<Integer> optional19 = Optional.ofNullable(null);Optional<String> str1Optional = optional18.map((a) -> "key" + a);Optional<String> str2Optional = optional19.map((a) -> "key" + a);//flatMap():功能与map()相似,差别请看如下代码。flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。Optional<Integer> optional1 = Optional.ofNullable(1);Optional<Optional<String>> str3Optional = optional1.map((a) -> {return Optional.<String>of("key" + a);});Optional<String> str4Optional = optional1.flatMap((a) -> {return Optional.<String>of("key" + a);});System.out.println(str3Optional.get().get());// key1System.out.println(str4Optional.get());// key1//Optional应用示例Person person = new Person();Car car = new Car();Insurance insurance = new Insurance();insurance.setName("aa");car.setInsurance(insurance);person.setCar(car);String insuranceName = Optional.ofNullable(person).map((p) -> p.getCar()).map(c -> c.getInsurance()).map(i -> i.getName()).orElse("unknown");System.out.println(insuranceName);}static class Person {private Car car;public Car getCar() {return car;}public void setCar(Car car) {this.car = car;}}static class Car {private Insurance insurance;public Insurance getInsurance() {return insurance;}public void setInsurance(Insurance insurance) {this.insurance = insurance;}}static class Insurance {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}

新的日期API LocalDate | LocalTime | LocalDateTime

新的日期API都是不可变的,更使用于多线程的使用环境中

@Test
public void test(){// 从默认时区的系统时钟获取当前的日期时间。不用考虑时区差LocalDateTime date = LocalDateTime.now();//2018-07-15T14:22:39.759System.out.println(date);System.out.println(date.getYear());System.out.println(date.getMonthValue());System.out.println(date.getDayOfMonth());System.out.println(date.getHour());System.out.println(date.getMinute());System.out.println(date.getSecond());System.out.println(date.getNano());// 手动创建一个LocalDateTime实例LocalDateTime date2 = LocalDateTime.of(2017, 12, 17, 9, 31, 31, 31);System.out.println(date2);// 进行加操作,得到新的日期实例LocalDateTime date3 = date2.plusDays(12);System.out.println(date3);// 进行减操作,得到新的日期实例LocalDateTime date4 = date3.minusYears(2);System.out.println(date4);
}
   @Testpublic void test2(){// 时间戳  1970年1月1日00:00:00 到某一个时间点的毫秒值// 默认获取UTC时区Instant ins = Instant.now();System.out.println(ins);System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());System.out.println(System.currentTimeMillis());System.out.println(Instant.now().toEpochMilli());System.out.println(Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant().toEpochMilli());}
   @Testpublic void test3(){// Duration:计算两个时间之间的间隔// Period:计算两个日期之间的间隔Instant ins1 = Instant.now();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Instant ins2 = Instant.now();Duration dura = Duration.between(ins1, ins2);System.out.println(dura);System.out.println(dura.toMillis());System.out.println("======================");LocalTime localTime = LocalTime.now();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}LocalTime localTime2 = LocalTime.now();Duration du2 = Duration.between(localTime, localTime2);System.out.println(du2);System.out.println(du2.toMillis());}
@Testpublic void test4(){LocalDate localDate =LocalDate.now();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}LocalDate localDate2 = LocalDate.of(2016,12,12);Period pe = Period.between(localDate, localDate2);System.out.println(pe);}
   @Testpublic void test5(){// temperalAdjust 时间校验器// 例如获取下周日  下一个工作日LocalDateTime ldt1 = LocalDateTime.now();System.out.println(ldt1);// 获取一年中的第一天LocalDateTime ldt2 = ldt1.withDayOfYear(1);System.out.println(ldt2);// 获取一个月中的第一天LocalDateTime ldt3 = ldt1.withDayOfMonth(1);System.out.println(ldt3);LocalDateTime ldt4 = ldt1.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));System.out.println(ldt4);// 获取下一个工作日LocalDateTime ldt5 = ldt1.with((t) -> {LocalDateTime ldt6 = (LocalDateTime)t;DayOfWeek dayOfWeek = ldt6.getDayOfWeek();if (DayOfWeek.FRIDAY.equals(dayOfWeek)){return ldt6.plusDays(3);}else if (DayOfWeek.SATURDAY.equals(dayOfWeek)){return ldt6.plusDays(2);}else {return ldt6.plusDays(1);}});System.out.println(ldt5);}
   @Testpublic void test6(){// DateTimeFormatter: 格式化时间/日期// 自定义格式LocalDateTime ldt = LocalDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");String strDate1 = ldt.format(formatter);String strDate = formatter.format(ldt);System.out.println(strDate);System.out.println(strDate1);// 使用api提供的格式DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;LocalDateTime ldt2 = LocalDateTime.now();String strDate3 = dtf.format(ldt2);System.out.println(strDate3);// 解析字符串to时间DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");LocalDateTime time = LocalDateTime.now();String localTime = df.format(time);LocalDateTime ldt4 = LocalDateTime.parse("2017-09-28 17:07:05",df);System.out.println("LocalDateTime转成String类型的时间:"+localTime);System.out.println("String类型的时间转成LocalDateTime:"+ldt4);}
   // ZoneTime  ZoneDate       ZoneDateTime@Testpublic void test7(){LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));System.out.println(now);LocalDateTime now2 = LocalDateTime.now();ZonedDateTime zdt = now2.atZone(ZoneId.of("Asia/Shanghai"));System.out.println(zdt);Set<String> set = ZoneId.getAvailableZoneIds();set.stream().forEach(System.out::println);}

LocalDate

public static void localDateTest() {//获取当前日期,只含年月日 固定格式 yyyy-MM-dd    2018-05-04LocalDate today = LocalDate.now();// 根据年月日取日期,5月就是5,LocalDate oldDate = LocalDate.of(2018, 5, 1);// 根据字符串取:默认格式yyyy-MM-dd,02不能写成2LocalDate yesteday = LocalDate.parse("2018-05-03");// 如果不是闰年 传入29号也会报错LocalDate.parse("2018-02-29");}
 /*** 日期转换常用,第一天或者最后一天...*/public static void localDateTransferTest(){//2018-05-04LocalDate today = LocalDate.now();// 取本月第1天: 2018-05-01LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());// 取本月第2天:2018-05-02LocalDate secondDayOfThisMonth = today.withDayOfMonth(2);// 取本月最后一天,再也不用计算是28,29,30还是31: 2018-05-31LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());// 取下一天:2018-06-01LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1);// 取2018年10月第一个周三 so easy?:  2018-10-03LocalDate thirdMondayOf2018 = LocalDate.parse("2018-10-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY));}

LocalTime

 public static void localTimeTest(){//16:25:46.448(纳秒值)LocalTime todayTimeWithMillisTime = LocalTime.now();//16:28:48 不带纳秒值LocalTime todayTimeWithNoMillisTime = LocalTime.now().withNano(0);LocalTime time1 = LocalTime.parse("23:59:59");}

LocalDateTime

//转化为时间戳  毫秒值long time1 = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();System.out.println(time1);//时间戳转化为localdatetimeDateTimeFormatter df= DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss.SSS");System.out.println(df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time1),ZoneId.of("Asia/Shanghai"))));

CompletableFuture异步编程

https://cloud.tencent.com/developer/article/1366581

jdk1.8-ForkJoin框架剖析

https://www.jianshu.com/p/f777abb7b251

一篇整理JDK8新特性相关推荐

  1. Java番外篇2——jdk8新特性

    Java番外篇2--jdk8新特性 1.Lambda 1.1.无参无返回值 public class Test {interface Print{void print();}public static ...

  2. 【JDK8新特性】之Lambda表达式

    目录 Lambda表达式 1. 需求分析 2.Lambda表达式初体验 3. Lambda的语法规则 3.1 Lambda练习1 3.2 Lambda练习2 4. @FunctionalInterfa ...

  3. JDK8新特性的学习总结

    (尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/87903464冷血之心的博客) 目录 背景: 正文: Lambda ...

  4. JDK8 新特性流式数据处理

    在学习JDK8新特性Optional类的时候,提到对于Optional的两个操作映射和过滤设计到JDK提供的流式出来.这篇文章便详细的介绍流式处理: 一. 流式处理简介 流式处理给开发者的第一感觉就是 ...

  5. JDK8新特性相关知识讲解汇总

    以下是博主本人对jdk8相关新知识的学习以及讲解的汇总,如果对你帮助可以多多点赞和关注我哦~ (1)JDK8新特性-Lambda表达式详细介绍以及应用 (2)JDK8 关于中断线程的总结 (3)JDK ...

  6. JDK8新特性-Optional类

    个人简介 作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门. 文章目录 个人简介 JDK8新特性-Option ...

  7. JDK8新特性之函数式接口

    转载自 JDK8新特性之函数式接口 什么是函数式接口 先来看看传统的创建线程是怎么写的 Thread t1 = new Thread(new Runnable() {@Overridepublic v ...

  8. JDK8新特性之重复注解

    转载自 JDK8新特性之重复注解 什么是重复注解 下面是JDK8中的重复注解( java.lang.annotation.Repeatable)定义的源码. @Documented @Retentio ...

  9. JDK8新特性之Optional

    转载自 JDK8新特性之Optional Optional是什么 java.util.Optional Jdk8提供 Optional,一个可以包含null值的容器对象,可以用来代替xx != nul ...

最新文章

  1. 大TTT需要复习的课件PPT以及大作业完成链接
  2. java String长度与varchar长度匹配理解(字符和字节长度理解)
  3. Nginx的http块其他的配置指令说明
  4. HDU - 5157 Harry and magic string(回文自动机)
  5. resolv.conf
  6. vector占用内存的释放
  7. java分布式会话redis_详解springboot中redis的使用和分布式session共享问题
  8. hive java udf_UDF_Hive教程_田守枝Java技术博客
  9. 使用code::blocks搭建objective-c的IDE开发环境 支持 @interface
  10. CSS三角制作(HTML、CSS)
  11. bad interpreter: No such file or directory
  12. 房地产“产权分割制”是什么大杀器?
  13. java开源内容发布系统_18个Java开源CMS系统一览
  14. Simulink 搭建模型【MATLAB官网帮助】
  15. 广义线性模型和广义加法模型_广义线性模型代码
  16. 【C语言】输入两个正整数m和n,求其最大公约数和最小公倍数。(要求用while语句实现)
  17. SurfaceView使用日记(二)--实例:绘画板
  18. 软件发布各版本代号含义
  19. http协议,http状态码,请求,响应
  20. Linux 时间、时区设置

热门文章

  1. Flutter之 GridView
  2. 清理缓存的问题ctrl+F5或者引入外部js的时候?加任意数字
  3. 深入浅出静态链接和动态链接
  4. EasyExcel导出包含多个sheet的Excel
  5. windows10删除文件时遇到“拒绝访问”的解决方法
  6. c语言实验报告课程名称,c语言实验报告
  7. 整车OTA布局新一轮「市场红利」
  8. 基于Qt的类似QQ好友列表抽屉效果的实现
  9. Construct Binary Tree from Preorder and Inorder Traversal(C++从前序与中序遍历序列构造二叉树)
  10. 【Web技术】1518- 抛弃 moment.js,基于 date-fns 封装日期相关 utils