Skip to main content

JDK8新特性

新增特性

新增什么特性

在JDK8版本中 引入了很多新的内容 分为

新的语法
新的功能
新的底层实现

集合相关

1、ArrayList 被设计为了懒加载的模式,
即初始化无参构造,只维护一个空列表,当我们第一次添加元素,才将数组初始化为10。

2、HashMap加入了红黑树数据结构

接口相关

1、接口中可以书写普通方法:使用default关键字修饰,加在返回值类型之前,访问修饰符之后。

2、接口中可以书写静态方法
package com.atguigu.test1;

public interface A {
public default void m1(){

}

public static void m2(){

}

}

函数式接口

函数式接口:即一个接口中只有一个抽象方法,这样的接口称之为SAM接口(Single Abstract Method );

这样的接口可以使用注解@FunctionalInterface修饰则称之为函数式接口;

函数式编程属于一种编程思想,就像面向过程,面向对象,等等都属于编程思想;

函数式编程的代表语言是Haskell,更强调函数(方法)可以实现什么操作,执行了什么功能,不注重是哪个角色调用了这个函数(方法),侧重于面向过程,不是面向对象(Java早期设计);

使用函数式编程 即表示前提必须为函数式接口;

只要一个接口中只有一个抽象方法,即可称之为函数式接口

package com.interfacePart.Test01;

public interface Usb {
// 接口内 默认为 全局静态变量 即 public static final 修饰
int A = 100;
// 接口中的方法默认都为全局抽象方法 即不管是否书写均使用public abstract修饰
void connect();
}

lambda表达式

lambda表达式的使用

package com.atguigu.test1;

/**
* 回顾匿名内部类 :即我们可以”直接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");

// 有两个参数 有返回值 只有一条语句,不用写 return
F f1 = (a,b)-> a + b;
System.out.println(f1.m1(10, 20));

// 有一个参数 有返回值 有多条语句,需要写一个大括号,有返回值,需要写return。
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表达式的形式,90%的场景是直接在方法体中直接调用一个方法,而不是在方法体中写逻辑,调用方法的技术也叫作方法引用。

原理:找一个方法,符合接口中声明的变量类型,返回值类型,作为函数式接口中的抽象方法的方法体,叫作方法引用。

在lambda表达式的基础上,使用其他方法的方法体,作为lambda表达式抽象方法的方法体;

规则:
被引用的方法体,原本的方法返回值,形参列表,必须和函数式接口中的抽象方法的返回值,形参列表保持一致。

固定格式:

构造方法引用 类名 :: new;
静态方法引用 类名 :: 方法名;
实例方法引用 对象名 :: 方法名;

✨ 方法引用的示例:

1、构造方法引用:

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();
}
interface B{
void m1(String str);
}
interface E<P>{
void m1(P p);
}
interface D<R,P>{
R m1(P p);
}
interface C{
void m1(String str,int num);
}
public static void main(String[] args) {
// lambda表达式 通过接口和直接写方法体
A a1 = ()-> System.out.println("");
a1.m1();

// 接口匹配构造方法的无参构造方法体
A a2 = Student :: new;
a2.m1();

// 接口匹配构造方法的有参构造方法体
B b1 = Student :: new;
b1.m1("赵四");

// 接口匹配构造方法的有参构造方法体
C c1 = Student :: new;
c1.m1("a", 20);
}

2、静态方法引用:函数式接口匹配类的静态方法的方法体

// 思考:哪个方法首先为静态方法
// 并且参数为布尔类型 返回值为String类型的,这样的方法体,在String类中存在
// 例如使用String类中的 valueOf方法
D<String,Boolean> d1 = String :: valueOf;
// 调用接口中的方法执行 valueOf方法体代码
int length = d1.m1(true).length();
System.out.println("length = " + length);

// 使用 Math类中 的 abs 静态方法,作为方法体,匹配D接口
D<Double,Double> d2 = Math :: abs;
System.out.println(d2.m1(20.0));
// 使用 Math类中 的 abs 静态方法,作为方法体,匹配D接口
D<Integer,Float> d3 = Math :: round;
System.out.println(d3.m1(3.5F));

3、实例方法引用:函数式接口匹配类的普通方法的方法体

// 定义一个 String 类型对象
String str = "abc";
// 使用 String类型对象的startsWith方法的方法体,匹配D接口
D<Boolean,String> d4 = str :: startsWith;
// 调用方法
System.out.println(d4.m1("def"));

D<Boolean,String> d5 = str :: endsWith;
System.out.println(d5.m1("c"));

JDK函数式接口

JDK中默认提供了常用的4类函数式接口位于 java.util.function,可供日常使用。(不用再写接口了,使用JDK自己提供的)

  • 消费型接口:
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"));
}
}

Stream流式编程

Stream 流式编程 是 Java 8 引入的一种 数据处理方式,它可以让你用类似“管道”的方式去操作集合、数组等数据源,从而写出更简洁、可读性更高的代码。用于处理集合,数组等数据。

它的核心理念是:

关注“做什么”,而不是“怎么做”。 你只描述数据的转换、过滤、汇总规则,底层会自动帮你完成迭代、遍历等操作。

基本理解

在 Java 里,Stream 是一个 元素序列,它并不存储数据,而是从数据源(如 Collection、数组、文件、生成器等)中读取数据,然后通过一系列操作(中间操作和终止操作)来处理这些数据。

流特点

  • ①Stream 自己不会存储元素。

  • ②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。

  • ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

处理流程

  1. 创建流(从数据源生成 Stream)
  2. 中间操作(对数据进行加工,如 filtermapsorted
  3. 终止操作(触发执行,如 collectforEachcount

中间操作是惰性执行的,只有在终止操作时才会真正遍历数据。

Stream接口

流接口

构造方法

无,使用静态方法创建
static interface Stream.Builder<T>

方法

generate()

作用:创建无限流

参数:

返回值:

示例:

中间操作方法

作用:中间操作

参数:

返回值:

示例:

终止操作方法

作用:终止操作

参数:

返回值:

示例:

创建流

创建流方式1:使用Collect类中的Stream()方法,创建流对象。

default Stream<E> stream()

作用:返回以此集合作为源的顺序 Stream

参数:无

返回值:Stream流对象,这个流对象可以读取到集合中的所有元素

特点:这个流对象,本身是不排序的,集合中的顺序是什么,在流对象中还是什么。

示例:
List<Student> list = new ArrayList<Student>();
Stream<Student> stream = list.stream();

创建流方式2:使用Arrays类中stream()方法。

static <T> Stream <T> stream(T[] array)

作用:返回顺序T与指定的数组作为源(静态方法)

参数:T类型的数组

返回值:Stream流对象

特点:这个流对象,本身是不排序的,集合中的顺序是什么,在流对象中还是什么。

示例:
IntStream stream2 = Arrays.stream(new int[]{1,2,3,4,5})

创建流方式3:使用Stream类中静态方法of(),通过显示值创建一个流,可以接收任意数量的参数。

static <T> Stream <T> of(T... values) 

作用:返回其元素是指定值的顺序排序流(静态方法)

参数:T类型的值

返回值:Stream流对象

示例:
// 方式3
Stream<String> stream2 = Stream.of("v", "t", "b");
System.out.println("stream2 = " + stream2);

创建流方式4:创建一个无限流:可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流

public static<T> Stream<T> generate(Supplier<T> s)

作用:返回无限顺序无序流,其中每个元素由提供的 Supplier (静态方法)

参数:供给型函数式接口对象

返回值:Stream流对象

示例:
// 生成一个无限流,里边的内容是随机数,限制5个,每个都打印出来。
Stream.generate(Math :: random).limit(5).forEach(System.out :: println)

中间操作

方法作用
filter(Predicate)按条件过滤
map(Function)元素映射转换
flatMap(Function)扁平化映射
sorted() / sorted(Comparator)排序
distinct()去重
limit(n)取前 n 个
skip(n)跳过前 n 个

终止操作

方法作用
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch**(**Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional T indFirst()返回第一个元素
long count()返回流中元素总数
Optional T max()返回流中最大值
Optional T min()返回流中最小值
void forEach(Consumer c)迭代遍历
findAny()查找元素
collect(Collectors.toList())转集合

示例

import java.util.*;
import java.util.stream.*;

public class StreamDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "pear", "orange", "apple");

// 流式编程:筛选 + 转大写 + 去重 + 排序 + 收集
List<String> result = list.stream()
.filter(s -> s.length() > 4) // 过滤长度大于4的
.map(String::toUpperCase) // 转大写
.distinct() // 去重
.sorted() // 排序
.collect(Collectors.toList()); // 收集成 List

System.out.println(result);
}
}

中间操作使用示例:

package com.newJDK.TestLambda.Stream;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class TestStream {
// 创建一个数组集合
static List<Student> list = new ArrayList<Student>();
static {
// 向集合中添加元素
Student stu1 = new Student("hanser",18,'女');
Student stu2 = new Student("yousa",19,'女');
Student stu3 = new Student("cat",20,'女');
Student stu4 = new Student("Akie",20,'女');
Student stu5 = new Student("senere",20,'女');
Student stu6 = new Student("senere",20,'女');
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
list.add(stu5);
list.add(stu6);
}

// filter使用
@Test
public void m1(){
// 创建 流对象
Stream<Student> stream = list.stream();
// filter的使用
stream.filter((stu)->stu.getName().equals("hanser")).forEach(System.out::println);
}

// map的使用:对每个元素进行处理
@Test
public void m2(){
// 创建 流对象
Stream<String> stream = Stream.of("abc", "edf");
// filter的使用
stream.map((str)->str.toUpperCase()).forEach(System.out::println);
}
// 去重
@Test
public void m3(){
// 创建 流对象
Stream<Student> stream = list.stream();
// 需要在 Student类中重写 equals方法和hashCode方法
stream.distinct().forEach(System.out::println);
}
@Test
// 限制条数
public void m4(){
// 创建 流对象
Stream<Student> stream = list.stream();
stream.limit(3).forEach(System.out::println);
}
// 跳过
@Test
public void m5(){
// 创建 流对象
Stream<Student> stream = list.stream();
stream.skip(1).forEach(System.out::println);
}

// 扁平化映射:
@Test
public void m6(){
// 创建 流对象
Stream<String> stream = Stream.of("Hanser", "is", "MyAngle");
// 遍历到流对象中的每个元素
stream.flatMap(TestStream::buildstrToStream).forEach(System.out::println);

}
// 定义一个方法处理逻辑:传入一个string字符串,返回一个流对象
public static Stream<Character> buildstrToStream(String str){
// 字符串转为字符数组
char[] chars = str.toCharArray();
// 新建一个集合
List<Character> list = new ArrayList<>();
// 把字符数组的每一个字符添加到集合中
for (char aChar : chars) {
list.add(aChar);
}
// 返回一个流对象
return list.stream();
}

// 排序
@Test
public void m7(){
// 创建 流对象
Stream<Student> stream = list.stream();
// Student类中实现了Comparable接口
stream.sorted().forEach(System.out::println);
}

// 排序
@Test
public void m7(){
// 创建 流对象
Stream<Student> stream = list.stream();
// Student类中实现了Comparable接口
stream.sorted(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
}).forEach(System.out::println);
}

}
package com.newJDK.TestLambda.Stream;

import java.util.Objects;

public class Student implements Comparable<Student>{
private String name;
private int age;
private char sex;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public char getSex() {
return sex;
}

public void setSex(char sex) {
this.sex = sex;
}

public Student(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

public Student() {

}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}

// 重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && sex == student.sex && Objects.equals(name, student.name);
}
// 重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}

// 重写比较方法
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}

终止操作使用示例:

// 判断是否全部匹配条件
@Test
public void m8(){
// 创建 流对象
Stream<Student> stream = list.stream();
boolean res = stream.filter(stu -> stu.getAge() > 20).allMatch(stu -> stu.getSex() == '女');
System.out.println("结果为"+res);
}

// 判断是否有一个匹配条件
@Test
public void m9(){
// 创建 流对象
Stream<Student> stream = list.stream();
boolean res = stream.filter(stu -> stu.getAge() > 20).anyMatch(stu -> stu.getSex() == '女');
System.out.println("结果为"+res);
}

// 返回流中元素数量
@Test
public void m10(){
// 创建 流对象
Stream<Student> stream = list.stream();
long count = stream.count();
System.out.println("结果为"+count);
}

// 返回第一个元素
@Test
public void m11(){
// 创建 流对象
Stream<Student> stream = list.stream();
Optional<Student> first = stream.findFirst();
System.out.println("结果为"+first);
}

// 返回流中最大的元素
@Test
public void m12(){
// 创建 流对象
Stream<Student> stream = list.stream();
Optional<Student> maxOne = stream.max(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println("结果为"+maxOne);
}

// 返回流中最小的元素
@Test
public void m12(){
// 创建 流对象
Stream<Student> stream = list.stream();
Optional<Student> maxOne = stream.min(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println("结果为"+maxOne);
}

Optional类

到目前为止,空指针异常是导致Java应用程序失败的最常见原因。

以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

以前写代码时每次都需要判断对象是否为空,现在使用Optional类对空指针的检查更佳简洁一点。

简化我们对空指针判断的代码。

方法

of(Object obj):静态方法

作用:根据传入对象获取一个Optional对象,此对象不能为null

参数:对象

返回值:Optional对象

示例:
public void m1(){
Optional<String> op1 = Optional.of("Hanser");
Optional<String> op2 = Optional.of(null);
System.out.println("Optional对象:" + op1); // Optional[Hanser]
System.out.println("Optional对象:" + op2); // 抛异常
}

empty():静态方法

作用:包装一个保存有null的Optional对象

参数:无

返回值:Optional对象

示例:
// Optional类中的empty方法
@Test
public void m2(){
Optional<String> op1 = Optional.empty();
System.out.println("Optional对象:" + op1); // Optional.empty
}

ofNullable(Object obj):静态方法

作用:根据传入对象获取一个Optional对象,此对象可以为null

参数:对象或null

返回值:Optional对象

示例:

// Optional类中的ofNullable(Object obj)
@Test
public void m3(){
Optional<String> op1 = Optional.ofNullable("123");
Optional<String> op2 = Optional.ofNullable(null);
System.out.println("Optional对象:" + op1);
System.out.println("Optional对象:" + op2);
}

get():Optional实例方法

作用:获取Optional中保存的对象,如果为null,则报空指针异常

参数:空

返回值:对象

示例:
@Test
public void m4(){
Optional<String> op1 = Optional.ofNullable("123");
String value = op1.get();
System.out.println("value = " + value);
}

isPresent():Optional实例方法

作用:表示判断Optional是否为null,为null结果为false,不为null结果为true

参数:空

返回值:布尔值

示例:
// isPresent
@Test
public void m5(){
Optional<String> op1 = Optional.ofNullable(null);
boolean res = op1.isPresent();
System.out.println("value = " + res); // false
}

ifPresent(Consumer ? super T consumer):Optional实例方法

作用:如果Optional对象中的对象不为null,则消费此对象,否则不消费

参数:消费型接口对象

返回值:无

示例:
// isPresent
@Test
public void m5(){
Optional<String> op1 = Optional.ofNullable(null);
// 消费型接口,有参,返回值无
op1.ifPresent(System.out::println);
}

orElse(T other):Optional实例方法

作用:如果当前Optional对象中保存对象为null,则使用传入对象

参数:对象

返回值:无

示例:
// isPresent
@Test
public void m6(){
Optional<String> op1 = Optional.ofNullable(null);
String value = op1.orElse("Akie");
System.out.println("value = " + value);
}

orElseGet(Supplier ? extends T other):Optional实例方法

作用:如果当前Optional对象中为null,则获取传入的另外一个对象,否则不获取

参数:供给型对象

返回值:无

示例:

// isPresent
@Test
public void m7(){
Optional<String> op1 = Optional.ofNullable(null);
String str = "Warma Kawaii";
// Supplier 不接受任何参数,只会返回一个值
String value = op1.orElseGet(str :: toString);
// String value1 = op1.orElseGet(str -> str.toString()); // 错误的
String value1 = op1.orElseGet(() -> str.toString()); // 正确的
System.out.println("value = " + value);
System.out.println("value = " + value1);
}

orElseThrow(Supplier ? extends X exceptionSupplier):实例方法

作用:如果当前Optional对象中为null则抛出异常,否则不抛出

参数:供给型对象

返回值:Throwable异常类型

示例:

// isPresent
@Test
public void m8(){
Optional<String> op1 = Optional.ofNullable(null);
String res = op1.orElseThrow(() -> new RuntimeException("运行时异常"));
System.out.println("res = " + res);
}