一. 匿名内部类
匿名内部类作用:简化代码
把子类继承父类/实现接口,重写父类/接口中的方法,创建子类对象合一步完成
匿名内部类的最终产物:
子类/实现类对象
格式:
在匿名内部类中,接口也是可以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] } }