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 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
处理流程
- 创建流(从数据源生成 Stream)
- 中间操作(对数据进行加工,如
filter
、map
、sorted
) - 终止操作(触发执行,如
collect
、forEach
、count
)
中间操作是惰性执行的,只有在终止操作时才会真正遍历数据。
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);
}