java8新特性主要有:函数式接口、lambda表达式、Stream API、接口对于static方法和默认方法的支持。
函数式接口指的是只有一个抽象方法的接口,函数式接口用@FunctionalInterface注解修饰,java中常用的函数式接口有Runnable、Comparator等等。
lambda是从java8开始支持的新语法,lambda语法对java语言做了扩展。使用lambda表达式的前提是函数式接口的存在,在java8以前,当我们想要使用一个函数式接口时,需要定义它的一个具体实现类,在这个实现类中实现它的那个唯一的抽象方法;或者是使用匿名内部类的方式去实现。从java8开始,因为lambda表达式支持将函数作为一个参数传递到调用它的方法中,所以使用函数式接口时,我们不再需要去为它创建实现类。
lambda的语法
java8为lambda添加了一个新的操作符:->这个箭头符号,又叫做lambda操作符;lambda表达式的结构为:左半部分是函数的参数列表,右半部分是函数的具体实现,又叫lambda体,中间由lambda操作符将左右两边连接起来。此函数如果没有入参,则左边只是一个(),此函数如果只有一个入参,则小括号可以省略,此函数如果有多个入参,则这多个参数用()包起来,参数之间用英文逗号隔开,我们无需声明参数的类型,lambda可以从上下文中推断出参数的具体类型。对于lambda体:此函数的实现如果包含多行代码,则这多行代码用{}包起来,此函数的实现如果只包含一行代码,则{}可以省略,此函数如果只包含一行代码,且有返回值,则不止{}可以省略,return关键字也可以省略。
lambda表达式的作用域
1、在lambda表达式内部可以访问它所在方法的局部变量和参数,但是不可以修改这些局部变量和参数,虽然我们可以不必使用final关键字去修饰它们,但它们拥有final的隐式语义。
2、对于能否在lambda表达式内部访问它所在方法的所属类中的变量和方法,取决于lambda表达式所在的方法是否是静态的,如果不是静态的,则可以访问类中所有属性和方法,包括静态的和非静态的、公有的和私有的;如果是静态的,则只能访问类中的静态属性和静态方法。
3、对于默认方法的访问权限:在lambda表达式中无法访问默认方法。
lambda表达式的出现,简化了函数式接口的使用,减少了代码中类的创建,以一种更加简洁的方式实现了函数式编程。
首先,Stream是从java8开始支持的一个抽象概念,它是为了实现对于集合数据的操作而设计的,使用Stream,使我们可以实现 像是使用SQL去操作数据库那样 去操作集合数据,Stream提供了一组高效易用的api去对集合数据执行操作,包括:集合中元素的过滤、分组、排序、类似于sql的limit操作、对于集合中各个元素的数据结构的转换、求集合中元素的数量、求最大元素、最小元素、为集合数据去重等操作。使用Stream API不会去改变数据源,只会返回一个包含着操作结果的新的stream。Stream的使用包含三个步骤:创建Stream、中间操作、终止操作。
创建Stream
创建Stream的方式有:Collection接口的stream方法、Arrays工具类的静态方法stream、Stream类的静态方法of。
中间操作主要包括:
1、用于数据过滤的filter方法:此方法需要传入一个函数作为参数,来定义数据的过滤逻辑。
2、用于元素排序的sort方法:有两种排序方式:自然排序和定制排序,自然排序的sort不需要传任何参数,但是有一个使用前提是,集合中的元素必须有比较大小的能力,也即元素所属类型要实现了Comparable接口;定制排序的sort方法需要传入Comparator的compare函数作为参数,来定义元素比较大小的逻辑。
3、limit 和 skip方法,这俩方法都需要传入一个数值类型的参数,类似于sql中limit关键字,limit方法用于获取结果中的前n条记录,而skip方法是跳过n条记录,获取结果中的其他记录,这俩方法是互补的。
4、distinct方法:此方法用于对集合中的数据去重,但是它有一个使用前提是,集合中的元素都重写了hashCode 和 equals方法,只有重写了这俩方法,distinct才能知道如何去判断两个对象是否重复。
5、map方法,此方法支持传入一个函数作为参数,这个函数将被使用到集合中的每一个元素上,来实现元素的数据结构转换。
多个中间操作连接成一个操作链,这些操作并不会立马执行,它们只有遇到了某个终止操作才会全部执行。
终止操作主要包括:
1、collect:这是最常用的一个终止操作,用于收集最终结果,它支持传入一个函数作为参数,包括:Collectors.toList(); Collectors.toSet(); Collectors.toCollection();分别用于将结果收集到一个list、set、一个普通集合中,还可以传入Collectors.groupingBy(...); 用于将结果按照某个规则进行分组,并将分组之后的数据收集到一个map结构中返回。
2、count方法:此方法用于计算得到的结果数据的总条数,类似于sql中的count函数。
3、max方法和min方法:这两方法分别用于返回集合中的最大元素和最小元素,使用这俩方法也有个前提是 集合中的元素要有比较大小的能力。
Stream API的存在简化了对于集合数据的复杂操作的过程,使我们对于集合的操作更加简洁高效。
在java8之前,接口中只能定义抽象方法,不能定义static方法,从java8开始接口开始支持静态方法的定义和实现,接口中定义的static方法的调用方式为:接口名.静态方法名。
默认方法也是从java8开始支持的,在java8之前,当我们想要为某个已经存在的接口去添加功能,只能在这个接口中添加抽象方法,然后要在它的每个实现类中去实现这个抽象方法,否则会编译不通过,接口与实现类的耦合度太高了,因此在java8中引入了默认方法,默认方法指的是用default关键字修饰的非抽象方法,在接口中要写这个默认方法的方法体,因为它不是一个抽象方法,所以它可以直接被实现类继承而无须在实现类中覆盖它,只不过在default方法的继承上有一个需要我们注意的问题:
当某个实现类既实现了某个接口又继承了某个类,而在它实现的接口和父类中定义了一个具有相同方法签名的default方法时,这个实现类继承的是父类的default方法。
当某个父接口和它的一个子接口都定义了一个具有相同方法签名的default方法,而某个实现类实现了这个子接口时,这个实现类继承的是子接口的default方法,这个是根据就近原则。
当某个实现类实现了多个接口,而这多个接口中都定义了一个具有相同方法签名的default方法时,实现类中需要覆盖这个default方法,否则会编译失败,当然我们可以在这个实现类的覆盖逻辑中,通过使用 接口名.super.default方法名(); 来引入某个接口的default方法的实现逻辑 来为此实现类所使用。