29.Java学习笔记第二十九节——动态代理与 Java8 新特性(尚硅谷视频整理)


一、反射的应用:动态代理

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优化:
在这里插入图片描述
输出“迪丽热巴”
在这里插入图片描述
输出“古力娜扎”
在这里插入图片描述
输出“苍老师”

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值