java8的新特性(一)
java8 新特性 导航:1.关于接口的改进2.Lambda表达式3.函数式(Functional)接口4.方法引用与构造器引用java8 新特性速度更快代码更少(增加了新的语法: Lambda表达式)强大的Stream API便于并行最大化减少空指针异常:OptionalNashorn引擎,允许在JVM上运行JS应用1.关于接口的改进Java 8中,你可以为接口添加静态方法和默认方法。从技术角度
java8 新特性导航:
1.关于接口的改进
java8 新特性
- 速度更快
- 代码更少(增加了新的语法: Lambda表达式)
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常:Optional
- Nashorn引擎,允许在JVM上运行JS应用
1.关于接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。
从技术角度来说,这是完全合法的,
只是它看起来违反了接口作为一个抽象定义的理念。
静态方法: 使用static关键字修饰。可以通过接口直接调用静态方法,
并执行其方法体。我们经常在相互一起使用的类中使用静态方法。
你可以在标准库中找到像Collection/Collections
或者Path/Paths这样成对的接口和类。
默认方法: 默认方法使用default关键字修饰。
可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
比如:
java 8 API中对Collection\List\Comparator等接口提供了丰富的默认方法。
如何定义接口: 定义接口中的成员
JDK7及以前: 只能定义全局常量和抽象方法
全局常量: public static final的。 但是书写时,可以省略不写
抽象方法: public abstract的。
JDK8:
除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
CompareA
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海 ");
}//Public 可以省略
default void method3(){
System.out.println("CompareA:上海");
}
}
CompareB
public interface CompareB {
default void method3(){
System.out.println("CompareB:上海");
}
}
SuperClass
public class SuperClass {
public void method3(){
System.out.println("SuperClass:北京");
}
}
SubClassTest
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
// 1.接口中定义的静态方法,只能通过接口来调用 ==>利用这个可以变成工具类
CompareA.method1();// ==》CompareA:北京
// 2.通过实现类的对象, 可以调用接口中的默认方法
// 如果实现类重写了接口中的默认方法,调用时,依然调用的是重写的方法
s.method2();// ==》SubClass:上海
// 3.如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
// 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。--》类优先原则 仅仅 针对当前有效
s.method3();// ==》SuperClass:北京 ==> SubClass:深圳
// 4.如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法
// 那么在实现类没有重写此方法的情况下,报错. ==>接口冲突
// 如果你把extends SuperClass去掉 就会报错 因为不知道调哪个method3()
// 结果就是: 必须在实现类中重写此方法
}
}
class SubClass extends SuperClass implements CompareA,CompareB {
public void method2(){
System.out.println("SubClass:上海 ");
}
public void method3(){
System.out.println("SubClass:深圳");
}
// 5.如何在子类(实现类)的方法中调用父类\接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
}
2.Lambda表达式
Lambda表达式的使用
函数接口才能用Lambda ,即只有一个抽象方法
2.1 举例:(o1,o2)->Integer.compare(o1,o2)
2.2 格式:
-》:Lambda操作符 或 箭头操作符
-》:左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
-》:右边:Lambda体(其实就是重写的抽象方法的方法体)
2.3 Lambda表达式的使用:(分为6种情况介绍)
总结:
》左边: Lambda形参列表的参数类型可以省略(类型推断);
如果Lambda形参列表只有一个参数,其一对()也可以省略
》右边: Lambda 体应该使用一对{}包裹;
如果Lambda体只有一条执行语句(可能是return语句),
省略这一对{}和return关键字
2.4 Lambda表达式的本质: 作为函数式接口的实例
2.5 如果一个接口中,只声明了一个抽象方法,
则此接口就称为函数式接口,我们可以在一个接口上使用
@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。
2.6 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
举例:
public class LambdaTest1 {
//语法格式一: 无参,无返回值
@Test
public void test1(){
//提供实现一个Runnable接口的匿名实现类的对象
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("偶爱你们");
}
};
r1.run();
System.out.println("*****************************");
Runnable r2 = () -> {
System.out.println("沃真能");
};
r2.run();
}
/**
偶爱你们
*****************************
沃真能
*/
//语法格式二:Lambda需要一个参数,但是没有返回值。
@Test
public void test2(){
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("人和人的区别在哪?");
System.out.println("*******************");
Consumer<String> con1 = (String s) ->{
System.out.println(s);
};
con1.accept("有心和没心");
}
/**
人和人的区别在哪?
*******************
有心和没心
*/
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test3(){
Consumer<String> con1 = (String s) ->{
System.out.println(s);
};
con1.accept("有心和没心");
System.out.println("*******************");
Consumer<String> con2 =(s) ->{
System.out.println(s);
};
con2.accept("有心和没心");
}
/**
有心和没心
*******************
有心和没心
*/
@Test
public void test4(){
ArrayList<String> list = new ArrayList<>();//类型推断
int[] arr = new int[]{1,2,3};
int[] arr1 = {1,2,3};//类型推断
}
//语法格式四: Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test5(){
Consumer<String> con1 = (s) ->{
System.out.println(s);
};
con1.accept("有心和没心");
System.out.println("*******************");
Consumer<String> con2 = s ->{
System.out.println(s);
};
con2.accept("有心和没心");
}
/**
有心和没心
*******************
有心和没心
*/
//语法格式五: Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test6(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12, 21));
System.out.println("*******************");
Comparator<Integer> com2 = (o1,o2) ->{
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12, 6));
}
/**
12
21
-1
*******************
12
6
1
*/
//语法格式六:当Lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test7(){
Comparator<Integer> com1 = (o1,o2) ->{
return o1.compareTo(o2);
};
System.out.println(com1.compare(12, 6));
System.out.println("*******************");
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com2.compare(12, 21));
}
/**
1
*******************
-1
*/
@Test
public void test8(){
Consumer<String> con1 = s ->{
System.out.println(s);
};
con1.accept("有心和没心");
System.out.println("*******************");
Consumer<String> con2 = s -> System.out.println(s);
con2.accept("有心和没心");
}
}
/**
有心和没心
*******************
有心和没心
*/
3.函数式(Functional)接口
函数式接口就是一个有且仅有一个抽象方法,
但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
通过 lambda 表达式来创建该接口的对象
即 Lambda表达式就是作为函数式接口的实例
自定义函数式接口
@FunctionalInterface
有注解,自己写函数式接口可以校验是否是只有一个
@FunctionalInterface
public interface MyInterface {
void method1();
}
JDK 1.8 之前已有的函数式接口(不全):
java.lang.Runnable
java.util.Comparator
java.io.FileFilter
java.awt.event.ActionListener
javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口: java.util.function
在java.util.function包下定义了Java 8的丰富的函数式接口
Java内置四大核心函数式接口
1.Consumer 消费型接口
对类型为T的对象应用操作,包含方法: void accept(T t)
2.Supplier 供给型接口
返回类型为T的对象,包含方法: T get()
3.Function<T, R> 函数型接口
对类型为T的对象应用操作,并返回结果。结果是R类型的对象。
包含方法: R apply(Tt)
4.Predicate 断定型接口
确定类型为T的对象是否满足某约束,并返回boolean值。
包含方法: boolean test(T t)
还有其他函数式接口如:(不全)
BiFunction<T,U,R>
BiConsumer<T,U>
BiPredicate<T,U>
举例:(导包省略 )
public class LambdaTest2 {
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("学习太累了,我去吃个大餐,价格:"+aDouble);
}
});
System.out.println("*****************************");
happyTime(400,money -> System.out.println("学习太累了,我去吃个大餐,价格:"+ money));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
/**
学习太累了,我去吃个大餐,价格:500.0
*****************************
学习太累了,我去吃个大餐,价格:400.0
*/
@Test
public void test2(){
List<String> list = Arrays.asList("happy","hello","blue","orange");
List<String> filterStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("h");
}
});
System.out.println(filterStrs);
System.out.println("*****************************");
List<String> filterStrs1 = filterString(list,s -> s.contains("h"));
System.out.println(filterStrs1);
}
//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
}
/**
[happy, hello]
*****************************
[happy, hello]
*/
4.方法引用与构造器引用
4.1 方法引用的使用
4.1.1 使用情境:
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
4.1.2 方法引用,本质上就是Lambda表达式,
而Lambda表达式作为函数式接口的实例。
所以方法引用,也是函数式接口的实例。
4.1.3 使用格式: 类(或对象) ::方法名
4.1.4 具体分为如下的三种情况:
情况1 对象::非静态方法
情况2 类::静态方法
情况3 类::非静态方法
4.1.5 方法引用使用的要求:
要求接口中的抽象方法的形参列表
和返回值类型与方法引用的方法的形参列表和返回值类型相同!
(针对于情况1和情况2)
举例:(导包 省略)
/**
* 方法引用的使用
*/
public class MethodRefTest {
//情况一:对象::实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
// accept方法用println方法替换 有它来实现
@Test
public void test1(){
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");
System.out.println("*******************");
PrintStream ps = System.out;
//返回个打印流的对象 System.out对象并赋给PrintStream ps
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
/**
* 北京
* *******************
* beijing
*/
//Supplier中的T get()
//Employee中的String getName()
// 抽象方法由另一个方法实现了 参数列表和返回值类型完全一致
@Test
public void test2(){
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
/**
* Tom
* *******************
* Tom
*/
//情况二:类::静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3(){
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compareTo;
System.out.println(com2.compare(12, 3));
}
/**
* -1
* *******************
* 1
*/
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4(){
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println("*******************");
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("*******************");
Function<Double,Long> func2 = Math::round;
System.out.println(func1.apply(12.6));
}
/**
* *******************
* 12
* *******************
* 13
*/
//情况三:类::实例方法 难度
//Comparator中的int compare(T t1,T t2)
// String中的int t1. compareTo(t2)
@Test
public void test5(){
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc", "abd"));
System.out.println("*******************");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abd", "abm"));
}
/**
* -1
* *******************
* -9
*/
//BiPredicate中的boolean test(T t1,T t2); 两个参数是布尔型的
//String中的boolean t1.equals(t2) 这个不是两个参数 但也是布尔型 但上面的参数t1 作为下面的调用者
@Test
public void test6(){
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("abc", "abc"));
System.out.println("*******************");
BiPredicate<String,String> pre2 = String ::equals;
System.out.println(pre2.test("abc", "abd"));
}
/**
* true
* *******************
* false
*/
//Function中的R apply(T t) 返回值是R T就相当于employee 然后调用getName()
//Employee中的String getName();
@Test
public void test7(){
Employee emp = new Employee(1001,"Tom",23,5600);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(emp));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(emp));
}
/**
* Tom
* *******************
* Tom
*/
}
4.2 构造器引用
和方法引用类似,
函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型
4.3 数组引用
大家可以把数组看傲是一个特殊的类,则写法与构造器引用一致。
举例:(导包 省略)
/**
* 1.构造器引用
*
* 2.数组引用
*/
public class ConstructorRefTest {
// 构造器引用
// Supplier 中的T get() 抽象方法返回值是T 这个T就是你要造的下面这个类型的对象
// Employee的空参构造器:Employee()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("***********************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("***********************");
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
/**
* ***********************
* Employee()...
* Employee{id=0, name='null', age=0, salary=0.0}
* ***********************
* Employee()...
* Employee{id=0, name='null', age=0, salary=0.0}
*/
//Function中的R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("***********************");
Function<Integer,Employee> func2 = Employee::new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
/**
* Employee(int id)...
* Employee{id=1001, name='null', age=0, salary=0.0}
* ***********************
* Employee(int id)...
* Employee{id=1002, name='null', age=0, salary=0.0}
*/
//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001, "Tom"));
System.out.println("***********************");
BiFunction<Integer,String,Employee> func2 = Employee::new;
System.out.println(func2.apply(1002, "Tom"));
}
/**
* Employee{id=1001, name='Tom', age=0, salary=0.0}
* ***********************
* Employee{id=1002, name='Tom', age=0, salary=0.0}
*/
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
//前一个参数是定义数组的长度的
Function<Integer,String[]> func1 = length -> new String[length];
// 创建了参数为五的String数组
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("***********************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
/**
* [null, null, null, null, null]
* ***********************
* [null, null, null, null, null, null, null, null, null, null]
*/
}
更多推荐
所有评论(0)