在此之前要先了解一下接口咋JDK1.8前后变化,接口不了解,源码都没法看。
1 基础-接口篇
1.1 JDK1.8之前
官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
简单理解:接口里面全部是由全局常量和公共的抽象方法所组成。(JDK1.8之前可以这样理解)
一个简单的接口例子
//Java类型名通常以大写字母开头
interface In1{
//全局常量
final int a = 10;
//抽象方法
void display();
}
有兴趣还可以去了解一下注解(注解本质还是一个接口),特别是注解接口。
1.2 JDK1.8之后
JDK1.8,接口中的方法可以有方法体(1.8之前没有的),但需要关键字static或者default来修饰,使用static来修饰的称之为静态方法,静态方法通过接口名来调用,使用default来修饰的称之为默认方法,默认方法通过实例对象来调用。
例子:
interface In1{
//全局常量
final int a = 10;
//抽象方法
void display();
//jdk1.8之后,可以有方法体,用static修饰
static void display(){
System.out.println("a="+a);
}
//jdk1.8之后,可以有方法体,用default修饰
default void display(){
System.out.println("2a="+2*a);
}
}
public class In1Impl implements In1{
@Override
public void display() {
// TODO 自动生成的方法存根
System.out.println("我是jdk1.8之前的");
}
}
public class Test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
In1Impl In1Impl1 = new In1Impl();
In1Impl1.display();//JDK1.8之前,通过实例对象来调用
In1Impl1.display2();//default通过实例对象来调用
In1.display1();//static通过接口名来调用
}
}
2 特性1-Lambda表达式
了解完接口的变化后,对lambda表达式也更容易去学习lambda表达式本质上是一段匿名内部类,使用lambda表达式可以对一个接口进行非常简洁的实现,而且接口定义的必须要实现的抽象方法只能有一个。这里就不拿匿名函数做比较了,拿接口做比较方便。
Lambda表达式在Java语言中引入了一个操作符**“->”**,该操作符被称为Lambda操作符或箭头操作符。它将Lambda分为两个部分:
- 左侧:指定了Lambda表达式需要的所有参数,用( )来描述参数列表,参数一个可以省略()。
- 右侧:制定了Lambda体,即Lambda表达式要执行的功能,用{ }来描述方法体,方法体只有一条语句可以省略。
具体的语法实例就不说了,给大家主动去回想,加强记忆,没接触过就给大家刘个举一反三的空间,加强学习效果。
2.1 通过接口实现
一个简单例子(不用lambda表达式,不用匿名函数)
public class Test2 {
public static void main(String[] args) {
Comparator.compare(10, 5);
}
}
//比较a和b的大小
interface Comparator{
static void compare(int a,int b) {
if(a-b>0) System.out.println("a>b");
else System.out.println("a<b");
}
}
对比观察,使用lambda表达式
public class Test2 {
public static void main(String[] args) {
Comparator2 comparator2 = (a, b) -> {
if(a-b>0) System.out.println("a>b");
else System.out.println("a<b");
};
comparator2.compare(10, 5);
}
}
interface Comparator2{
void compare(int a,int b);
}
2.2 lambda方法引用
普通方法的引用
public class Test2 {
public static void main(String[] args) {
//方法引用:快速的将一个Lambda表达式的实现指向一个已经实现的方法
Comparator2 comparator2 = (a, b) -> compare(a,b);
comparator2.compare(10, 5);
//和上面等同,用方法的隶属者::方法名
//静态方法中,隶属者是类,非静态,隶属者是一个对象实例
Comparator2 comparator2_2 = Test2::compare ;
comparator2_2.compare(10, 5);
}
//静态方法
private static void compare(int a,int b){
if(a-b>0) System.out.println("a>b");
else System.out.println("a<b");
}
}
interface Comparator2{
void compare(int a,int b);
}
构造方法的引用
//实体类
public class Person {
String name;
private int age;
public Person() {
System.out.println("无参构造方法已经执行");
}
public Person(String name, int age) {
this.name=name;
this.setAge(age);
System.out.println("有参构造方法已经执行");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class test4 {
public static void main(String[] args) {
GetPersion create = () -> new Person();
//修改成构造方法的引用
GetPersion create1 = Person::new;
Person a = create1.getPerson();
//修改成构造方法的引用
GetPersion2 create2 = Person::new;
Person b = create2.getPerson("Lq林", 10);
}
}
interface GetPersion{
Person getPerson();
}
interface GetPersion2{
Person getPerson(String name, int age);
}
2.3 lambda常用实例
排序:
public class Test5 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("a",10));
list.add(new Person("b",15));
list.add(new Person("c",13));
list.add(new Person("d",20));
list.add(new Person("e",11));
//排序,开发工具中选择查看sort声明可见sort方法,对应下文的int compare(T o1, T o2);
list.sort((o1, o2) -> o2.getAge() - o1.getAge());
System.out.println(list);
}
}
//补充介绍说明sort方法,该方法是ArrayList包下的方法
@Override
@SuppressWarnings("unchecked")
//
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
//补充说明Comparator
@FunctionalInterface//lambda的专门的函数式接口
public interface Comparator<T> {
...
int compare(T o1, T o2);
...
}
循环forEach
public class Test6 {
public static void main(String[] args) {
//集合遍历
ArrayList<Integer> list = new ArrayList<>();
//添加元素
Collections.addAll(list, 1,2,3,4,5,6,7,8,9,0);
//集合遍历有遍历下标,for循环,迭代器和forEach实现,这里用forEach实现
//将集合中的没一个元素都带入方法accept中。
list.forEach(System.out::println);
//遍历偶数
list.forEach(ele ->{
if (ele % 2 == 0) System.out.println(ele);
});
}
}
其他的基础实例有增加和删除,线程实例等等,在此省略,常用接口JDK1.8都内置好了。
2.4 lambda的函数式接口
函数式接口:有且仅有一个抽象方法的接口。这主要体现在lambda(其他新特性在源码中也有体现)中。直接给接口添加@FunctionalInterface 注解就OK了,编译器发现该接口多于一个抽象方法就会报错。
3 特性2-Stream
Stream:流,数据渠道,操作(如筛选、排序等)数据源(集合数组等)产生的元素序列。对stream的流水线式操作分为两类,中间操作和结束操作。中间操作形成一条流的流水线,不生成结果,不执行操作;结束操作执行流水线全部内容(包括中间操作的内容),并能生成结果。
3.1 创建Stream的几种方式
数组arrays,集合list,stream中的静态方法of()、重复iterate和空empty等。
其中创建Stream有顺序流和并行流;如果流中的数据量足够大,并行流可以加快处速度。
注:直接创建并行流,还可以通过parallel()
把顺序流转换成并行流。
public class Stream1 {
/**
* 1.创建Stream,有stream()顺序流和parallelStream()并行流,此处只用顺序流
* 2.中间操作
* 3.终止操作
* @param args
*/
public static void main(String[] args) {
// 1、数组,通过Array
String[] arr = new String[]{"ab", "cd", "ef"};
Stream<String> Stream1 = Arrays.stream(arr);
// 2、集合,通过list
List<String> list = Arrays.asList("ab", "cd", "ef");
Stream<String> Stream2 = list.stream();
// 3.1、值,通过stream中的静态方法of()
Stream<String> stream3 = Stream.of("ab", "cd", "ef");
// 3.2、迭代,通过stream中的静态方法iterate创建无限流,0是初始元素,打开iterate声明翻译可知
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
//对4进行中间操作和终止操作
stream4.limit(10).forEach(System.out::println);
}
}
3.2 stream的中间操作
筛选和切片、映射、排序、查找与匹配等
例如筛选与切片的:
方法 | 描述 |
---|---|
filter(Predicate p) | 过滤,接收Lambda表达式,从流中排除某些元素 |
distinct() | 大多人理解成:筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素(本人个人理解:关键词 DISTINCT 用于返回唯一不同的值) |
limit(long maxSize) | 截断流,使其元素不超过给定数量(本人理解:关键词limit用于限制数量maxSize) |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补 |
上文中,这条语句的中间操作就是limit。
//对4进行中间操作和终止操作
stream4.limit(10).forEach(System.out::println);
更多中间操作实例可参考
大佬冰河:【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!
3.3 终止stream的几种方式
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer、Double、String等等,甚至是 void 。
拿最简单的查找与匹配有关的
tream API中有关查找与匹配的方法如下表所示。
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反, Stream API 使用内部迭代) |
例子:还是拿回上文最熟悉的那个forEach内部迭代
//对4进行中间操作和终止操作
stream4.limit(10).forEach(System.out::println);
更多终止操作实例可参考
大佬冰河