2020年了,你还不知道JAVA8新特性??

Java8新特性

lambda表达式

在JDK8之前,一个方法能接受的参数都是变量,例如: object.method(Object o)
那么,如果需要传入一个动作呢?比如回调。
那么你可能会想到匿名内部类。

//传统方式:
interface Printer{
    public void printer(integer id);
}
class Operation(){
		public static void create(integer id,Printer printer){
		     printer.printer(id);
		}
}
class demo{
	public static void main(String[] args) {
		Operation operation=new Operation();
		int id=1;
		//匿名内部类
		operation.create(i,new Printer(){
			 public void printer(integer id){
			   System.out.println("id:"+id);
			 }
		});
	}
}

Printer其实就是一种动作,但是我们真正关心的只有printer方法里的内容而已,我们用Lambda
表示,可以将上面的代码就可以优化成

//Lambda
public static void main(String[] args) {
		Operation operation=new Operation();
		int id=1;
		operation.create(id, x->System.out.println("id:"+x));
	}

如果我们的接口方法定义不带任何参数,则可以用空括号替换:

()->  System.out.println("anything you wan to print")

注意:

  • 即使没有在箭头的左侧指定参数的类型,编译器也会从接口方法的形式参数中推断出其类型
  • 当只有一个参数的时候,我们完全可以省略参数的括号
  • 当函数体只有一行的时候,我们完全可以省略函数体花括号

函数式接口

函数式接口是新增的一种接口定义。
用**@FunctionalInterface修饰的接口叫做函数式接口**,或者,函数式接口就是一个只具有一个抽象方法的普通接口,@FunctionalInterface可以起到校验的作用。

Java8中提供给我们这么多函数式接口就是为了让我们写Lambda表达式更加方便,当然遇到特殊情况,你还是需要定义你自己的函数式接口然后才能写对应的Lambda表达式。
总的来说,如果没有函数式接口,就不能写Lambda表达式。

接口的默认方法和静态方法

在接口中用default修饰的方法称为默认方法
接口中的默认方法一定要有默认实现(方法体),接口实现者可以继承它,也可以覆盖它。

    default String methodDefault(String s) {
        System.out.println("lol");
    }

方法引用

  • 实例对象::实例方法名

  • 类名::静态方法名

  • 类名::实例方法名

  • 引用构造方法

  • 引用数组

          // 引用方法1   实例对象::实例方法名
            // System.out代表的就是PrintStream类型的一个实例,println是这个实例的一个方法
    //        Consumer<String> consumer1 = s -> System.out.println(s);
            Consumer<String> consumer2 = System.out::println;
            consumer2.accept("呵呵");
     
            // 引用方法2   类名::静态方法名
            // Function中的唯一抽象方法apply方法参数列表与abs方法的参数列表相同,都是接收一个Long类型参数。
    //        Function<Long, Long> f = x -> Math.abs(x);
            Function<Long, Long> f = Math::abs;
            Long result = f.apply(-3L);
     
            // 引用方法3  类名::实例方法名
            // 若Lambda表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法
    //        BiPredicate<String, String> b = (x,y) -> x.equals(y);
            BiPredicate<String, String> b = String::equals;
            b.test("a", "b");
            // 引用构造方法
            // 在引用构造方法的时候,构造方法参数列表要与接口中抽象方法的参数列表一致,格式为 类名::new
    //        Function<Integer, StringBuffer> fun = n -> new StringBuffer(n);
            Function<Integer, StringBuffer> fun = StringBuffer::new;
            StringBuffer buffer = fun.apply(10);
    
      // 引用数组
            // 引用数组和引用构造器很像,格式为 类型[]::new,其中类型可以为基本类型也可以是类
    //        Function<Integer, int[]> fun = n -> new int[n];
            Function<Integer, int[]> fun1 = int[]::new;
            int[] arr = fun1.apply(10);
            Function<Integer, Integer[]> fun2 = Integer[]::new;
            Integer[] arr2 = fun2.apply(10);
    
    
  • 实例对象::实例方法名
    

引入了stream流(管道流)

Stream 是在 Java8 新增的特性,普遍称其为流;它不是数据结构也不存放任何数据,其主要用于集合的逻辑处理。Stream是对集合对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者打批量数据操作

这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选、排序、聚合等。
和以前的collection操作不同,Stream操作还有两个基础的特征:
Pipelining:中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格。这样做可以对操作进行优化,比如延迟执行和短路。
内部迭代:以前对集合遍历都是通过Iterator或者ForEach的方式,显示的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,通过访问者模式实现。并行流parallelStream

Stream管道流的基本操作

  • 源操作:可以将数组、集合类、行文本文件转换成管道流Stream进行数据处理
  • 中间操作:对Stream流中的数据进行处理,比如:过滤、数据转换等等
  • 终端操作:作用就是将Stream管道流转换为其他的数据类型。这部分我们还没有讲,我们后面章节再介绍。

管道的功能包括:Filter(过滤)、Map(映射)、sort(排序)等,集合数据通过Java Stream管道处理之后,转化为另一组集合或数据输出。

image-20200709170510127
List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");

List<String> list = nameStrs.stream()
        .filter(s -> s.startsWith("L"))
        .map(String::toUpperCase)
        .sorted()
        .collect(toList());
System.out.println(list);
  • 首先,我们使用Stream()函数,将一个List转换为管道流

  • 调用filter函数过滤数组元素,过滤方法使用lambda表达式,以L开头的元素返回true被保留,其他的List元素被过滤掉

  • 然后调用Map函数对管道流中每个元素进行处理,字母全部转换为大写

  • 然后调用sort函数,对管道流中数据进行排序

  • 最后调用collect函数toList,将管道流转换为List返回

    将数组转换为管道流

    使用Stream.of()方法,将数组转换为管道流。

    String[] array = {"Monkey", "Lion", "Giraffe", "Lemur"};
    Stream<String> nameStrs2 = Stream.of(array);
    
    Stream<String> nameStrs3 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur");
    
    将集合类对象转换为管道流

    通过调用集合类的stream()方法,将集合类对象转换为管道流。

    List<String> list = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
    Stream<String> streamFromList = list.stream();
    
    Set<String> set = new HashSet<>(list);
    Stream<String> streamFromSet = set.stream();
    
    将文本文件转换为管道流

    通过Files.lines方法将文本文件转换为管道流,下图中的Paths.get()方法作用就是获取文件,是Java NIO的API!

    也就是说:我们可以很方便的使用Java Stream加载文本文件,然后逐行的对文件内容进行处理。

    Stream<String> lines = Files.lines(Paths.get("file.txt"));
    

    参考:具体可以参考这个写的很详细,很棒

    Stream总结

    不是数据结构,它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据
    它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素
    所有 Stream 的操作必须以 lambda 表达式为参数
    惰性化,很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始,Intermediate操作永远是惰性化的
    当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的
    

Date/Time API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。对日期与时间的操作一直是Java程序员最痛苦的地方之一。标准的 java.util.Date以及后来的java.util.Calendar一点没有改善这种情况(可以这么说,它们一定程度上更加复杂)。
这种情况直接导致了Joda-Time——一个可替换标准日期/时间处理且功能非常强大的Java API的诞生。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影响,并且吸取了其精髓。
LocalDate类

    /**
     * LocaleDate只持有ISO-8601格式且无时区信息的日期部分
     */
    public static void testLocaleDate() {
        LocalDate date = LocalDate.now(); // 当前日期
 
        date = date.plusDays(1); // 增加一天
        date = date.plusMonths(1); // 减少一个月
        date = date.minusDays(1); // 减少一天
        date = date.minusMonths(1); // 减少一个月
        System.out.println(date);
    }

LocalTime类

    /**
     * LocaleTime只持有ISO-8601格式且无时区信息的时间部分
     */
    public static void testLocaleTime() {
        LocalTime time = LocalTime.now(); // 当前时间
        time = time.plusMinutes(1); // 增加一分钟
        time = time.plusSeconds(1); // 增加一秒
        time = time.minusMinutes(1); // 减少一分钟
        time = time.minusSeconds(1); // 减少1秒
        System.out.println(time);
    }

LocalDateTime类

    /**
     * LocaleDateTime把LocaleDate与LocaleTime的功能合并起来,它持有的是ISO-8601格式无时区信息的日期与时间
     */
    public static void testLocalDateTime() {
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime); // UTC格式
        System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); // 自定义格式
 
        // 原有方法
//        Date nowDate = new Date();
//        System.out.println(nowDate);
//        System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format(nowDate));
    }

ZoneDateTime类

    /**
     * ZonedDateTime持有ISO-8601格式,具有时区信息的日期与时间。
     */
    public static void testZonedDateTime() {
        ZonedDateTime zonedDateTime =  ZonedDateTime.now();
        System.out.println(zonedDateTime);
 
        ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
        System.out.println(zonedDatetimeFromZone);
 
        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId);
    }

Clock类

    /**
     * 它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。
     * Clock可以替换System.currentTimeMillis()与TimeZone.getDefault()
     */
    public static void testClock() {
        Clock utc = Clock.systemUTC(); // 世界标准时间
        System.out.println(LocalDateTime.now(utc));
 
        Clock shanghai = Clock.system(ZoneId.of("Asia/Shanghai")); // 上海时间
        System.out.println(LocalDateTime.now(shanghai));
    }

Duration类

    /**
     * Duration使计算两个日期间的不同变的十分简单。
     */
    public static void testDuration() {
        final LocalDateTime from = LocalDateTime.parse("2019-07-15 18:50:50", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        final LocalDateTime to = LocalDateTime.parse("2019-07-16 19:50:50", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        final Duration duration = Duration.between(from, to);
        System.out.println("Duration in days: " + duration.toDays()); // 1
        System.out.println("Duration in hours: " + duration.toHours()); // 25
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值