文章目录
一、反射的应用:动态代理
1、代理设计模式的原理
(1)使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
(2)之前为大家讲解过代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。(比如你要买房创建一个买房的代理类,买化妆品创建一个买化妆品的类……静态代理就是在编译期就确定好要创建的代理类,所以会造成过多的代理。此时动态代理就是先创建一个代理,在编译期才确定到底代理的内容是什么,再创建对应类的对象。运行时创建类的对象要利用反射来做。)
2、动态代理含义
(1)动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
(2)动态代理使用场合:
- 调试
- 远程方法调用
3、动态代理优点
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
七、反射的应用:动态代理
1、静态代理举例
代理类与被代理类在编译期间就确定下来了。
interface ClothFactory
{
void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory
{
private ClothFactory factory;
public ProxyClothFactory(ClothFactory factory)
{
this.factory=factory;
}
public void produceCloth()
{
System.out.println("代理工厂做一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做一些后续收尾工作");
}
}
//被代理类
class NikeClothFactory implements ClothFactory
{
public void produceCloth()
{
System.out.println("耐克工厂生产一批运动服");
}
}
//测试
public class StaticProxyTest
{
public static void main(String[] args)
{
//创建被代理类对象
NikeClothFactory nike=new NikeClothFactory();
//创建代理类对象
ProxyClothFactory proxyClothFactory=new ProxyClothFactory(nike);
proxyClothFactory.produceCloth();
}
}
输出结果:
2、动态代理举例
interface Human
{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human
{
public String getBelief()
{
return "I belief i can fly!";
}
public void eat(String food)
{
System.out.println("我喜欢吃"+food);
}
}
//问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
//问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
class ProxyFactory//用来生产代理类
{
//调用此方法,返回一个代理类的对象,解决问题一
public static object getProxyInstance(Object obj)//obj:被代理类的对象
{
MyInvocationHandler handler=new MyInvocationHandler();
handler.bind(obj);
//创建一个代理类对象,与被代理类实现相同的类加载器,接口,handler用来调用与被代理类同名方法(解决问题二)
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler
{
private Object obj;//需要使用被代理类的对象进行赋值
public void bind(Object obj)
{
this.obj=obj;
}
//当通过代理类的对象调用方法a时,就会自动调用如下的方法。将被代理类要执行的方法a的功能就声明在invoke()中
//proxy是代理类的对象,method是代理类对象调用的方法(也是被代理类对象被调用的方法)
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable
{
//method即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
Object returnValue=method.invoke(obj,args);
//上述方法的返回值就作为当前类中的invoke()的返回值
return returnValue;
}
}
//测试
public class ProxyTest
{
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("四川麻辣烫");
}
}
输出结果:
3、动态代理与AOP(Aspect Orient Programming)
前面介绍的Proxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制:
改进后的说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代码块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法。
中间的方法不必写死,想调哪个调哪个。
class HumanUtil
{
public void method1()
{
System.out.println("==============通用方法一===============");
}
public void method2()
{
System.out.println("==============通用方法二===============");
}
//上面的代码
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable
{
HumanUtil util=new HumanUtil();
util.method1();//方法1确定
Object returnValue=method.invoke(obj,args);//方法不确定
util.method2();//方法2确定
return returnValue;
}
二、Java8新特性
1、新特性简介
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。
Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
- 速度更快
- 代码更少(增加了新的语法:Lambda 表达式)
- 强大的 Stream API
- 便于并行
- 最大化减少空指针异常:Optional
- Nashorn引擎,允许在JVM上运行JS应用
2、Lambda 表达式
1、Lambda 表达式简介
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
2、Lambda 表达式举例
①:
“ ”************ ”上是一般写法,下是Lambda 表达式。
输出结果:
②:
另外一种方法;
3、Lambda 表达式格式
->: Lambda操作符或箭头操作符。
操作符左边:其左边是Lambda形参列表(其实就是接口中的抽象方法的形参列表)。
操作符右边:右边是Lambda体(其实就是重写的抽象方法的方法体)。
4、Lambda 表达式的使用
总结:
->左边:Lambda形参列表的参数类型可以省略(类型推断):如果Lambda形参列表只有一个参数,其一对()也可以省略。
->右边:Lambda体应该使用一对{}包裹:如果Lambda体只有一条执行语句(可能是return语句),可以省略这一对{}和return关键字(必须一起省略)。
5、Lambda 表达式的本质
作为函数式接口的实例。
3、函数式(Functional)接口
1、函数式(Functional)接口简介
(1)只包含一个抽象方法的接口,称为函数式接口。
(2)你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
(3)我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
2、函数式(Functional)接口理解
3、函数式(Functional)接口举例
分别举例:
①
④:
常规写法
简便写法:将上述代码从上第三行到8行改写为:
4、方法引用与构造器引用
1、方法引用
(1) 方法引用简介
- 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
- 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式(Lambda表达式是函数式接口的一个实例),也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
- 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!(针对前两种情况)
- 格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
- 如下三种主要使用情况:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
(2) 方法引用举例
情况一:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
① 对象::实例方法名
例1:
" *********************** "上是lambda写法,下面是方法引用写法。
传递给Lambda体的操作:System.out.println(str)有实现的方法:println,所以使用方法引用,要创造一个对应的对象来调用。
例2:
② 类::静态方法名
例1:
例2:
③ 类::实例方法名
第一个参数作为方法的调用者。
例1:
例2:
最后一句写错了,是pre2
例3:
2、构造器引用(没听懂……)
(1)构造器引用简介
- 格式: ClassName::new
- 与函数式接口相结合,自动与函数式接口中方法兼容。
- 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
(2)构造器引用举例
例1:
例2:
例3:
3、数组引用(没听懂……(;′⌒`))
(1)数组引用简介
- 格式: type[] :: new
- 将数组看做一个类,数组引用与构造器引用相同。
(2)数组引用举例
例1:
5、强大的Stream API
1、什么是 Stream
Stream到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,Stream讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
2、强大的Stream API简介
- Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
- Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
- Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
3、为什么要使用Stream API
- 实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。
- Collection 集合 和 Stream的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
4、Stream 的操作三个步骤
- 1- 创建 Stream
一个数据源(如:集合、数组),获取一个流 - 2- 中间操作
一个中间操作链,对数据源的数据进行处理 - 3- 终止操作(终端操作) 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
5、强大的Stream API测试
(1)创建Stream方式
<1>方式一:通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
- default Stream stream() : 返回一个顺序流
- default Stream parallelStream() : 返回一个并行流
区别是:
顺序流对于上述Employee按照顺序取,并行流就随机取了。
<2>方式二:通过数组
Java8 中的 Arrays 类的静态方法 stream() 可以获取数组流:
- static < T > Stream< T > stream(T[] array): 返回一个流
<3>方式三:通过Stream的of()
可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
- public static< T > Stream< T > of(T… values) : 返回一个流
<4>方式四:创建无限流
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
- 迭代public static< T > Stream< T > iterate(final T seed, final UnaryOperator< T > f)
- 生成public static< T > Stream< T > generate(Supplier< T > s)
(2)Stream的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
<1>筛选与切片
<2>映射
<3>排序
(3)Stream的终止操作
- 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
- 流进行了终止操作后,不能再次使用。
<1>匹配与查找
<2>归约
输出结果:55
<3>收集
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
右边被挡住的代码部分:
6、Optional类
1、Optional类简介
- 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
- Optional< T > 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
- Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
2、Optional类的方法
- Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
- 创建Optional类对象的方法:
→ Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
→ Optional.empty() : 创建一个空的 Optional 实例。
→ Optional.ofNullable(T t):t可以为null。 - 判断Optional容器中是否包含对象:
→ boolean isPresent() : 判断是否包含对象
→ void ifPresent(Consumer<? super T> consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。 - 获取Optional容器的对象:
→ T get(): 如果调用对象包含值,返回该值,否则抛异常
→ T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
→ T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
→ T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
3、Optional类的测试
出现空指针异常的情况:
常规优化:
Optional优化:
输出“迪丽热巴”
输出“古力娜扎”
输出“苍老师”