JDK8新特性
一、Lambda表达式
1.组成:
- 以逗号隔开的参数列表(xx,xx,xx…)
- ->符号
- Lambda表达式方法体。(λ代码块)
2.lambda表达式的作用
1.删减了大量的无用代码,使得编程可以直奔核心;
2.让Java开始支持函数式编程。
2.1、lambda表达式删减了大量的无用代码,使得编程可以直奔核心。
现在,先用传统方式,实现一个入门级的多线程程序,如下。
//以多线程为例。我们要启动一个多线程需要
@Test
void contextLoads() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行");
}
}).start();
}
程序中,new Thread()的参数是一个Runnable 接口,本次用匿名内部类方式实现了该接口,并重写了接口中的run()方法。但仔细读这种代码,容易发现 里面出现了大量的无用代码
代码中方法必不可少的东西:
(1)run()方法的参数列表:()
(2)方法体:System.out.print(…)
其中很容易理解,对于一个方法(函数)而言,参数列表和方法体 是没法省略的。(有人可能问:为什么返回值类型可以省?因为lambda有自动类型推测机制,可以根据return值 自动推算出返回值类型)。
好,搞懂以上后,lambda表达式的语法就出来了:
lambda表达式由 参数列表 和方法体 两部分组成,并且二者之间使用“->”进行连接。lambda表达式的最大作用,就是对上述形式的“匿名内部类”进行简化。现在,将上述代码,使用lambda写成如下的等价程序。
/**
*Lambda表达式是用来简化代码的,所以对于方法中能省的全省掉,就是Lambda表达式
*但有些是不能绝对省的1.方法体不能省 2.方法列表不能省
*/
@Test
void contextLoads() {
new Thread( () ->{
System.out.println("线程执行");
}
}).start();
}
将原来那些冗余的代码去掉以后,我们的代码变得简洁了很多
2.2 、让Java开始支持函数式编程。
再仔细观察这句代码:
new Thread( () ->{
System.out.println("线程执行");
}
}).start();
在使用lambda时,我们可以将 一段“函数”传到一个方法中,本次是将“() ->System.out.println(“Hello World”)”这段函数 作为参数,传到了new Thread(…)的构造方法中了。大家回忆一下,在以前,方法的参数是不是只能是 变量或表达式?因此lambda的出现,就可以让函数传到了方法的参数中,这也称为函数式编程。
3.函数式接口
3.1、函数式接口的定义
函数式接口是指被@FunctionalInterface注解修饰,且有且只有一个抽象方法的接口。这是因为假如接口有多个抽象方法,使用lambda表达式表达的就不知道是哪个方法了。
@Test
void contextLoads() {
new Thread( () ->{
System.out.println("线程执行");
}
}).start();
}
//创建Thread对象时,需要传入一个Runnable对象,假如Runnable有多个抽象方法。lambda表达式就不知道重写哪个方法了
注意:lambda表达式重写的必须是函数式接口(或者只有一个抽象方法的抽象类)
函数式接口需要注意以下几点:
- 即使没有标注@FunctionalInterface ,但是只有一个抽象方法,也称之为函数式接口。
- 特殊情况:如果某个接口中有多个抽象方法,但是只有一个抽象方法是本接口新定义的,其他抽象方法和Object中已有的方法重复,那么该接口仍然是函数式接口。(这种情况比较极端,了解即可)
3.2、 jdk自带的函数式接口
存放位置:函数式接口大部分放在java.util.function包下
jdk自带了4大函数式接口:
//四大核心函数式接口
//有参,无返回值(消费型)
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
//无参,有返回值(供给型)
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
//有参,有返回值(函数型)
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
//断言式接口
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
4、Lambda表达式的用法
4.1、第一种用法:定义一个函数式接口,用Lambda重写抽象方法
基本语法:函数式接口 变量名= lambda表达式。
上示例
@Test
public void testLambda() {
//1. 测试使用断言型接口
Predicate<Integer> isGood = (sorce) -> {//la
return sorce > 80;
};
//上面这一步就相当于我们重写了Predicate接口的test(T t)方法
System.out.println("Predicate接口断言你成绩及格时间为"+isGood.test(100));
isGood.test(100);//调用重写后的test方法
//2. 测试使用函数型接口
Function<Integer,String> function=(a)->{
return "Function接口接收参数"+a+",并将它进行二次方计算返回"+a*a;
};
//使用lambda表达式重写Function接口的R apply(T t)方法
System.out.println(function.apply(100));//调用重写后的apply方法
//3. 测试使用消费型接口
Consumer<String> consumer=(a)-> {System.out.println("Consumer接口消费了:"+a);};
consumer.accept("苹果");
// 4. 测试使用生产型接口
Supplier<String> supplier=()->{
return "Supplier生产粮食";
};
System.out.println(supplier.get());
}
4.2、第二种用法:将Lambda表达式代表的函数式接口作为一个方法的参数存在
基本使用:下面的程序中我们用Lambda表达式( (word) -> word.toUpperCase(Locale.ROOT))代表Function接口作为参数传递给方法。
@Test
public void testLambda(){
String s = toUpper("hello,world", (word) -> word.toUpperCase(Locale.ROOT));
System.out.println(s);
}
public String toUpper(String word, Function<String,String> fun){
//此时fun的抽象方法apply,已经被lambda重写了,所以下面调用的是重写后的apply方法。
return fun.apply(word);
}
二、接口的默认方法和静态方法
1、接口的默认方法
在jdk8之前,接口只能写抽象方法,jdk8以后接口可以有自己默认的方法。该接口的实现类可以调用接口的默认方法。当然了实现类也可以重写接口的默认方法.
public interface MyInterface {
default void say(String word){
System.out.println("我说"+word);
}
}
public class MyInterfaceImpl implements MyInterface {
}
@Test
public void testInterfaceDefaultMethod(){
MyInterface myInterface=new MyInterfaceImpl();
myInterface.say("hello world");
}
2、接口的静态方法
接口定义的静态方法,可以直接通过接口名.方法直接调用。
public interface MyInterface {
static void staticMethod(String word){
System.out.println("我说"+word);
}
}
@Test
public void testInterfaceStaticMethod(){
MyInterface.staticMethod("hello boy");
}
}
三、方法引用
jdk8允许使用::来引用一个已经存在的方法。其语法如下:
类名::方法名
注意:只需要写方法名,不需要写括号。
方法的引用,有以下4种:
1):引用静态方法 类名::方法
2):引用某个对象实例的方法 实例名::方法
3):引用某个类型任意对象的实例方法 类型(接口名)::方法
4):引用构造方法 类名::new
四、重复注解
java5引入注解以后,注解就被广泛使用,但是java5引入的注解有个局限,就是在同一个地方同一个注解只能使用一次。java8打破了这个限制,引入重复注解的概念。可以在同一个地方多次使用同一个注解。
java8中使用@Repeatable定义重复注解。
/*@Repeatable 注解是用于声明其它类型注解的元注解,来表示这个声明的注解是 可重复的。
*@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解。
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
}