Java 8 新特性

匿名内部类

缺点:因为语法上的限制,代码不够简洁;这里最关键的是方法体

public class Test {
    public static void main(String[] args) {
        // 匿名内部类
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程任务执行!!!!");
            }
        };
        // 启动线程
        new Thread(runnable).start();
    }
}

 

Lambda表达式

语法

(形参列表) -> {Lambda体}

本质

  • 返回一个接口实例;该接口内需要实现的方法只有一个,但可以有其他默认的实现方法(default);

示例

public class Test {
    public static void main(String[] args) {
        // 代码更加简洁:不再有不得不创建接口对象的束缚,不再有抽象方法覆盖重写的负担
        new Thread(() -> System.out.println("多线程任务执行!!!!")).start();
    }
}

 接口函数

  • 核心:只需要关注输入什么,输出什么

 方法引用和构造器引用

  • 本质:简化Lambda表达式
// 方法引用
public class Test {
    public static void main(String[] args) {
        List<Integer> map = Arrays.asList(1, 2, 3, 4, 5);

        map.forEach(System.out::println);
    }
}

// 构造器引用
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3);
        Stream<int[]> stream1 = stream.map((i) -> new int[i]);
        // Lambda表达式
        stream1.forEach((i) -> System.out.println(i.length));

        System.out.println("---------------------");

        Stream<Integer> stream11 = Stream.of(1, 2, 3);
        Stream<int[]> stream2 = stream11.map(int[]::new);
        // 构造引用
        stream2.forEach((i) -> System.out.println(i.length));
    }
}

 

注:

  • lambda表达式本质上是在本类的一个方法里面执行的;所以lambda表达式里面的this会指向当前类;
  • 若lambda表达式里面用了this,则生成的方法是非静态的;否则是静态的;
  • 静态方法和实例方法的区别在于实例方法有this;
  • java里面默认把this作为参数,放到实例方法的第一个参数;

 

惰性求值

String msg = "打印一些日志:" + this
logger.fine(msg);

// 即使最后不打印日志,但字符串的拼接工作还是会执行
logger.fine(() -> "打印一些日志:" + this);

// 使用了lambda表达式之后,字符串的拼接放到一个函数里面,fine日志需要打印的时候才去调用这个方法才真正执行!

 

stream流编程

  • stream流中也使用惰性求值(外部迭代关注怎么做、内部迭代关注做什么);
  • Stream 自己不会存储元素,专门处理集合对象;集合强调存储数据,Stream强调处理数据;
  • Stream不会改变源对象,每次处理读会返回一个持有结果的新的Stream;

 

操作步骤

 

示例

import java.util.stream.IntStream;

public class StreamDemo1 {

  public static void main(String[] args) {
    int[] nums = { 1, 2, 3 };
    // 外部迭代
    int sum = 0;
    for (int i : nums) {
      sum += i;
    }
    System.out.println("结果为:" + sum);

    // 使用stream的内部迭代
    // map就是中间操作(返回stream的操作)
    // sum就是终止操作
    int sum2 = IntStream.of(nums).map(StreamDemo1::doubleNum).sum();
    System.out.println("结果为:" + sum2);

    System.out.println("惰性求值就是终止没有调用的情况下,中间操作不会执行");
    IntStream.of(nums).map(StreamDemo1::doubleNum);
  }

  public static int doubleNum(int i) {
    System.out.println("执行了乘以2");
    return i * 2;
  }
}

 

操作类型分类

  • 首先分为中间操作和最终操作;在最终操作没有调用的情况下,所有的中间操作都不会执行(惰性求值);
  • 一般来说:返回stream流的就是中间操作,可以继续链式调用下去;不是返回stream流的就是最终操作;

 

中间操作

 

终止操作

 

  • 中间操作也分为有状态操作和无状态操作;状态就是和其他数据有关系;
  • 如果方法只有一个参数,那就是无状态操作,否则就是有状态操作;
  • 在多个操作的时候,我们需要把无状态操作写在一起,有状态操作放到最后,这样效率会更加高;

 

  • 最终操作里面又分为短路操作和非短路操作;短路操作就是只需要得到指定个结果后就可以结束的终止操作;

 

package com.jxsr.controller;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

public class TestController {
    public static void main(String[] args) {

        Random random = new Random();
        // 随机产生数据
        Stream<Integer> stream = Stream.generate(() -> random.nextInt())
                // 产生500个 ( 无限流需要短路操作. )
                .limit(5)
                // 第1个无状态操作
                .peek(s -> print("peek: " + s))
                // 第2个无状态操作
                .filter(s -> {
                    print("filter: " + s);
                    return s > 1000000;
                })
                // 有状态操作
                .sorted((i1, i2) -> {
                    print("排序: " + i1 + ", " + i2);
                    return i1.compareTo(i2);
                })
                // 又一个无状态操作
                .peek(s -> {
                    print("peek2: " + s);
                })
                // 多线程(并行)
                .parallel();

        // 终止操作
        stream.count();


    }

    public static void print(String s) {
        // System.out.println(s);
        // 带线程名(测试并行情况)
        System.out.println(Thread.currentThread().getName() + " > " + s);
        try {
            TimeUnit.MILLISECONDS.sleep(5);
        } catch (InterruptedException e) {
        }
    }

}

  大家自己测试一下代码,能发现stream的调用方法,就像现实中的流水线一样,一个元素只会迭代一次,但如果中间有无状态操作,前后的操作会单独处理(元素就会被多次迭代);

  (个人理解,不一定对)经过无状态操作时每个元素单独过;经过有状态操作时所有元素一起过;

 

参考链接

【1】http://www.imooc.com/article/27181

posted @   先娶国王后取经  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示