在JDK8版本中 引入了很多新的内容 分为
新的语法
新的功能
新的底层实现
等等
ArrayList 被设计为了懒加载的模式 初始化无参构造 只维护一个空列表 当我们第一次添加元素 才将数组初始化为10
HashMap加入了红黑树数据结构
接口中可以书写普通方法 使用default关键字修饰 加在返回值类型之前 访问修饰符之后
接口中可以书写静态方法
public interface A {
public default void m1(){
}
public static void m2(){
}
}函数式接口:即一个接口中只有一个抽象方法 这样的接口称之为 SAM接口 Single Abstract Method 这样的接口可以使用注解@FunctionalInterface修饰 称之为函数式接口
只要一个接口中只有一个抽象方法,即可称之为函数式接口函数式编程属于一种编程思想 就像面向过程 面向对象 等等 都属于编程思想
函数式编程的代表语言是Haskell 更强调函数(方法)可以实现什么操作,执行了什么功能,不注重是哪个角色调用了这个函数(方法)
使用函数式编程 即表示前提必须为函数式接口
lambda表达式即函数式编程的最终实现
回顾匿名内部类 :即我们可以”直接new“接口 或者抽象类 相当于创建一个匿名内部类
使用了lambda表达式以后 之前匿名内部类书写格式混乱的问题 可以得到解决
前提:lambda表达式只能用于函数式接口
写法越简洁 前期越难理解 后期使用越方便
lambda表达式格式:()->
/**
* 回顾匿名内部类 :即我们可以”直接new“接口 或者抽象类 相当于创建一个匿名内部类
* 使用了lambda表达式以后 之前匿名内部类书写个数混乱的问题 可以得到解决
* 前提:lambda表达式只能用于函数式接口
*
* 写法越简洁 前期越难理解 后期使用越方便
*/
public class B {
public static void main(String[] args) {
C c1 = new C() {
@Override
public void m1() {
System.out.println("匿名内部类的方式重写m1方法");
}
};
c1.m1();
// 无参 无返回值 只有一条语句
C c2 = ()-> System.out.println("lambda表达式的方式重写m1方法");
c2.m1();
// 有一个参数 无返回值 只有一条语句
D d1 = (a)-> System.out.println("lambda表达式方式重写D接口m1方法" + a);
d1.m1(100);
// 有两个个参数 无返回值 只有一条语句
E e1 = (a,b)-> System.out.println("lambda表达式方式重写E接口m1方法" + a + b);
e1.m1(123, "abc");
// 有两个参数 有返回值 只有一条语句
F f1 = (a,b)-> a + b;
System.out.println(f1.m1(10, 20));
// 有一个参数 有返回值 有多条语句
F f2 = (a,b)->{
System.out.println(a + b);
return a + b;
};
}
}
interface F {
int m1(int a,int b);
}
interface E {
void m1(int a,String b);
}
interface D {
void m1(int a);
}
interface C {
void m1();
}方法引用 :在lambda表达式的基础上 使用其他的方法的方法体 来作为 lambda表达式(函数式接口中)
抽象方法的方法体
具体细节:被引用的方法体 原本的方法 返回值 形参列表 必须和 函数式接口中抽象方法的返回值 形参列表 完全匹配
否则 将无法引用
方法引用格式 ::
构造方法引用 类名 :: new;
静态方法引用 类名 :: 方法名;
实例方法引用 对象名 :: 方法名;
/**
* 方法引用 :在lambda表达式的基础上 使用其他的方法的方法体 来作为 lambda表达式(函数式接口中)
* 抽象方法的方法体
*
* 具体细节:被引用的方法体 原本的方法 返回值 形参列表 必须和 函数式接口中抽象方法的返回值 形参列表 完全匹配
* 否则 将无法引用
*
* 方法引用格式 ::
* 构造方法引用 类名 :: new;
* 静态方法引用 类名 :: 方法名;
* 实例方法引用 对象名 :: 方法名;
*/
public class TestMethodReference {
public static void main(String[] args) {
A a1 = ()-> System.out.println("");
A a2 = Student :: new;
a2.m1();
B b1 = Student :: new;
b1.m1("赵四");
C c1 = Student :: new;
c1.m1("a", 20);
// 思考:哪个方法首先为静态方法
// 并且参数为布尔类型 返回值为String类型的
D<String,Boolean> d1 = String :: valueOf;
System.out.println(d1.m1(true).length());
D<Double,Double> d2 = Math :: abs;
System.out.println(d2.m1(20.0));
D<Integer,Float> d3 = Math :: round;
System.out.println(d3.m1(3.5F));
System.out.println("-----------------------------------------------------");
String str = "abc";
D<Boolean,String> d4 = str :: startsWith;
System.out.println(d4.m1("def"));
D<Boolean,String> d5 = str :: endsWith;
System.out.println(d5.m1("c"));
E<String> e1 = System.out :: println;
e1.m1("hello world");
}
}
interface E<P> {
void m1(P p);
}
interface D<R,P> {
R m1(P p);
}
interface C {
void m1(String str,int num);
}
interface B {
void m1(String str);
}
class Student {
private String name;
private int age;
public Student(String name) {
this.name = name;
System.out.println("单个参数name属性的构造方法");
}
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("两个参数 name age属性的构造方法");
}
public Student() {
System.out.println("无参构造方法");
}
}
interface A {
void m1();
}JDK提供的函数式接口位于 java.util.function 这个包中
这些函数式接口可以大致分为四类
消费型接口:Consumer accept(T t) 只接受参数没有返回值
功能型接口:Function<T,R> R apply(T t) 有参数 有返回值
供给型接口:Supplier T get() 没有参数 但是有返回值
断言型接口:Predicate boolean test(T t) 有参数有返回值 但是返回值固定为布尔类型
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* JDK提供的函数式接口位于 java.util.function 这个包中
* 这些函数式接口可以大致分为四类
* 消费型接口:Consumer<T> accept(T t) 只接受参数没有返回值
* 功能型接口:Function<T,R> R apply(T t) 有参数 有返回值
* 供给型接口:Supplier<T> T get() 没有参数 但是有返回值
* 断言型接口:Predicate<T> boolean test(T t) 有参数有返回值 但是返回值固定为布尔类型
*/
public class TestFunctional {
public static void main(String[] args) {
Consumer<Integer> consumer = System.out::println;
consumer.accept(100);
Function<String,Integer> function = Integer :: parseInt;
System.out.println(function.apply("123"));
Supplier<Double> supplier = Math ::random;
System.out.println(supplier.get());
Predicate<String> predicate = String :: isEmpty;
System.out.println(predicate.test("abc"));
}
}Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
1- 创建 Stream:通过一个数据源(如:集合、数组),获取一个流
2- 中间操作:每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象,因此中间操作可以是个操作链,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。
3- 终止操作:终止操作的方法返回值类型就不再是Stream了,因此一旦执行终止操作,就结束整个Stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。
创建 Stream方式一:通过集合Java8 中的 Collection 接口被扩展,提供了两个获取流的方法: default Stream stream() : 返回一个顺序流
创建 Stream方式二:通过数组Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static Stream stream(T[] array): 返回一个流
创建 Stream方式三:通过Stream的of()可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
public static Stream of(T... values) : 返回一个流
创建 Stream方式四:创建无限流(了解)可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
public static Stream generate(Supplier s)
| 方 法 | 描 述 |
|---|---|
filter(Predicate p) |
保存符合指定条件的元素 |
distinct() |
筛选,通过流所生成元素的equals() 去除重复元素 |
limit(long maxSize) |
保留指定个数的前 |
skip(long n) |
跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
sorted() |
产生一个新流,其中按自然顺序排序 |
map(Function f) |
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
flatMap(Function f) |
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
| 方法 | 描述 |
|---|---|
| boolean allMatch(Predicate p) | 检查是否匹配所有元素 |
| boolean anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
| boolean noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
| Optional findFirst() | 返回第一个元素 |
| long count() | 返回流中元素总数 |
| Optional max() | 返回流中最大值 |
| Optional min() | 返回流中最小值 |
| void forEach(Consumer c) | 迭代 |
到目前为止,空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
| 方法 | 描述 |
|---|---|
| of(Object obj) | 根据传入对象获取一个Optional对象,此对象不能为null |
| empty() | 包装一个保存有null的Optional对象 |
| ofNullable(Object obj) | 根据传入对象获取一个Optional对象,此对象可以为null |
| get() | 获取Optional中保存的对象,如果为null,则报空指针异常 |
| isPresent() | 表示判断Optional是否为null,为null结果为false,不为null结果为true |
| ifPresent(Consumer<? super T> consumer) | 如果Optional对象中的对象不为null,则消费此对象,否则不消费 |
| orElse(T other) | 如果当前Optional对象中保存对象为null,则使用传入对象 |
| orElseGet(Supplier<? extends T> other) | 如果当前Optional对象中为null则获取到另外一个对象,否则不获取 |
| orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果当前Optional对象中为null则抛出异常,否则不抛出 |