Java认识设计模式
https://www.runoob.com/design-pattern/design-pattern-tutorial.html
一、什么是设计模式?
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结
二、设计模式的作用是什么?
使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
三、什么是单例模式/单态模式?
单例模式---保证一个类仅有一个实例。
当类被频繁地创建与销毁的时候,我们使用单例模式,这样可以减少了内存的开销,避免对资源的多重占用。
四、单例模式的两种表示方式以及区别
1、懒汉式
package com.wangxing.danli;
/**
* 懒汉式
* 1.构造方法私有
* 2.提供一个静态成员变量【私有】,用于保存当前类对象
* 3.提供一个静态方法【公共】返回创建好的当前类对象
* 4.为了保证在多线程情况下当前类对象只有一个我们就需要添加“synchronized”关键字
* @author 14336
*
*/
public class SingleObject1 {
private SingleObject1(){}
//当sObject1保存了一次SingleObject1对象后就会一直指向那个对象,所以以后谁要用,方法就会返回保存的对象而不会新创建对象了
private static SingleObject1 sObject1=null;
//为了保证在多线程的情况下当前类对象只有一个,我们就需要添加“synchronized”关键字
public static synchronized SingleObject1 getSingleObject1(){
//SingleObject1 sObject1=null;
if (sObject1==null) {
System.out.println("空");
sObject1=new SingleObject1();
}
// else{
// sObject1=null;
// System.out.println("变空");
// }
System.out.println("不空");
return sObject1;
}
}
2.饿汉式
package com.wangxing.test1;
/**
* 饿汉式
* @author Administrator
*
*/
public class SingleObject2 {
/*
1.构造方法私有
2.提供一个静态成员变量[私有],创建当前类对象
3.提供一个静态方法【公共】返回当前类对象
*/
private SingleObject2() {};
private static SingleObject2 sobj1=new SingleObject2();
//当在多线程情况下使用是为了保证当前类对象只有一个我们就需要添加"synchronized"
public static synchronized SingleObject2 getSingleObject1() {
return sobj1;
}
}
区别
| 懒汉式 | 饿汉式 |
资源利用率 | 好 | 差 |
运行速度 | 差 | 好 |
五、什么是工厂模式?
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
1.需求量大
2.牵一发,动全身
有3种角色
-
工厂角色----【普通类】
-
抽象产品角色---【抽象类/接口】
-
具体产品角色----【抽象类/接口子类】
例如:有农场生产各种水果,有西瓜,有苹果,有香蕉......
农场---工厂角色----【类】
水果---抽象产品角色---【抽象类/接口】
西瓜,苹果,香蕉---具体产品角色----【抽象类/接口子类】
抽象工厂模式---提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
六、如何编写简单工厂【静态】模式?
具体代码自己实现
七、什么是代理模式?
1.静态代理
为其他对象提供一种代理以控制对这个对象的访问。
买火车票不一定在火车站买,也可以去代售点。
//提供售票业务的接口
package com.wangxing.proxy1;
/**
* 提供售票业务的接口
* @author 14336
*
*/
public interface SellePiaoService {
void maipiao();
}
//火车站
package com.wangxing.proxy1;
/**
* 火车站
* @author 14336
*
*/
public class HuoCheZhan implements SellePiaoService {
@Override
public void maipiao() {
System.out.println("我家离火车站近,我就去火车站买票");
}
}
//代售点
package com.wangxing.proxy1;
public class DaiShouDian implements SellePiaoService {
@Override
public void maipiao() {
System.out.println("我家离火车站远,我就去代售点买票");
}
}
//测试
package com.wangxing.proxy1;
public class TestMain {
public static void main(String[] args) {
//根据家距离火车站的远近,决定去什么地方买票
//家距离火车站近,选择火车站
HuoCheZhan hcZhan=new HuoCheZhan();
hcZhan.maipiao();
//家距离火车站远,选择代售点买票
DaiShouDian daiShouDian=new DaiShouDian();
daiShouDian.maipiao();
}
}
上面这个实例中体现了静态代理,【兄弟模式的静态代理】
package com.wangxing.proxy2;
public interface SellePiaoService {
void maipiao();
}
package com.wangxing.proxy2;
public class HuoCheZhan implements SellePiaoService{
@Override
public void maipiao() {
System.out.println("家距离火车站近,去火车站买票");
}
}
//子类 继承火车站的买票方法
package com.wangxing.proxy2;
public class DaiShouDian extends HuoCheZhan{
@Override
public void maipiao() {
System.out.println("家距离火车站远,去代售点买票");
}
}
package com.wangxing.proxy2;
public class TestMain {
public static void main(String[] args) {
//根据家距离火车站的远近,决定去什么地方买票
//距离火车站近,就去火车站买票
HuoCheZhan cheZhan=new HuoCheZhan();
cheZhan.maipiao();
//家距离火车站远,就选择去代理点买票
DaiShouDian daiShouDian=new DaiShouDian();
daiShouDian.maipiao();
}
}
上面这个实例中体现了静态代理,【父子模式的静态代理】
静态代理的缺点需要手动创建子类,当需要被代理元素较多的时候,我们的工作量变大了。
由于静态代理的上述缺点,所以我们需要一个专门生产代理对象的类
2.动态代理--专门由一个类来生产需要被代理的类的代理对象。
有两种方式:
动态代理----通过java.lang.reflect Class Proxy类来创建代理类对象。
【只能为实现过某个接口的java类提供代理类对象】
CGlib代理-------CGlib是一个第三发的开发包,用的时候需要自己事先下载导入到项目中
【所有的java类提供代理类对象】
七、JDK动态代理与CGlib代理的区别
JDK动态代理:
动态代理相比于静态代理,动态代理的代理类是在动态生成的,也就是jvm通过反射获取代码生成代理类,所以用户并不能决定代理角色和真实角色之间的联系,而是由程序运行时候决定的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理实现的三步走:
1.实现InvocationHandler接口,创建自己的调用处理器 。
2.给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 。
3.执行真实角色具体任务。
要想创建处理器必须实现InvocationHandler接口,之后通过类装载器创建代理类。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:指代我们所代理的那个真实对象
method:指代的是我们所要调用真实对象的某个方法的Method对象
args:指代的是调用真实对象某个方法时接受的参数
我们依旧要创建抽象角色和真实角色类
package com.wangxing.proxy3;
/**
* 提供售票业务的接口
* @author 14336
*
*/
public interface SellePiaoService {
void maipiao();
}
package com.wangxing.proxy3;
/**
* 火车站
* @author 14336
*
*/
public class HuoCheZhan implements SellePiaoService {
@Override
public void maipiao() {
System.out.println("我家距离火车站近,我就到火车站买票");
}
}
package com.wangxing.proxy3;
public class DaiShouDian implements SellePiaoService {
@Override
public void maipiao() {
System.out.println("我家离火车站远,我就去代售点买票");
}
}
代理角色:和静态代理相比,静态代理运用对象调用完成代理,而动态代理通过实现InvocationHandler接口,创建了一个调用处理器,其实是通过Java的反射机制完成.
package com.wangxing.proxy3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 1.需要让生成代理类对象的java类实现InvocationHandler接口
* 2.重写InvocationHandler接口的invoke方法
* 3.创建生成代理对象的方法
* @author 14336
*
*/
public class CreateDaiShouDianObject implements InvocationHandler{
/*
* InvocationHandler 是代理实例的调用处理程序 实现的接口。
* 每个代码实例都具有一个关联的调用处理程序。对代理实例调用方法时,
* 将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
* Object invoke(Object proxy, Method method, Object[] args)
* 在代理实例上处理方法调用并返回结果。
* 参数:
* proxy - 在其上调用方法的代理实例
*
* method - 对应于在代理实例上调用的接口方法的 Method 实例。
* Method 对象的声明类将是在其中声明方法的接口,
* 该接口可以是代理类赖以继承方法的代理接口的超接口。
*
* args - 包含传入代理实例上方法调用的参数值的对象数组,
* 如果接口方法不使用参数,则为 null。
* 基本类型的参数被包装在适当基本包装器类
* (如 java.lang.Integer 或 java.lang.Boolean)的实例中。
*/
//定义目标对象【具体需要代理对象的java类】
private Object targetObject;
public CreateDaiShouDianObject(Object targetObject) {
this.targetObject=targetObject;
}
/*
* Proxy 提供用于创建 动态代理类 和 实例 的静态方法,
* 它还是由这些方法创建的所有动态代理类的超类。
* 构造方法:
* protected Proxy(InvocationHandler h)
* 使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。
protected--受保护的
*/
/*
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
* 参数:
* loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
*
*/
//通过java提供的Proxy类创建出代理对象
public Object getProxyObject(){
//getClassLoader() 返回该类的类加载器。有些实现可能使用 null 来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回 null。
//返回:加载此对象所表示的类或接口的类加载器。
return Proxy.newProxyInstance(this.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
//用来执行具体的代理动作
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(targetObject, args);
}
}
package com.wangxing.proxy3;
public class TestMain {
public static void main(String[] args) {
//根据家距离火车站的远近,决定去什么地方买票
//家距离火车站近,选择火车站
HuoCheZhan cheZhan=new HuoCheZhan();
cheZhan.maipiao();
//代售点类
DaiShouDian daiShouDian=new DaiShouDian();
//家距离火车站远,选择代售点
//创建火车站的代理对象
CreateDaiShouDianObject createDaiShouDianObject= new CreateDaiShouDianObject(daiShouDian);
//得到代理对象
SellePiaoService proxy=(SellePiaoService) createDaiShouDianObject.getProxyObject();
proxy.maipiao();
}
}
看上去实现很简单,我们只需要实现接口,调用其中的方法就可以实现动态代理。
通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法。
注意:JDK动态代理只能处理有接口实现关系的java类
Cglib代理:
cglib是一个强大的高性能的代码生成包,广泛的被许多AOP的框架使用,它的开源地址在https://github.com/cglib/cglib
通过它实现动态代理,主要用到import net.sf.cglib.proxy包下的MethodInterceptor、MethodProxy和Enhancer类
(1)、MethodInterceptor
MethodInterceptor类是方法拦截器,代理类实例的方法被调用时会回调MethodInterceptor的intercept()方法拦截,用来实现自己的代理逻辑,类似于jdk动态代理的InvocationHandler接口
(2)、MethodProxy
MethodProxy是intercept()方法中的第四个参数的类型,它是实际类方法的代理引用,使用methodProxy比使用jdk自身的method在效率上会有提升
(3)、Enhancer
Enhancer用来动态创建实际类子类的代理类实例,setSuperclass()方法设置实际类为父类,setCallback()方法建立方法回调,create()方法创建一个代理类实例
package com.wangxing.proxy4;
public class DaiLiDian extends HuoCheZhan {
public DaiLiDian() {
// TODO 自动生成的构造函数存根
}
public DaiLiDian(String name){
}
@Override
public void maipiao() {
System.out.println("距离代理点近,就去代理点买票");
}
}
package com.wangxing.proxy4;
/**
* 火车站
* @author 14336
*
*/
public class HuoCheZhan {
public void maipiao(){
System.out.println("我家离火车站近,我就去火车站买票");
}
}
package com.wangxing.proxy4;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 1.需要让生成代理类对象的java类实现MethodInterceptor接口
* 2.重写MethodInterceptor接口的intercept方法
* 3.创建生成代理对象的方法
* @author 14336
*
*/
public class CreateDaiShouDianObject implements MethodInterceptor {
//定义目标对象【具体需要代理对象的java类】
private Object targetObgect;
public CreateDaiShouDianObject(Object targetObject) {
this.targetObgect=targetObject;
}
//创建代理类对象
public Object getProxyObject(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(targetObgect.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
//用来执行具体的代理动作
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// TODO 自动生成的方法存根
return arg3.invokeSuper(arg0, arg2);
}
}
package com.wangxing.proxy4;
public class TestMain {
public static void main(String[] args) throws Exception {
//根据家距离火车站的远近,决定去什么地方买票
//家距离火车站近,选择火车站
HuoCheZhan cheZhan=new HuoCheZhan();
Class daili=Class.forName("com.wangxing.proxy4.DaiLiDian");
//注意newInstance()这个方法调用的是类的无参数构造方法来完成类的创建
Object object= daili.newInstance();
cheZhan.maipiao();
//家距离火车站远,选择代售点
//创建火车站的代理对象
CreateDaiShouDianObject createDaiShouDianObject=new CreateDaiShouDianObject(object);
//得到代理对象
//子类对象父类保存
HuoCheZhan proxy=(DaiLiDian)createDaiShouDianObject.getProxyObject();
proxy.maipiao();
}
}
比较
1.静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
手动创建一个与目标类相同接口的子类,包装目标类。
2.JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;【兄弟模式】
通过jdk提供的反射包中Proxy这个类,动态的创建一个与目标类实现相同接口的子类对象,包装目标。
3.CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。【父子模式】
通过CGlib提供的Enhancer这个类,动态的创建一个目标类的子类对象,包装目标类。
静态代理 | JDK动态代理 | CGlib动态代理 |
手动创建代理类 | 动态生成代理类对象 | 动态生成代理类对象 |
| jdk提供的反射包中Proxy这个类 | CGlib提供的Enhancer这个类 |
| 只能为实现过接口的Java类创建代理对象 | 为任何Java类创建代理对象 |