一. 匿名内部类
匿名内部类作用:简化代码
把子类继承父类/实现接口,重写父类/接口中的方法,创建子类对象合一步完成
匿名内部类的最终产物:
子类/实现类对象
格式:
在匿名内部类中,接口也是可以new出对象的
new 父类/接口(){
从写重复父类/接口中的方法
};
例如1:
/*
线程的父类是Thread,原来的写法:
1.先创建MyThread类继承自Thread
2.使用的时候再new MyThread,然后再调用start方法
*/
new MyThread().start();
--------------------------------------------------------------------
/*
使用匿名内部类的写法:
*/
new Thread(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"黑马");
}
}
}.start();
例如2:
/*
不使用用匿名内部类写法:
1.先创建RunnableImpl类,并实现Runnable接口
2.再new RunnableImpl
3.将任务交给Thread,再调用start()
*/
Runnable r = new RunnableImpl();
new Thread(r).start();
--------------------------------------------------------------------
/*
使用匿名内部类写法:
*/
new Thread(new Runnable(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println("朱上林"+i);
}
}
}).start();
二. Lambda表达式
Lambda表达式,用来简化匿名内部类的书写,不再有“不得不创建接口对象”的束缚,不再有“抽象方法重写”的负担,就是这么简单!
用法
定义方法:
定义方法invokeCook(Cook cook)中有接口参数,并且方法体中调用了该接口参数的抽象方法cook.makeFood()
调用方法:
invokeCook(()->{}); 此时Lambda表达式表示的就是cook.makeFood()抽象方法的实现(相当于回调函数,调用的时候实现抽象方法)
案例1:无参数的Lambda
//创建Cook接口,并定义抽象方法
public interface Cook {
//定义无参数无返回值的方法makeFood
public abstract void makeFood();
}
//测试类
public class Demo01Cook {
public static void main(String[] args) {
//使用匿名内部类写法
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃饭了");
}
});
//使用Lambda表达式,简化匿名内部类的书写
invokeCook(()->{ //Lambda表达式来表示实现接口中的抽象方法
System.out.println("吃饭了");
});
//优化Lambda,如果写在一行{} return ; 三个同时省略
invokeCook(()-> System.out.println("吃饭了"));
}
public static void invokeCook(Cook cook){
cook.makeFood();
}
}
案例2:有参数的Lambda
//对数组中的Person对象使用Arrays的sort方法通过年龄进行升序(前边-后边)排序,匿名内部类方式实现
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
//使用Lambda表达式,简化匿名内部类
Arrays.sort(arr,(Person o1, Person o2)->{
return o1.getAge()-o2.getAge();
});
//简化Lambda,省略{} return ;
Arrays.sort(arr,(Person o1, Person o2)->o1.getAge()-o2.getAge()
);
案例3
public interface Calculator {
//定义一个计算两个int整数和的方法并返回结果
public abstract int calc(int a,int b);
}
public class Demo01Calculator {
public static void main(String[] args) {
//调用invokeCalc方法,方法的参数是一个接口,使用匿名内部类
invokeCalc(10, 20, new Calculator() {
@Override
public int calc(int a, int b) {
return a+b;
}
});
//使用Lambda表达式简化匿名内部类的书写
invokeCalc(120,130,(int a,int b)->{
return a + b;
});
//优化省略Lambda
invokeCalc(120,130,(a,b)-> a + b);
}
public static void invokeCalc(int a,int b,Calculator c){
int sum = c.calc(a,b);
System.out.println(sum);
}
}
Lambda使用前提
1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
2.使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
三、函数式接口
概念
有且只有一个抽象方法的接口,称之为函数式接口
当然接口中可以包含其他的方法(默认,静态,私有)
@FunctionalInterface注解
作用:可以检测接口是否是一个函数式接口,类似@Override检查一个重写方法
是:编译成功
否:编译失败(接口中没有抽象方法,或者抽象方法的个数多余1个)
例:
定义函数式接口
@FunctionalInterface
public interface MyFunctionalInterface {
//定义一个抽象方法
public abstract void method();
}
使用函数式接口
public class Demo {
//定义一个方法,参数使用函数式接口MyFunctionalInterface
public static void show(MyFunctionalInterface myInter){
myInter.method();
}
public static void main(String[] args) {
//调用show方法,方法的参数是一个接口,所以可以传递接口的实现类对象
show(new MyFunctionalInterfaceImpl());
//调用show方法,方法的参数是一个接口,所以我们可以传递接口的匿名内部类
show(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("使用匿名内部类重写接口中的抽象方法");
}
});
//调用show方法,方法的参数是一个函数式接口,所以我们可以Lambda表达式
show(()->{
System.out.println("使用Lambda表达式重写接口中的抽象方法");
});
//简化Lambda表达式
show(()-> System.out.println("使用Lambda表达式重写接口中的抽象方法"));
}
}
四、函数式编程
Lambda的特点: 延迟加载,提升性能
Lambda的使用前提,必须存在函数式接口,函数式接口作为形参的方法,在被调用时,实参Lambda表达式就表示该函数式接口中的那个抽象方法的实现。
例1
发现以下代码存在的一些性能浪费的问题
调用showLog方法,传递的第二个参数是一个拼接后的字符串
先把字符串拼接好,然后在调用showLog方法,showLog方法中如果传递的日志等级不是1级
那么就不会是如此拼接后的字符串,所以感觉字符串就白拼接了,存在了浪费
public class Demo01Logger {
public static void showLog(int level, String message){
if(level==1){
System.out.println(message);
}
}
public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showLog(2,msg1+msg2+msg3);
}
}
使用Lambda延迟加载,提升性能:
1.将新能浪费的代码定义为一个函数式接口中的一个抽象方法
@FunctionalInterface
public interface MessageBuilder {
//定义一个拼接消息的抽象方法,返回被拼接的消息
public abstract String builderMessage();
}
2.方法中将接口作为参数,调用该方法时,lambda表达式会根据判断是否成立,决定是否执行,所以可以把Lambda表达式看作,函数式接口中的那个抽象方法的实现
public class Demo02Lambda {
public static void showLog(int level, MessageBuilder mb){
//对日志的等级进行判断,如果是1级,则调用MessageBuilder接口中的builderMessage方法
if(level==1){
System.out.println(mb.builderMessage());
}
}
public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showLog(1,()->{
return msg1+msg2+msg3;
});
}
}
例2:参数为函数式接口
public class Demo01Runnable {
//函数式接口作为形参的方法,再被调用时,实参lambda表达式就表示该函数式接口中的那个抽象方法的实现。
public static void startThread(Runnable run){
new Thread(run).start();
}
public static void main(String[] args) {
startThread(()->{
System.out.println("线程启动了");
});
}
}
例3:方法的返回值类型是函数式接口
public class Demo02Comparator {
//定义一个方法,方法的返回值类型使用函数式接口Comparator
public static Comparator<String> getComparator(){
//方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
/*return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//按照字符串的降序排序
return o2.length()-o1.length();
}
};*/
//使用Lambda表达式
return (o1, o2)->o2.length()-o1.length();
}
public static void main(String[] args) {
//创建一个字符串数组
String[] arr = {"aaa","b","cccccc","dddddddddddd"};
//调用Arrays中的sort方法,对字符串数组进行排序
Arrays.sort(arr,getComparator());
//输出排序后的数组
System.out.println(Arrays.toString(arr));
//[dddddddddddd, cccccc, aaa, b]
}
}