[Java笔记]动态代理、lambda表达式、函数式接口、构造器引用
程序员文章站
2022-03-20 22:58:10
day29review获取Class实例的三种方式.classgetClass()Class.forName(String classPath)总结:创建类的方式new + 构造器可以考虑:Xxx,Xxxs,XxxFactory,XxxBuilder类中查看是否有静态方法反射动态代理复习:静态代理public class StaticProxyTest { public static void main(String[] args) { N...
day29
review
- 获取Class实例的三种方式
- .class
- getClass()
- Class.forName(String classPath)
- 总结:创建类的方式
- new + 构造器
- 可以考虑:Xxx,Xxxs,XxxFactory,XxxBuilder类中查看是否有静态方法
- 反射
动态代理
- 复习:静态代理
public class StaticProxyTest {
public static void main(String[] args) {
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
proxyClothFactory.produceCloth();
}
}
interface ClothFactory {
void produceCloth();
}
// 代理类
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做了一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做了一些收尾工作");
}
}
// 被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
- 动态代理
思考:
- 如何根据加载到内存中的被代理类,动态地创建一个代理类及对象?
- 当通过代理类的对象调用方法时,如何动态地调用被代理类中的同名方法?
package com.yanchengxu.java;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author : yanchengxu
* @create : 2020/11/26 23:59
*/
public class DynamicProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("麻辣烫");
}
}
interface Human {
String getBelief();
void eat(String food);
}
// 被代理类
class SuperMan implements Human {
@Override
public String getBelief() {
return "I believe I can fly~";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class ProxyFactory {
/**
* 返回一个代理类的对象
* @param obj 被代理类对象
* @return 代理类的对象
*/
public static Object getProxyInstance(Object obj) {
Class<?> cls = obj.getClass();
ClassLoader loader = cls.getClassLoader();
Class<?>[] interfaces = cls.getInterfaces();
MyInvocationHandler handler = new MyInvocationHandler();
// 绑定
handler.bind(obj);
// 动态生成代理类对象
Object proxyInstance = Proxy.newProxyInstance(loader, interfaces, handler);
return proxyInstance;
}
}
class MyInvocationHandler implements InvocationHandler {
/**
* 需要使用被代理类进行赋值
*/
private Object obj;
/**
* 绑定
* @param obj 被代理类对象
*/
public void bind(Object obj) {
this.obj = obj;
}
/**
*
* @param proxy 代理类的对象
* @param method 代理类对象调用的方法
* @param args 代理类对象调用的方法的参数
* @return 代理类对象调用的方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnVal = method.invoke(obj, args);
return returnVal;
}
}
Lambda表达式
- 格式:(形参列表) -> 方法体
-
->
:lambda操作符 - 左边:lambda形参列表,对应接口中抽象方法的形参列表
- 右边:lambda体,对应重写的抽象方法的方法体
- 格式
- 无参无返回值
- 有参数无返回值
- 参数类型都可以省略,类型推断
- 若只有一个参数,可以省略
()
- 需要两个及以上的参数,多条执行语句,并且可以有返回值
- 只有一条语句,若有
return
和{}
则可以省略
总结:
- 形参列表
- 形参列表的参数类型可以省略
- 如果只有一个参数,
()
也可以省略
- Lambda体:
- 多条语句应该用
{}
包裹 - 如果只有一条执行语句,则可以省略
{}
- 当仅有一条return语句时,省略
{}
是则必须省略return
- 多条语句应该用
- 本质
作为函数式接口的实例
函数式接口
-
如果接口中只有一个抽象方法,则称之为函数式接口
-
Java内置四大核心函数式接口
应用举例
public class FunctionalTest {
@Test
public void testConsumer() {
happyTime(999.99, money -> System.out.println("今天花了" + money + "元"));
}
public void happyTime(double money, Consumer<Double> con) {
con.accept(money);
}
@Test
public void testPredicate() {
List<String> list = Arrays.asList("北京", "天津", "上海", "重庆", "东京");
List<String> strings = filter(list, s -> s.contains("京"));
System.out.println(strings);
}
public List<String> filter(List<String> list, Predicate<String> pre) {
ArrayList<String> arrayList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
arrayList.add(s);
}
}
return arrayList;
}
}
- 其他接口
方法引用
-
适用情景:当要传给
Lambda
体的操作,已经有实现的方法了,此时可以适用方法引用 -
本质:方法引用本质上是
Lambda
表达式
由上文,
Lambda
表达式本质上是函数式接口的实例,故方法引用也是函数式接口的实例
-
使用格式:
类(对象) :: 方法名
-
使用要求:
要求接口中的抽象方法的形参列表和返回值与方法引用的方法的形参列表和返回值相同
- 分类:
- 对象::非静态方法
- 类::静态方法
- 类::非静态方法
- 举例
public class MethodRefTest {
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("Lambda");
System.out.println("--------------------------");
PrintStream ps = System.out;
Consumer<String> con2 = ps :: println;
con2.accept("MethodReference");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee pony = new Employee(1001, "Pony", 23, 9999);
Supplier<String> sup = () -> pony.getName();
System.out.println(sup.get());
System.out.println("-------------------------");
Supplier<String> sup2 = pony :: getName;
System.out.println(sup2.get());
}
// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com.compare(34, 43));
System.out.println("-------------");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(34, 42));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double, Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(13.4));
System.out.println("---------------------------");
Function<Double, Long> func2 = Math::round;
System.out.println(func2.apply(13.5));
}
// 情况三:类 :: 实例方法
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (t1, t2) -> t1.compareTo(t2);
System.out.println(com1.compare("abc", "abd"));
System.out.println("--------------------");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abd", "abc"));
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String, String> pre1 = (t1, t2) -> t1.equals(t2);
System.out.println(pre1.test("abc", "abd"));
System.out.println("--------------------------");
BiPredicate<String, String> pre2 = String::equals;
System.out.println(pre2.test("abc", "abc"));
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
Employee pony = new Employee(1001, "Pony", 23, 34935);
Function<Employee, String> func1 = e -> e.getName();
System.out.println(func1.apply(pony));
System.out.println("-----------------");
Function<Employee, String> func2 = Employee::getName;
System.out.println(func2.apply(pony));
}
}
构造器引用和数组引用
- 构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表与构造器的形参列表一致
抽象方法的返回值类型即为构造器所属的类
- 数组引用
将数组看作一个特殊的类,则使用方法与构造器引用一致
- 举例
public class ConstructorRefTest {
//构造器引用
//Supplier中的T get()
//Employee中的空参构造器Employee()
@Test
public void test1(){
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("----------------");
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
//Function中的R apply(T t)
@Test
public void test2(){
Function<Integer, Employee> func1 = id -> new Employee(id);
System.out.println(func1.apply(1001));
System.out.println("----------------");
Function<Integer, Employee> func2 = Employee::new;
System.out.println(func2.apply(1002));
}
//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, "Pony"));
}
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
Function<Integer, String[]> func1 = length -> new String[length];
System.out.println(Arrays.toString(func1.apply(5)));
System.out.println("---------------------");
Function<Integer, String[]> func2 = String[] :: new;
System.out.println(Arrays.toString(func2.apply(3)));
}
}
Stream API
- Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,Stream讲的是计算
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
- Stream使用步骤
- 创建Stream
常用方法
类 | 方法原型 | 说明 |
---|---|---|
Collection | default Stream stream() | 返回一个顺序流 |
Collection | default Stream parallelStream() | 返回一个并行流,这里并行的意思是返回的数据顺序可能与输入的不一致 |
Arrays | static Stream stream(T[] array) | 返回一个流 |
Stream | public static Stream of(T… values) | 静态方法,可以接收任意数量的参数 |
Stream | public static Stream iterate(final T seed, final UnaryOperator f) | 静态方法,创建无限流 |
Stream | public static Stream generate(Supplier s) | 静态方法,创建无限流 |
使用举例
@Test
public void testCreateStream() {
List<Employee> employees = EmployeeData.getEmployees();
// try1 通过集合创建
Stream<Employee> stream = employees.stream(); // 顺序流
Stream<Employee> parallelStream = employees.parallelStream(); // 并行流
// try2 通过数组创建
int arr[] = new int[]{1, 3, 4, 5};
IntStream stream1 = Arrays.stream(arr);
// try3 通过Stream.of
Stream<Integer> stream2 = Stream.of(1, 3, 4, 5);
// try4 创建无限流
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
System.out.println("----------------");
Stream.generate(Math::random).limit(5).forEach(System.out::println);
}
- 中间操作
常用方法
方法原型 | 说明 |
---|---|
Stream filter(Predicate<? super T> predicate) | 接收Lambda ,过滤元素 |
Stream limit(long maxSize) | 截断流,使其元素不超过给定数量maxSize
|
Stream skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流,与 limit(n) 互补 |
Stream distinct() | 通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
Stream map(Function<? super T, ? extends R> mapper) | 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
Stream sorted(Comparator<? super T> comparator) | 按comparator定制排序,没有参数时为自然排序 |
使用举例
@Test
public void testIntermediate1() {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// filter
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println("----------------------");
// limit
list.stream().limit(3).forEach(System.out::println);
System.out.println("----------------------");
// skip
list.stream().skip(3).forEach(System.out::println);
System.out.println("----------------------");
// distinct
list.add(list.get(0));
list.add(list.get(0));
list.add(list.get(0));
list.stream().forEach(System.out::println);
System.out.println("----------------------");
list.stream().distinct().forEach(System.out::println);
System.out.println("----------------------");
}
@Test
public void testIntermediate2() {
List<String> list = Arrays.asList("aab", "bbdsf", "ccsf");
// map
list.stream().map(String::length).filter(len -> len > 3)
.forEach(System.out::println);
System.out.println("----------------------");
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest::toStream);
streamStream.forEach(s -> s.forEach(System.out::println));
System.out.println("----------------------");
// flatMap
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest::toStream);
characterStream.forEach(System.out::println);
System.out.println("----------------------");
// sort
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1, e2) -> {
int compareAge = Integer.compare(e1.getAge(), e2.getAge());
if (compareAge == 0) {
return Double.compare(e1.getSalary(), e2.getSalary());
}
return compareAge;
}).forEach(System.out::println);
}
public static Stream<Character> toStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
彩蛋
@Test
public void testInterviewFromWWT() {
// 一行求1到100的和
System.out.println(IntStream.rangeClosed(1, 100).sum());
}
- 终止操作
常用方法
方法原型 | 说明 |
---|---|
boolean allMatch(Predicate<? super T> predicate) | 检查是否匹配所有元素 |
boolean anyMatch(Predicate<? super T> predicate) | 检查是否至少匹配一个元素 |
boolean noneMatch(Predicate<? super T> predicate) | 检查是否没有匹配的元素 |
Optional findFirst() | 返回第一个元素 |
Optional findAny() | 返回当前流中的任意元素 |
long count() | 返回流中元素的总个数 |
Optional max(Comparator<? super T> comparator) | 返回流中最大值 |
Optional min(Comparator<? super T> comparator) | 返回流中最小值 |
void forEach(Consumer<? super T> action) |
内部迭代;使用Collection 接口需要用户去做迭代,称为外部迭代 |
T reduce(T identity, BinaryOperator accumulator) | 可以将流中元素反复结合起来,得到一个值。返回 T,identity为初始值 |
Optional reduce(BinaryOperator accumulator) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
<R, A> R collect(Collector<? super T, A, R> collector) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
应用举例
public class StreamAPITest2 {
//1-匹配与查找
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// allMatch(Predicate p)——检查是否匹配所有元素。
// 练习:是否所有的员工的年龄都大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
// anyMatch(Predicate p)——检查是否至少匹配一个元素。
// 练习:是否存在员工的工资大于 10000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(anyMatch);
// noneMatch(Predicate p)——检查是否没有匹配的元素。
// 练习:是否存在员工姓“雷”
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch);
// findFirst——返回第一个元素
Optional<Employee> employee = employees.stream().findFirst();
System.out.println(employee);
// findAny——返回当前流中的任意元素
Optional<Employee> employee1 = employees.parallelStream().findAny();
System.out.println(employee1);
}
@Test
public void test2(){
List<Employee> employees = EmployeeData.getEmployees();
// count——返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
System.out.println(count);
// max(Comparator c)——返回流中最大值
// 练习:返回最高的工资:
Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Optional<Double> maxSalary = salaryStream.max(Double::compare);
System.out.println(maxSalary);
// min(Comparator c)——返回流中最小值
// 练习:返回最低工资的员工
Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(employee);
System.out.println();
// forEach(Consumer c)——内部迭代
employees.stream().forEach(System.out::println);
//使用集合的遍历操作
employees.forEach(System.out::println);
}
//2-归约
@Test
public void test3(){
// reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
// 练习1:计算1-10的自然数的和
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
// reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
// 练习2:计算公司所有员工工资的总和
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
// Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
System.out.println(sumMoney.get());
}
//3-收集
@Test
public void test4(){
// collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
// 练习1:查找工资大于6000的员工,结果返回为一个List或Set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
System.out.println();
Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
}
补充:Collections
Optional
-
Optional<T>
类(java.util.Optional
) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null
,表示这个值不存在。原来用null
表示一个值不存在,现在Optional
可以更好的表达这个概念。并且可以避免空指针异常。 -
常用方法
本文地址:https://blog.csdn.net/weixin_42580646/article/details/110254047