Java8常用新特性

Java8概述。Java8,也就是jdk7.8,是意义深远的一个新版本,是Java5之后一个大的版本升级,让Java语言和库仿佛获得了新生。

新版本包含:

  1. 随着大数据的兴起,函数式编程在处理大数据上的优势开始体现,引入了Lambada函数式编程
  2. 使用Stream彻底改变了集合使用方式:只关注结果,不关心过程
  3. .新的客户端图形化工具界面库:JavaFX
  4. 良好设计的日期/时间API
  5. 增强的并发/并行API
  6. Java与JS交互引擎 -nashorn
  7. 其他特性

一、Lamdba表达式

什么是Lambda表达式?
Lambda表达式是带有参数变量的表达式,是一段可以传递的代码,可以被一次或多次执行,是一种精简的字面写法,其实就是把匿名内部类中“一定”要做的工作省略掉,然后由JVM通过推导把简化的表达式还原。
格式: (parameters参数) -> expression表达式或方法体
paramaters:

  1. 类似方法中的形参列表,这里的参数是函数式接口里的参数
  2. ->:可理解为“被用于”的意思
  3. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。如果负责运算的代码无法用表达式表示,可以使用编写方法实现,但必须用{}包围并按需明确使用return语句。

需求:对字符串数组按字符串

import java.util.Arrays;
import java.util.Comparator;

public class Demo1 {
    public static void main(String[] args) {
        // 定义字符串数组
        String[] strArr = {"abc", "cd", "abce", "a"};

        // 传统方法
        // 匿名内部类
        Arrays.sort(strArr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return Integer.compare(o2.length(), o1.length());
            }
        });

        // 输出排序结果
        for (String s : strArr) {
            System.out.println(s);
        }
        System.out.println("---------------------");

        //Lambda表达式
        Arrays.sort(strArr, (s1, s2) -> Integer.compare(s2.length(), s1.length()));

        // 输出
        for (String s : strArr) {
            System.out.println(s);
        }
    }
}

输出结果:

abce
abc
cd
a
---------------------
abce
abc
cd
a

Process finished with exit code 0

需求:用Lambda实现多线程

public class Demo2 {
    public static void main(String[] args) {
        // Lambda表达式
        new Thread(() -> System.out.println(1 + "hello world")).start();

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

        // 方法体
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(2 + "hello world");
            }
        }).start();
    }
}

输出结果:

----------------
1hello world
2hello world
2hello world
2hello world
2hello world
2hello world
2hello world
2hello world
2hello world
2hello world
2hello world

何时使用?
通过上面的两个需求,发现Lamdba表达式很简单,那何时使用呢?
需要显示创建函数式接口对象的地方,都可以使用
实际上函数式接口的转换是Lambda表达式唯一能做的事情
即lambda必须和Functional Interface配套使用
主要用于替换以前广泛使用的内部匿名类,各种回调
比如事件响应器、传入Thread类的Runnable等

.函数式接口分类

  1. 系统与定义函数接口(Comparator, Runnable)
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}
  1. 用户自定义函数接口(注解必须有,表达式是直接通过阐述列表来实现的,只能有一个有效方法)
@FunctionalInterface
public interface MyInterface {
	String info(String tip);
}

公共定义的函数式接口
从jdk1.8开始为了方便用户开发专门提供了一个新的包:java.util.function,在这个包里面针对于用户有可能做的函数式接口做了一个公共定义,最为核心的有四个接口:

  1. 功能性接口:Function<T,R>
    有输入参数,有返回值,是对接收一个T类型参数,返回R类型的结果的方法的抽象。通过调用apply方法执行内容。

需求:给定一个字符串,返回字符串长度

public class Demo3 {
    public static void main(String[] args) {
        // 定义字符串
        String str = "helloworld";

        // 调用方法
        // 在调用的时候写方法体,方法比较灵活
        int length = testFun(str, (s) -> s.length());
        System.out.println(length);
    }

    public static Integer testFun(String str, Function<String ,Integer> fun){
        // 执行
        Integer length = fun.apply(str);

        return length;
    }
}

输出结果:

10
  1. 消费型接口:Consumer< T >
    有输入参数,没返回值,对应的方法类型接收一个参数,没有返回值。一般来说使用Consumer接口往往伴随着一些期望状态的改变或事件的发生,典型的forEach就是使用的Consumer接口。虽然没有任何的返回值,但是向控制台输出结果Consumer使用accept对参数执行行为。

需求:输出字符串

import java.util.function.Consumer;

public class Demo4 {
    public static void main(String[] args) {
        // 创建字符串
        String str = "hello world";

        // 调用
        testConsumer(str, (s)-> System.out.println(s));
    }
    
    public static void testConsumer(String str, Consumer<String> consumer){
        // 执行
        consumer.accept(str);
    }
}

输出结果:

hello world
  1. 供给型接口:Supplier< T >
    无传入参数,有返回值,该接口对应的方法类型不接受参数,但是提供一个返回值,使用get()方法获得这个返回值。
import java.util.function.Supplier;

public class Demo5 {
    public static void main(String[] args) {
        // 创建字符串
        String str = "hello world";

        // 调用
        String sup = testSup(() -> str);

        System.out.println(sup);
    }

    public static String testSup(Supplier<String> sup){
        // 执行
        String s = sup.get();
        return s;
    }
}

输出结果:

hello world
  1. 断言型接口:Predicate< T >
    有传入参数,有返回值Boolean。该接口对应的方法为接收一个参数,返回一个Boolean类型值,多用于判断与过滤,使用test()方法执行这段行为。

需求:输入字段串,判断长度是否大于0

import java.util.function.Predicate;

public class Demo6 {
    public static void main(String[] args) {
        // 创建字符串
        String str = "hello world";

        // 调用
        boolean flag = testPre(str, (s) -> s.length() > 0);

        System.out.println(flag);
    }

    public static boolean testPre(String str, Predicate<String> pre) {
        // 执行
        boolean flag = pre.test(str);

        return flag;
    }
}

输出结果:

true

Lambda的优点

  1. 极大的减少代码冗余,同时可读性也好过冗长的匿名内部类
  2. 与集合类批处理操作结合,实现内部迭代,并充分利用现代多核CPU进行并行计算。之前集合类的迭代都是外部的,即客户代码。而内部迭代意味着由Java类库来进行迭代,而不是客户代码

和匿名内部类的区别
3. 在lambda中,this不是指向lambda表达式产生的那个对象,而是它的外部对象
4. Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法,但每一个匿名内部类编译器会为其创建一个类文件

二、方法引用

  1. 概述:某些lambda表达式里面仅仅是调用了一个已存在的方法,在这种情况下直接通过方法名称引用方法的形式可读性更高一些,这种形式就是方法引用方法引用是一种更简洁易懂的lambda 表达式替换其唯一用途就是支持Lambda的简写。
    |-类静态方法引用-|-ContainingClass::staticMethodName-|
    |-某个对象的方法-|-ContainingObject::instanceMethodName-|
    |-特定类的对象的方法引用-|-ContainingType::methodName-|
    |-构造方法引用-|-ClassName::new-|
    函数接口中抽象方法的参数列表,必须与方法引用方法的参数列表保持一致,方法引用中::后只是方法名,不能加();forEach()也是jdk8的新特性,比如:list.forEach((s) -> System.out.println(s));—list.forEach(Syetem.out::println);

  2. 类静态方法引用
    需求:求绝对值,使用Function实现

import java.util.function.Function;

public class Demo7 {
    public static void main(String[] args) {
        // 调用
        long l1 = testAbs(-10L, (s) -> Math.abs(s));

        // 改进
        long l2 = testAbs(-10, Math::abs);

        System.out.println(l1);
        System.out.println(l2);
    }

    public static long testAbs(long s, Function<Long, Long> fun) {
        Long l = fun.apply(s);
        return l;
    }
}

输出结果:

10
10
  1. 实例方法引用

循环集合中的元素,使用forEach方法,(s)->System.out.println(s)的类型是Cnsumer类型,其accept接收参数和println一致。

import java.util.ArrayList;
import java.util.List;
 
public class Demo8 {
	public static void main(String[] args) {
		// 创建集合
		List<String> list = new ArrayList<>();
		
		// 添加元素
		list.add("e");
		list.add("c");
		list.add("a");
		list.add("d");
		list.add("b");
		
		// 排序
		list.sort((s1, s2) -> s1.compareTo(s2));
		
		// 遍历
		list.forEach((s) -> System.out.println(s));
		list.forEach(System.out::println);
	}
}
  1. 构造方法引用

在引用构造器的时候,构造器参数列表要与接口中抽象方法的参数列表一致。
格式为:类名::new

import java.util.function.Supplier;
 
public class Demo9 {
	public static void main(String[] args) {
		getString(String :: new);
	}
	
	public static void getString(Supplier<String> su) {
		String s = su.get();
		System.out.println(s == null);
	}
}
  1. 任意对象的实例方法

若Lambda表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法。
参考上面中的排序。

三、Stream流

  1. 思考:计算字段串List中长度大于2的元素的长度。
    分析:使用增强for循环
    a. 代码冗余
    b. 实现并行计算很麻烦
    c. 代码无法及时传递程序员的意图 ,必须读完代码
  2. 外部迭代
    forEach工作原理:代码底层使用Iterator进行迭代的,是串行化操作。
    在这里插入图片描述
  3. 内部迭代
    在这里插入图片描述
  4. Stream概述

Stream是用函数式编程方式在集合类上进行复杂操作的工具,更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作。高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作。Stream 会隐式地在内部进行遍历,做出相应的数据转换,而和迭代器又不同的是,Stream 可以并行化操作,借助于 Lambda 表达式,极大的提高编程效率和程序可读性。

  1. 常用操作
  • forEach
    迭代集合中元素。接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式,此操作是及早求值方法。
  • collect(toList())
    由Stream 里的值生成一个列表,是一个及早求值操作,很多Stream 操作都是惰性求值,因此调用Stream 一系列方法之后,还需最后再调用一个类似collect 的及早求值方法返回集合。
    开篇的例子:(再将符合要求的字符串放到一个新的集合里),使用filter过滤器:遍历数据并检查其中的元素。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
public class Demo10 {
	public static void main(String[] args) {
		// 创建集合
		List<String> list = new ArrayList<>();
 
		// 添加元素
		list.add("sdf");
		list.add("a");
		list.add("asdf");
		list.add("d");
		list.add("basdfgh");
 
		// 统计长度大于2的
		long count = list.stream().filter((s) -> s.length() > 2).count();
 
		// 将符合要求的放入集合
		List<String> list2 = list.stream().filter((s) -> s.length() > 2).collect(Collectors.toList());
 
		System.out.println(count);
		list2.forEach(System.out :: println);
	}
}
  1. map

如果有一个函数可以将一种类型的值转换成另外一种类型
map 操作就可以使用该函数,将一个流中的值转换成一个新的流。
需求:将字符串全转换成大写

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
public class Demo11 {
	public static void main(String[] args) {
		// 创建集合
		List<String> list = new ArrayList<>();
 
		// 添加元素
		list.add("sdf");
		list.add("a");
		list.add("asdf");
		list.add("d");
		list.add("basdfgh");
		
		// 转换为大写
		List<String> list2 = list.stream().map((s) -> s.toUpperCase()).collect(Collectors.toList());
		
		list2.forEach(System.out :: println);
	}
}
  1. filter

遍历数据并检查其中的元素。例如获取字符串List中以数字开始的字符集合

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
public class Demo12 {
	public static void main(String[] args) {
		// 创建集合
		List<String> list = new ArrayList<>();
 
		// 添加元素
		list.add("1sdf");
		list.add("a");
		list.add("2asdf");
		list.add("d");
		list.add("basdfgh");
 
		// 获取数字开头的
		List<String> list2 = list.stream().filter((s) -> Character.isDigit(s.charAt(0))).collect(Collectors.toList());
 
		list2.forEach(System.out::println);
	}
}
  1. flatMap

可用Stream 替换值, 然后将多个Stream 连接成一个Stream
map 操作可用一个新的值代替Stream 中的值
若用户希望让map操作有点变化
生成一个新的Stream 对象取而代之则flatMap适用
假设有一个包含多个列表的流,现在希望得到所有数字的序列

import java.util.Arrays;
import java.util.stream.Stream;
 
public class Demo13 {
	public static void main(String[] args) {
 
		Stream<?> flatMap = Stream.of(Arrays.asList("a", "b"), Arrays.asList(1, 2, 3)).flatMap((s) -> s.stream());
		flatMap.forEach(System.out :: println);
	}
}
  1. max和min

获取Stream中最大值或最小值,获取字符串List中长度最长的字符串长度

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class Demo14 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		
		list.add("abc");
		list.add("ab");
		list.add("abcd");
		list.add("abcde");
		
		// 获取最大值
		int max = list.stream().map((s) -> s.length()).max(Integer :: compareTo).get();
		System.out.println(max);
		
		// 获取最小值,另一种方法
		int min = list.stream().min(Comparator.comparing((s) -> s.length())).get().length();
		System.out.println(min);
	}
}
  1. reduce

通过指定的函数把stream中的多个元素汇聚为一个元素,min max等是它的特例
格式:reduce(初始值,(r, i) -> r + i) 或者 reduce((r, i) -> r + “*” i)
计算1~100的和

import java.util.ArrayList;
import java.util.List;
 
public class Demo15 {
	public static void main(String[] args) {
		List<Long> list = new ArrayList<>();
		
		// 封装到集合
		for (long i = 1; i <= 100; i++) {
			list.add(i);
		}
		
		// 计算
		// reduce:参1,和的初始值
		Long sum = list.stream().parallel().reduce(0L, (r, l) -> r + l);
		
		System.out.println(sum);
	}
}
  1. 练习

获取Student集合中年龄小于20岁的集合中年龄最大的学生信息
Student类省略

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class Demo16 {
	public static void main(String[] args) {
		List<Student> list = new ArrayList<>();
 
		Student s1 = new Student("张三", 21);
		Student s2 = new Student("李四", 19);
		Student s3 = new Student("王五", 18);
		Student s4 = new Student("程六", 17);
		Student s5 = new Student("赵七", 20);
 
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		list.add(s5);
 
		// 筛选
		Student student = list.stream().filter((s) -> s.getAge() < 20).max(Comparator.comparing((s) -> s.getAge()))
				.get();
		System.out.println(student);
	}
}

查找集合中以a开头的字符的长度集合

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class Demo17 {
	public static void main(String[] args) {
		List<Integer> list = Stream.of("abc", "b", "a", "abcd").filter((s) -> s.startsWith("a")).map((s) -> s.length())
				.collect(Collectors.toList());
		System.out.println(list);
	}
}

四、并行API

  1. 概述

未完待续…

个人博客-ifknow

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值