文章目录
1. Lambda表达式
1.1 Lambda表达式初体验
案例需求
启动一个线程,在控制台输出一句话:多线程程序启动了
实现方式一
实现步骤
定义一个类MyRunnable实现Runnable接口,重写run()方法
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable的对象作为构造参数传递
启动线程
实现方式二
匿名内部类的方式改进
实现方式三
Lambda表达式的方式改进
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}
public class LambdaDemo {
public static void main(String[] args) {
//实现类的方式实现需求
// MyRunnable my = new MyRunnable();
// Thread t = new Thread(my);
// t.start();
//匿名内部类的方式改进
// new Thread(new Runnable() {
// @Override
// public void run() {
// System.out.println("多线程程序启动了");
// }
// }).start();
//Lambda表达式的方式改进
new Thread( () -> {
System.out.println("多线程程序启动了");
} ).start();
}
}
函数式编程思想概述
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”而我们要学习的Lambda表达式就是函数式思想的体现
1.2 Lambda表达式的基本格式
1.3 Lambda表达式练习
练习一:
定义一个接口(Eatable),里面定义一个抽象方法:void eat();
定义一个测试类(EatableDemo),在测试类中提供两个方法 一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
public interface Eatable {
void eat();
}
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
}
public class EatableDemo {
public static void main(String[] args) {
//在主方法中调用useEatable方法
Eatable e = new EatableImpl();
useEatable(e);
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
});
//Lambda表达式
useEatable(() -> {
System.out.println("一天一苹果,医生远离我");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
练习二:有参有返回值抽象方法的练习
定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
定义一个测试类(AddableDemo),在测试类中提供两个方法 一个方法是:useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
public interface Addable {
int add(int x,int y);
}
public class AddableDemo {
public static void main(String[] args) {
//在主方法中调用useAddable方法
useAddable((int x,int y) -> {
return x + y;
// return x - y;
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
其实,lambda表达式的代码块就是抽象方法的实现 形式。
1.4 Lambda表达式的省略规则
代码演示
public interface Addable {
int add(int x, int y);
}
public interface Flyable {
void fly(String s);
}
public class LambdaDemo {
public static void main(String[] args) {
// useAddable((int x,int y) -> {
// return x + y;
// });
//参数的类型可以省略
useAddable((x, y) -> {
return x + y;
});
//但是有多个参数的情况下,不能只省略一个
// useAddable((x,int y) -> {
// return x + y;
// });
// useFlyable((String s) -> {
// System.out.println(s);
// });
// useFlyable((s) -> {
// System.out.println(s);
// });
//如果参数有且仅有一个,那么小括号可以省略
// useFlyable(s -> {
// System.out.println(s);
// });
//如果代码块的语句只有一条,可以省略大括号和分号
useFlyable(s -> System.out.println(s));
//如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
useAddable((x, y) -> x + y);
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
1.5 Lambda表达式的注意事项
1.6 Lambda表达式和匿名内部类的区别
2. 接口组成更新
2.1 接口组成更新概述
常量
public static final
抽象方法
public abstract
默认方法(Java 8)
public default 返回值类型 方法名(参数列表) { }
静态方法(Java 8)
public static 返回值类型 方法名(参数列表) { }
私有方法(Java 9)
private 返回值类型 方法名(参数列表) { }
2.2 接口中默认方法应用
public interface MyInterface {
void show1();
void show2();
}
public class MyInterfaceImplOne implements MyInterface {
@Override
public void show1() {
System.out.println("One show1");
}
@Override
public void show2() {
System.out.println("One show2");
}
}
public class MyInterfaceDemo {
public static void main(String[] args) {
//按照多态的方式创建对象并使用
MyInterface my = new MyInterfaceImplOne();
my.show1();
my.show2();
}
}
如果我们在接口中新增抽象方法,就必须在实现类中重写抽象方法,否则编译无法通过,有没有什么办法解决呢?
在JDK8中,引入了默认方法,如果抽线方法被设置为默认方法,就不需要在实现类中进行实现。但是,也可以在实现类中重写抽象方法。
public interface MyInterface {
void show1();
void show2();
// void show3();
// public default void show3() {
// System.out.println("show3");
// }
default void show3() {
System.out.println("show3");
}
}
public class MyInterfaceDemo {
public static void main(String[] args) {
//按照多态的方式创建对象并使用
MyInterface my = new MyInterfaceImplOne();
my.show1();
my.show2();
my.show3();
}
}
2.3 接口中静态方法应用
public interface Inter {
void show();
default void method(){
System.out.println("Inter 中的默认方法执行了");
}
// public static void test(){
// System.out.println("Inter 中的静态方法执行了");
// }
static void test(){
System.out.println("Inter 中的静态方法执行了");
}
}
public interface Flyable {
public static void test() {
System.out.println("Flyable 中的静态方法执行了");
}
}
public class InterImpl implements Inter,Flyable {
@Override
public void show() {
System.out.println("show方法执行了");
}
}
public class InterDemo {
public static void main(String[] args) {
//按照多态的方式创建对象并使用
Inter i = new InterImpl();
i.show();
i.method();
// i.test();
Inter.test();
// InterImpl.test();
Flyable.test();
}
}
2.4 接口中私有方法应用
私有方法产生原因
Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实
现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性。
public interface Inter {
default void show1() {
System.out.println("show1开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
// show();
method();//默认方法可以调用私有的静态方法和非静态方法
System.out.println("show1结束执行");
}
default void show2() {
System.out.println("show2开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
// show();
method();
System.out.println("show2结束执行");
}
private void show() {
System.out.println("初级工程师");
System.out.println("中级工程师");
System.out.println("高级工程师");
}
static void method1() {
System.out.println("method1开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
// show();
method();//静态方法只能调用静态方法,而不能调用非静态方法
System.out.println("method1结束执行");
}
static void method2() {
System.out.println("method2开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
method();
System.out.println("method2结束执行");
}
private static void method() {
System.out.println("初级工程师");
System.out.println("中级工程师");
System.out.println("高级工程师");
}
}
public class InterImpl implements Inter {
}
public class InterDemo {
public static void main(String[] args) {
//按照多态的方式创建对象并使用
Inter i = new InterImpl();
i.show1();
System.out.println("--------");
i.show2();
System.out.println("--------");
Inter.method1();
System.out.println("--------");
Inter.method2();
}
}
3. 方法引用
3.1 方法引用初体验
方法引用的出现原因
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要
那我们又是如何使用已经存在的方案的呢?
这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
public interface Printable {
void printString(String s);
}
public class PrintableDemo {
public static void main(String[] args) {
//在主方法中调用usePrintable方法
// usePrintable((String s) -> {
// System.out.println(s);
// });
usePrintable(s -> System.out.println(s));
// System.out.println("爱生活爱Java");
//方法引用符:::
usePrintable(System.out::println);
//可推导的就是可省略的
}
private static void usePrintable(Printable p) {
p.printString("爱生活爱Java");
}
}
3.2 方法引用符
3.3 方法引用类对象练习
将字符串转成数值输出
public interface Converter {
int convert(String s);
}
public class ConverterDemo {
public static void main(String[] args) {
//在主方法中调用useConverter方法
// useConverter((String s) -> {
// return Integer.parseInt(s);
// });
useConverter(s -> Integer.parseInt(s));
//引用类方法
useConverter(Integer::parseInt);
//Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
}
private static void useConverter(Converter c) {
int number = c.convert("666");
System.out.println(number);
}
}
使用说明:
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
3.4 方法引用对象的实例方法
public class PrintString {
//把字符串参数变成大写的数据,然后在控制台输出
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
public interface Printer {
void printUpperCase(String s);
}
public class PrinterDemo {
public static void main(String[] args) {
//在主方法中调用usePrinter方法
// usePrinter((String s) -> {
String result = s.toUpperCase();
System.out.println(result);
// System.out.println(s.toUpperCase());
// });
usePrinter(s -> System.out.println(s.toUpperCase()));
//引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
//Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
}
private static void usePrinter(Printer p) {
p.printUpperCase("HelloWorld");
}
}
使用说明:
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
3.5 方法引用类的实例方法
public interface MyString {
String mySubString(String s,int x,int y);
}
public class MyStringDemo {
public static void main(String[] args) {
//在主方法中调用useMyString方法
// useMyString((String s,int x,int y) -> {
// return s.substring(x,y);
// });
useMyString((s,x,y) -> s.substring(x,y));
//引用类的实例方法
useMyString(String::substring);
//Lambda表达式被类的实例方法替代的时候
//第一个参数作为调用者
//后面的参数全部传递给该方法作为参数
}
private static void useMyString(MyString my) {
String s = my.mySubString("HelloWorld", 2, 5);
System.out.println(s);
}
}
Lambda表达式被类的实例方法替代的时候 第一个参数作为调用者 后面的参数全部传递给该方法作为参数
3.6 引用构造方法
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public interface StudentBuilder {
Student build(String name,int age);
}
public class StudentDemo {
public static void main(String[] args) {
//在主方法中调用useStudentBuilder方法
// useStudentBuilder((String name,int age) -> {
Student s = new Student(name,age);
return s;
// return new Student(name,age);
// });
useStudentBuilder((name,age) -> new Student(name,age));
//引用构造器
useStudentBuilder(Student::new);
//Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
}
private static void useStudentBuilder(StudentBuilder sb) {
Student s = sb.build("Adrian", 18);
System.out.println(s.getName() + "," + s.getAge());
}
}
Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数