文章目录


前言

      本篇文章是对之前学习java基础知识的再整理,通过看视频以及查阅博客进行汇总整理。部分图引用的是尚硅谷的课件(觉得比较好),若有侵权,请联系我删除。

      博客文章汇总:博客目录索引(持续更新)


一、Lambda表达式

1.1、认识与使用Lambda表达式

JDK8推出的一个匿名函数,使用​​Lambda​​表达式可以让代码变得更加简洁、灵活。

首先来看一个例子:普通方法重写、Lambda表达式、方法引用

@Test
public void test() {
//重写匿名接口类
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};

//Lambda表达式来表示匿名接口类
Runnable r2 = ()-> System.out.println("changluliner");

//方法引用
Comparator<Integer> com2 = Integer::compareTo;

}
  • 对于匿名接口类有三种方式表示。



从下面对比开始使用Lambda表达式


Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
//使用lambda表达式
Comparator<Integer> com1 =(o1,o2) -> Integer.compare(o1,o2);
  1. 无参数,无返回值:​​()->{System.out.println(132)}​​。
  2. 有参数,无返回值:​​(String o1) -> {System.out.println(132)}​​​ 或 省略掉​​()​
  3. 若Lambda体只有一条语句,可以省略掉​​{}​​​,若是有返回值的也可以省略掉​​return​​。

类型推断示例

@FunctionalInterface
public interface Comparator<T> {

int compare(T o1, T o2);
}
  • 对于该​​Comparator​​​接口写lambda表达式时()中可以不指定类型,编译器会自动根据你初始变量名中设置的泛型参数决定类型。例如:​​Compartor<Integer> com = (o1,o2) -> Integer.compare(o1,o2);​​​这里o1、o2默认为​​Integer​​类型。



1.2、函数式接口

介绍函数式接口

函数式接口:只包含一个抽象方法的接口,可以通过Lambda表达式来创建该接口的对象。

  • 可使用​​@FunctionalInterface​​注解来检查该接口是否为一个函数式接口,同时javadoc也会包含一条声明说明这个接口是一个函数式接口。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

Java学习笔记 JDK8新特性_函数式接口

  • 当在有多个抽象方法的接口上定义​​@FunctionalInterface​​就会出现编译时异常。

说明:以前用匿名实现类表示的都可以使用​​Lambda​​表达式来写,举例如下:

@Test
public void test() {
//Runnable接口
Runnable runnable = () -> System.out.println("runnable接口");

//Comparator<T>接口
Comparator<Integer> comparator = (o1,o2)->Integer.compare(o1,o2);
}




四大核心函数式接口

函数式接口

方法

​Consumer<T>​​:消费型接口

​void accept(T t);​

​Supplier<T> ​​:供给型接口

​T get();​​(返回类型为T的对象)

​Function<T,R>​​:函数型接口

​R apply(T t);​​(对类型为T的对象应用操作,并返回结果。结果是R类型的对象。)

​Predicate<T>​​:断定型接口

​boolean test(T t);​​(确定类型为T的对象是否满足某约束,并返回 boolean值。)

其他不常用接口

Java学习笔记 JDK8新特性_JDK8新特性_02


Consumer接口示例


public class AnnotationTest {
@Test
public void test() {
//普通写法,传入一个匿名接口实现类
useMoney(200, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println(aDouble);
}
});//200.0

//lambda表达式
useMoney(1000,o1-> System.out.println(o1));//1000.0
}

public void useMoney(double money, Consumer<Double> con){
con.accept(money);
}
}
  • 在方法中使用​​Consumer​​接口作为参数,处理单个值。



Predicate接口示例


public class AnnotationTest {
@Test
public void test() {
//过滤掉List中包含"长"的字符串
List<String> lists = filterString(Arrays.asList("长路", "林儿", "长宏"), o1 -> !o1.contains("长"));
System.out.println(lists);//[林儿]
}

//使用Predicate接口方法来过滤list集合中的元素
public List<String> filterString(List<String> list, Predicate<String> predicate){
ArrayList<String> lists = new ArrayList<>();
for (String s : list) {
if(predicate.test(s))
lists.add(s);
}
return lists;
}
}
  • ​Predicate​​接口中的方法对其参数值进行判断返回一个布尔值。



1.3、方法引用与构造器引用


方法引用


何时使用:当你要对一个抽象方法进行Lambda体的操作时,若是有其他已实现的​​方法参数列表及返回值与其保持一致​​时,那么就可以使用方法引用,在执行抽象方法时会到引用的方法中依次执行

三种使用方式:通过::隔开

  • ​对象::实例方法名​
  • ​类::静态方法名​
  • ​类::实例方法名​

例1:​​Consumer​​接口示例

//示例
@Test
public void test() {
//Consumer<Integer> consumer = o -> System.out.println(o);
Consumer<Integer> consumer = System.out::println;
consumer.accept(123);//123
}

//看一下Consumer接口的方法
void accept(T t);

//再看一下System.out的println()方法
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
  • 可以看到​​Consumer​​​接口的方法参数类型与返回值同​​println()​​​一致,将​​println()​​​方法体放置到​​accept()​​中。

例2:​​Comparator​​接口示例

@Test
public void test() {
//Comparator<Integer> comparator = (o1,o2) -> Integer.compare(o1,o2);
Comparator<Integer> comparator = Integer::compare;
System.out.println(comparator.compare(12, 23));//-1
}

//看一下Comparator的抽象方法
int compare(T o1, T o2);

//再看下Integer的compareTo()方法
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
  • 在执行​​comparator​​​实例的方法​​compare()​​​实际上会到​​Integer​​​的指定方法​​compareTo()​​中执行。



构造器引用


格式:​​类名::new​

使用说明:要求构造器的参数列表与接口抽象方法中的参数列表一致,且方法的返回值为构造器对应的对象。

示例

@Test
public void test() {
//Function<String,String> function = n -> new String(n);
Function<String,String> function = String::new;
System.out.println(function.apply("changlu"));//changlu
}

//说明:看下Function接口的方法,上面泛型设置T为String,R为String(即返回值为String)
R apply(T t);

//String::new实际如下:
String apply(String t){
return new String(t);
}

针对于数组引用示例

@Test
public void test() {
//Function<Integer,String[]> function = n -> new String[n];
Function<Integer,String[]> function = String[]::new;
System.out.println(function.apply(5).length);//5
}
  • 注意这里的第一个参数为​​Integer​​型,来表示开辟数组的个数。



二、Stream流

2.1、Stream的概述

​Stream​​​:指的是​​java.util.stream​​,将真正的函数式编程风格引入到java中,用于操作数据源(如集合、数组等)所产生的元素序列。

概述:Stream是Java8中处理集合的关键抽象概念,它可以指定你对集合进行的操作如复杂查找、过滤与映射数据等操作(类似于数据库筛选查询),也可以通过其进行并行操作。

​Stream​​​和​​Collection​​集合区别:​​Collection​​​是一种静态的内存数据结构,而​​Stream​​是有关面向计算操作。前者是面向内存,存储在内容`中;后者面向CPU,通过CPU来实现计算。

操作三步骤:①首先有数据源(如集合,数组)获取一个流。②进行中间方法操作。③终止操作。

  • 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果,之后不会再使用。

注意点

  1. Stream自己不会存储元素。
  2. Stream不会改变源对象,它会返回一个持有结果的新Stream流。
  3. Stream操作时延迟执行的(懒加载),只有等到需要结果的时候才执行。



2.2、获取Stream方式(四种)


方式一:通过Collection接口扩展的方法获取stream


​default Stream<E> stream()​​:返回一个包含集合数据的顺序流。

​default Stream<E> parallelStream() ​​:返回一个包含集合数据的并行流。




方式二:通过数组,Arrays类中获取


​public static <T> Stream<T> stream(T[] array)​​:返回一个流。

Java学习笔记 JDK8新特性_java_03




方式三:通过Stream类的静态方法of()


​static<T> Stream<T> of(T t)​​:可传入单个任意类型。

​static<T> Stream<T> of(T... values)​​:可以接收任意数量的参数。




方式四:使用Stream类的静态方法创建无限流:就是根据不同形式无限生成出来对应类型的流


​static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)​​:迭代形式。

  • ​seed​​:初始值。
  • ​f​​:迭代方式,例如t->t+2,每次+2。

​static<T> Stream<T> generate(Supplier<T> s)​​:生成形式。

  • 实现​​T get();​​方法,不断返回值。

举例

@Test
public void test() {
//创建迭代流:输出10个数据
Stream<Integer> iterate = Stream.iterate(0, t -> t + 2);
iterate.limit(10).forEach((o)-> System.out.print(o+" "));//0 2 4 6 8 10 12 14 16 18
//创建生成流:
Stream<Double> generate = Stream.generate(Math::random);
generate.limit(2).forEach(System.out::println);
//0.3946065759053158
//0.44714749442637125
}




2.3、Stream的中间操作

重要说明:由于​​Stream​​​的中间操作方法返回的都是​​Stream<T>​​,所以我们可以连接多个方法操作形成一个流水线。需要注意的是除非流水线上触发终止操作,否则中间的筛选等操作不会执行任何处理,只会在终止操作时一次性全部处理,称为"​​惰性求值​​"。


筛选与切片


​Stream<T> filter(Predicate<? super T> predicate)​​:从流中排除某些元素。

​Stream<T> distinct();​​:通过流中所生成元素的hashCode()和equals()方法取出重复元素。

​Stream<T> limit(long maxSize);​​:截断流,使流中元素不会超过maxsize个。

​Stream<T> skip(long n);​​:跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,就会返回空流(与limit方法互补)。




映射


​<R> Stream<R> map(Function<? super T, ? extends R> mapper);​​:返回由给定函数应用于此流的元素的结果组成的流。

​DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);​​:返回一个DoubleStream,其中包含给定函数应用于此流的元素的结果。

  • 其中的​​ToDoubleFunction​​接口方法会返回一个Double值

​IntStream mapToInt(ToIntFunction<? super T> mapper);​​:返回一个IntStream,参数中的接口返回的是一个int值。

​LongStream mapToLong(ToLongFunction<? super T> mapper);​​:与上面大致相同。




排序


​Stream<T> sorted()​​;:产生一个新流,会按照其中的元素自然排序排列。

​Stream<T> sorted(Comparator<? super T> comparator);​​​:产生一个新流,可以添加一个​​Comparator​​接口进行定制排序。




2.4、Stream的终止操作

说明:终止操作会从流的流水线上生成结果,其结果可以是任何不是流的值,例如​​List​​​、​​Integer​​​、​​void​​。进行终止操作时才开始执行中间操作,一旦进行了终止操作后,就不能再次使用了。


匹配与查找


​boolean allMatch(Predicate<? super T> predicate);​​:检查是否匹配所有元素。

​boolean anyMatch(Predicate<? super T> predicate);​​:检查是否至少匹配一个元素。

​boolean noneMatch(Predicate<? super T> predicate);​​:检查是否匹配所有元素。

​Optional<T> findFirst();​​:返回第一个元素。

​Optional<T> findAny();​​:返回当前流中的任意元素。

​long count();​​:返回流中元素总数。

​Optional<T> max(Comparator<? super T> comparator);​​:返回流中的最大值,不过需要实现定制排序。

​Optional<T> min(Comparator<? super T> comparator);​​:返回流中的最小值。




遍历迭代


​void forEach(Consumer<? super T> action);​​:内部迭代。

  • 我们之前对于Collection集合中的元素是使用iterator来进行手动获取其中的值,称为外部迭代
  • 使用该方法则时内部迭代,流中的每个元素都会放置在调用​​Comsumer​​​接口的抽象方法中作为参数,你可以对其参数进行操作。一般来说直接遍历如​​stream.forEach(System.out::println())​



归约


​T reduce(T identity, BinaryOperator<T> accumulator);​​:将流中的元素反复集合起来(以及identity),返回一个值T。

//创建迭代流:输出10个数据
Stream<Integer> iterate = Stream.iterate(0, t -> t + 2);
Integer reduce = iterate.limit(2).reduce(5, Integer::sum);//7

​Optional<T> reduce(BinaryOperator<T> accumulator);​​​:将流中的元素集合起来,这里返回的是​​Optional​​。

说明:map与reduce的连接通常称为​​map-reduce​​模式,Google用它来进行网络搜索而出名。




收集


​<R, A> R collect(Collector<? super T, A, R> collector);​​:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。

  • 参数中的接口可以使用​​Collectors​​​中的方法来返回方法参数​​Collector​​接口。
  • ​Collector​​接口中的方法实现决定对流执行收集的操作。(如收集到List、Set、Map容器中)

Java学习笔记 JDK8新特性_抽象方法_04

  • 该接口中包含了多个接口抽象方法。

我们再去看下​​Collectors​​​类中的主要方法来搭配使用​​collect()​​方法:

Java学习笔记 JDK8新特性_抽象方法_05

Java学习笔记 JDK8新特性_抽象方法_06

  • 引用尚硅谷课件中的图片。



三、Optional类

3.1、介绍Optional类

​Optional​​类:能够很好解决空指针的问题。

  1. 该类是一个容器类,可以保存类型T的值,代表该值存在;若是保存为null,代表值不存在。
  2. 以前使用null表示一个值不存在,现在使用Optional更好表达这个概念,避免空指针异常。
public final class Optional<T> {
...
}

Java学习笔记 JDK8新特性_java_07




3.2、Optional的方式介绍

​Optional​​类提供了许多有用的方法,其中就包括了检测控制,这样我们以后就不需要进行显式空值判断了。


创建Optional类对象方法


​static <T> Optional<T> ofNullable(T value)​​:value可以为null或非空,返回一个实例。

​static <T> Optional<T> of(T value)​​​:value必须是非空否则会报​​NullPointerException​​。

​static<T> Optional<T> empty() ​​:创建一个空的Optional实例。




判断Optional容器中是否包含对象(可检测对象是否为null)


​boolean isPresent()​​ : 判断是否包含对象,若是不包含对象返回false,有对象返回为true。

​void ifPresent(Consumer consumer) ​​:如果有值,就执行Consumer 接口的实现代码,并且该值会作为参数传给它。




获取Optional容器的对象


​T get()​​:若容器中对象为null抛异常,若是不为null,返回该值。

​T orElse(T other) ​​:如果有值则将其返回,若是容器中对象为null返回指定的other对象。

​T orElseGet(Supplier other)​​ :如果有值则将其返回,若是容器中对象为null返回由 Supplier接口实现提供的对象。

​T orElseThrow(Supplier exceptionSupplier) ​​:如果有值则将其返回,若是容器中对象为null抛出由Supplier接口实现提供的异常。




3.3、Optional类源码分析

​Optional​​类是如何来存储一个可以为null或不为null并进行判断的呢?

public final class Optional<T> {

//通过使用value来判断
private final T value;

//1.1 使用of()方法获取一个Optional实例
public static <T> Optional<T> of(T value) {
//调用了1.2 有参构造
return new Optional<>(value);
}

//1.2 有参构造
private Optional(T value) {
//调用Objects的方法来判断是否为null,一旦为null抛出异常,不为null返回实例给value接收
this.value = Objects.requireNonNull(value);
}

//1.3 使用ofNullable()方法获取实例
public static <T> Optional<T> ofNullable(T value) {
//若是为空,调用1.4 empty()方法,不为空则直接调用1.1的of()方法
return value == null ? empty() : of(value);
}

//1.4 该方法用于返回一个无参构造创建的Optional类,其中value值自然为null
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}

//get():若是容器中value为空抛出异常,不为空返回值value值
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
}

​Objects​​类:有参构造调用的方法

public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}

总结

  1. ​of(T value)​​​方法中实际上会调用​​Object.requireNonNull​​方法,该方法能够判断若是为空则抛空指针。
  2. ​ofNullable(T value)​​​方法为什么传入null不抛异常呢?是因为检测到value为null时,返回了一个空的​​Optional​​构造器,其中的value自然也就为null了。
  3. 其余的相关方法看了源码之后就能一目了然。




我是长路,感谢你的阅读,如有问题请指出,我会听取建议并进行修正。
欢迎关注我的公众号:长路Java,其中会包含软件安装等其他一些资料,包含一些视频教程以及学习路径分享。
学习讨论qq群:891507813 我们可以一起探讨学习
注明:转载可,需要附带上文章链接