设计模式是什么
设计模式是面向对象的一种思想。
设计模式的基本原则?
- 单一职责原则
- 开放封闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖翻转原则
基本分类和为什么分为3类?
创建型(怎么优雅创建对象)
结构性(对象的结构)
行为型(运行效果)(主要使用多态)
工厂模式
基本思想:我需要一辆汽车,我不用关心汽车是怎么创造出来的,只需要去工厂提货。
分类:创造型
public class CarFactory {
public Car getCar(int type) {
if (type == 1) {
return new BenzCar("奔驰", 7);
}
if (type == 2) {
return new BmwCar("宝马", 7);
}
throw new IllegalArgumentException("类型错误");
}
}
public interface Car {
void run();
}
public class BenzCar implements Car {
String name;
Integer total;
public BenzCar(String name, Integer total) {
this.name = name;
this.total = total;
}
@Override
public void run() {
System.out.println("Benz run fast .. ");
}
@Override
public String toString() {
return "BenzCar{" +
"name='" + name + '\'' +
", total=" + total +
'}';
}
}
public class BmwCar implements Car {
String name;
Integer total;
public BmwCar(String name, Integer total) {
this.name = name;
this.total = total;
}
@Override
public void run() {
System.out.println("Bmw run fast too ..");
}
@Override
public String toString() {
return "BmwCar{" +
"name='" + name + '\'' +
", total=" + total +
'}';
}
}
我需要汽车的时候只需要去CarFactory 里面告诉他们类型就可以得到对应的汽车。
JDK 中的工厂设计模式
java.util.Calendar
策略模式
策略是小处替换
为什么要使用策略模式?
答:消除业务逻辑中非常多的if else。具体业务看下面这个例子。
public String getCheckResult(String type) {
if ("校验1".equals(type)) {
return "执行业务逻辑1";
} else if ("校验2".equals(type)) {
return "执行业务逻辑2";
} else if ("校验3".equals(type)) {
return "执行业务逻辑3";
} else if ("校验4".equals(type)) {
return "执行业务逻辑4";
} else if ("校验5".equals(type)) {
return "执行业务逻辑5";
} else if ("校验6".equals(type)) {
return "执行业务逻辑6";
} else if ("校验7".equals(type)) {
return "执行业务逻辑7";
} else if ("校验8".equals(type)) {
return "执行业务逻辑8";
} else if ("校验9".equals(type)) {
return "执行业务逻辑9";
}
return "不在处理的逻辑中返回业务错误";
}
最简单的实现?
阿里规范对于多if-else的说明?
卫语句
Replace Nested Conditional with Guard Clauses
示例:来自规范嵩山版
public void findBoyfriend(Man man) {
if (man.isUgly()) {
System.out.println("本姑娘是外貌协会的资深会员");
return;
}
if (man.isPoor()) {
System.out.println("贫贱夫妻百事哀");
return;
}
if (man.isBadTemper()) {
System.out.println("银河有多远,你就给我滚多远");
return;
}
System.out.println("可以先交往一段时间看看");
}
策略模式在 JDK 中的应用?
compare()、ThreadPoolExecutor 拒绝策略RejectedExecutionHandler
单例模式
定义
jvm中只有一个实例创建。
饿汉式
怎么理解什么是饿汉式,我已经饿的受不了了,我必须立即吃饭。
定义:在类加载的时候就会创建单例对象。
优点:简单,无线程安全问题。
缺点:浪费空间,不够灵活。
public class Hungry {
public final static Hungry INSTANCE = new Hungry();
private Hungry() {
}
}
饿汉式 静态代码块
public class HungryStatic {
public final static HungryStatic INSTANCE;
static {
INSTANCE = new HungryStatic("例如从配置文件读入");
}
private HungryStatic(String config) {
}
private String config;
public String getConfig() {
return config;
}
public void setConfig(String config) {
this.config = config;
}
}
枚举
优点:线程安全,不会被反射破坏。
public enum Enum {
INSTANCE;
}
懒汉式 (线程不安全)
定义:在使用的时候判断如果没有创建过则创建。
优点:节约空间。
缺点:存在线程安全问题。
public class Lazy {
private static Lazy instance;
private Lazy() {
}
public static Lazy getInstance() {
if (instance == null) {
instance = new Lazy();
}
return instance;
}
}
多个线程同时进行 instacne == null 判断,会导致多个线程同时创建对象。
懒汉式 双检锁 DCL
public class LazySafe {
private static volatile LazySafe instance;
private LazySafe() {
}
public static LazySafe getInstance() {
if (instance == null) {
synchronized (LazySafe.class) {
if (instance == null) {
instance = new LazySafe();
}
}
}
return instance;
}
}
为什么需要第二次判断
假设有10个线程同时进来争抢锁,线程一拿到锁,其他9个线程进入阻塞状态等待锁释放继续争抢,如果没有第二次判断,每个线程拿到锁后都会创建一个新的对象,加入第二次判断防止后来的线程创建。
为什么要加 volatile
由于指令重排序,new 对象的过程可能会发生先指向空对象但是没有完成初始化的情况。
一句话总结就是防止拿到 instance == null 的 对象。
反编译一条 new 指令可以拿到以下的结果。
步骤4 初始化对象,步骤7 指针指向该对象,由于 cpu存在指令重排序,先完成指针指向的话就会产生null对象的情况,所以需要禁止指令重排序保证对象初始化之后在完成指向的动作。
懒汉式 静态内部类
原理 : 静态内部类的延迟初始化,且类只会加载一次。
public class LazyInner {
private LazyInner() {
}
private static class Inner {
private static final LazyInner INSTANCE = new LazyInner();
}
public static LazyInner getInstance() {
return Inner.INSTANCE;
}
}
The “Double-Checked Locking is Broken” Declaration
如何破坏单例?
多线程破坏单例
在普通的懒汉式单例中可以被多线程破坏。
错误示例
将获取实例的锁升级到方法级别。
可以保证线程安全,但是方法性能太低,任意读请求都需要排队依次加锁获取对象。
典型的锁粒度过粗的问题。
正确示例
使用双检锁,或者内部类的形式保证线程安全。
指令重排序破坏单例
双检锁不使用 volatile 关键字修饰,就会产生重排序情况的产生。
克隆破坏单例
反序列化破坏单例
反射破坏单例
结论 只有枚举可以防止通过反射来破坏。
破坏实例
我们以Runtime类为例来演示使用反射破坏单例。
Class<Runtime> aClass = Runtime.class;
Constructor<Runtime> cons = aClass.getDeclaredConstructor();
cons.setAccessible(true);
Runtime runtime = cons.newInstance();
Runtime runtime2 = cons.newInstance();
System.out.println(runtime);
System.out.println(runtime2);
----
java.lang.Runtime@568db2f2
java.lang.Runtime@378bf509
解决方案
优先使用枚举来解决反射破坏单例的问题。
我们来获取一下枚举类中的构造器
Class<Enum> aClass = Enum.class;
Constructor<?>[] constructors = aClass.getDeclaredConstructors();
// Constructor<Enum> cons = aClass.getDeclaredConstructor();
Constructor<Enum> cons2 = aClass.getDeclaredConstructor(String.class, int.class);
// cons.setAccessible(true);
cons2.setAccessible(true);
// Enum e1 = cons.newInstance();
// Enum e2 = cons.newInstance();
Enum e3 = cons2.newInstance();
Enum e4 = cons2.newInstance();
可以看到里面会生成一个String , int 参数类型的构造器
idea 反编译结果跟预期明显不同。
javap 反编译 class 文件后发现也没有相应的构造方法
我们更换反编译工具,使用 jad 进行后续的测试
jad的安装和使用
jad下载地址
将 jad.exe 放在 jdk 的 bin 目录下即可
执行命令 jad xxxx.class文件得到反编译后的源代码。
jad 反编译后成功看到Enum类中 String 和 int 参数的构造函数。
单例在java的应用场景
Runtime
饿汉式实现单例。
代理模式
参考链接1
参考链接2
这里有一个接口 Msg
public interface Msg {
void sendMsg(String msg);
Msg work(String str);
}
这里有一个Msg的实现类
public class MsgImpl implements Msg {
@Override
public void sendMsg(String msg) {
System.out.println(msg);
}
@Override
public Msg work(String str) {
System.out.println(str);
return this;
}
}
正常的调用过程如下
public class Client {
public static void main(String[] args) {
Msg msg = new MsgImpl();
msg.sendMsg("abcdefg");
msg.work("w1").work("w2");
}
}
某一天我想统计一下每一个方法耗时,最基本的我直接修改实现类中的代码,如果这个类有一百个方法,那么。。。或者这个类是其他 jar 包提供的,我们就不能通过修改代码的形式来实现。
静态代理
我们直接把实现类的对象丢进来执行原来的方法,我们在增强我们想要的功能。
public class MsgProxy implements Msg {
private Msg msg;
public MsgProxy(Msg msg) {
this.msg = msg;
}
@Override
public void sendMsg(String str) {
System.out.println("start ...");
msg.sendMsg(str);
System.out.println("end ...");
}
@Override
public Msg work(String str) {
return null;
}
}
如果我们想统计 100 个类呢? 创建 100 个静态代理么,如果你不嫌麻烦的话当然可以。
那么有没有更加简单的方法呢?
动态代理
核心接口 InvocationHandler 用来控制代理对象的动作,也就是具体实现。
核心方法 Proxy.newProxyInstance 用来创建代理对象。
第一步定义处理器,代理对象的所有调用都会分发到这个方法中。
public class ActionInvocationHandle implements InvocationHandler {
Object o;
public ActionInvocationHandle(Object o) {
this.o = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long st = System.currentTimeMillis();
Object result = method.invoke(o, args);
System.out.println(System.currentTimeMillis() - st);
return result;
}
}
第二步 创建代理对象
Msg msg2 = new MsgImpl();
Msg proxy2 = (Msg) Proxy.newProxyInstance(Msg.class.getClassLoader(),
new Class[]{Msg.class},
new ActionInvocationHandle(msg2));
proxy2.sendMsg("sdadsa");
第三步 查看输出结果
sdadsa
1000
封装创建对象过程
在处理器中新增一段代码
public class ActionInvocationHandle implements InvocationHandler {
Object o;
public ActionInvocationHandle(Object o) {
this.o = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long st = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(1);
Object result = method.invoke(o, args);
System.out.println(System.currentTimeMillis() - st);
return result;
}
public static <T> T createProxy(T target, Class<T> targetClass) {
if (!targetClass.isInterface()) {
throw new IllegalStateException("targetInterface必须是接口类型!");
} else if (!targetClass.isAssignableFrom(target.getClass())) {
throw new IllegalStateException("target必须是targetInterface接口的实现类!");
}
ClassLoader classLoader = target.getClass().getClassLoader();
return (T) Proxy.newProxyInstance(classLoader,
target.getClass().getInterfaces(),
new ActionInvocationHandle(target));
}
}
创建的过程简化为下面这样
Msg msg2 = new MsgImpl();
Msg proxy3 = ActionInvocationHandle.createProxy(msg2, Msg.class);
proxy3.sendMsg("xxxxxxxxx");
如何获取Proxy0.class文件
以jdk1.8为例
//生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
获取到的源码如下所示
public final class $Proxy0 extends Proxy implements Msg {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sendMsg(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.example.miccommon.designpatterns.proxy.Msg").getMethod("sendMsg", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Proxy 类
Proxy 重要方法
//获取处理器
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
//获取代理class
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
//判断是否
public static boolean isProxyClass(Class<?> cl)
//创建代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
cglib
添加依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
jar包目录如下
我们重点看一下 proxy 模块
这有一个类 Service1
public class Service1 {
public void m1() {
System.out.println("我是m1方法");
}
public void m2() {
System.out.println("我是m2方法");
}
}
使用 cglib 来代理 Service1 这一个类
public class Cglib01 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service1.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("调用方法:" + method);
return methodProxy.invokeSuper(o, objects);
});
Service1 proxy = (Service1) enhancer.create();
proxy.m1();
proxy.m2();
}
}
------
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service1.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service1.m2()
我是m2方法
如果我们将 setCallback 这一行注释掉会发生什么呢?
可以看到 Callback 参数是必须要传的。
那么重点就是 setCallback 这个地方,我们点进去
参数传入了一个 Callback 类型接口,我们点进去
Callback 主要子接口如下
Dispatcher
public class CglibExtend08 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service1.class);
enhancer.setCallback((Dispatcher) Service1::new);
Service1 proxy = (Service1) enhancer.create();
proxy.m1();
proxy.m2();
}
}
FixedValue
public class Cglib03 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service3.class);
enhancer.setCallback((FixedValue) () -> "路人甲");
Service3 proxy = (Service3) enhancer.create();
System.out.println(proxy.m1());
System.out.println(proxy.m2());
System.out.println(proxy.toString());
}
}
InvocationHandler
jdk
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
cglib
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
在这种类型下跟 jdk 动态代理一样
public class Cglib07 {
public static void main(String[] args) {
Service1 service1 = new Service1();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service1.class);
enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
System.out.println("调用方法:" + method);
return method.invoke(service1, objects);
});
Service1 proxy = (Service1) enhancer.create();
proxy.m1();
proxy.m2();
}
}
LazyLoader 延迟加载
案例
MethodInterceptor
MethodInterceptor
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
这里多了一个 MethodProxy proxy
NoOp
public class Cglib04 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service3.class);
enhancer.setCallback(NoOp.INSTANCE);
Service3 proxy = (Service3) enhancer.create();
System.out.println(proxy.m1());
System.out.println(proxy.m2());
}
}
效果等同于没有做任何增强
ProxyRefDispatcher
案例1:使用MethodInterceptor增强方法处理
public class Service1 {
public void m1() {
System.out.println("我是m1方法");
}
public void m2() {
System.out.println("我是m2方法");
}
}
public class Cglib01 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service1.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("调用方法:" + method);
return methodProxy.invokeSuper(o, objects);
});
Service1 proxy = (Service1) enhancer.create();
proxy.m1();
proxy.m2();
}
}
----
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m2()
我是m2方法
案例2:在一个方法内调用另一个方法是否会被拦截?
public void m1() {
System.out.println("我是m1方法");
this.m2();
}
public void m2() {
System.out.println("我是m2方法");
}
public class Cglib02 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service2.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("调用方法:" + method);
return methodProxy.invokeSuper(o, objects);
});
Service2 proxy = (Service2) enhancer.create();
proxy.m1();
}
}
----
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m2()
我是m2方法
结论,代理类中一个方法调用另一个方法仍然会进入方法拦截器。
案例3:使用FixedValue拦截并返回固定值
public class Cglib03 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service3.class);
enhancer.setCallback((FixedValue) () -> "abcdefg");
Service3 proxy = (Service3) enhancer.create();
System.out.println(proxy.m1());
System.out.println(proxy.m2());
}
}
---
abcdefg
abcdefg
案例4:使用NoOp.INSTANCE不做增强处理
public class Service3 {
public String m1() {
System.out.println("我是m1方法");
return "hello:m1";
}
public String m2() {
System.out.println("我是m2方法");
return "hello:m2";
}
}
public class Cglib04 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service3.class);
enhancer.setCallback(NoOp.INSTANCE);
Service3 proxy = (Service3) enhancer.create();
System.out.println(proxy.m1());
System.out.println(proxy.m2());
}
}
----
我是m1方法
hello:m1
我是m2方法
hello:m2
案例5:不同的方法使用不同的拦截器(CallbackFilter)
enhancer.setCallbackFilter 通过 Callback[] 索引位置来决定走哪一个处理器。
public class Service4 {
public void insert1() {
System.out.println("我是insert1");
}
public void insert2() {
System.out.println("我是insert2");
}
public String get1() {
System.out.println("我是get1");
return "get1";
}
public String get2() {
System.out.println("我是get2");
return "get2";
}
}
public class Cglib05 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service4.class);
Callback[] callbacks = {
(MethodInterceptor) (o, method, objects, methodProxy) -> {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
},
(FixedValue) () -> "fixedValueDefault"
};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(method -> {
String methodName = method.getName();
return methodName.startsWith("insert") ? 0 : 1;
});
Service4 proxy = (Service4) enhancer.create();
System.out.println("---------------");
proxy.insert1();
System.out.println("---------------");
proxy.insert2();
System.out.println("---------------");
System.out.println(proxy.get1());
System.out.println("---------------");
System.out.println(proxy.get2());
}
}
----
---------------
我是insert1
public void com.example.lurenjia.spring.c15.cglib.Service4.insert1(),耗时(纳秒):7290600
---------------
我是insert2
public void com.example.lurenjia.spring.c15.cglib.Service4.insert2(),耗时(纳秒):35500
---------------
fixedValueDefault
---------------
fixedValueDefault
案例6:使用CallbackHelper
public class Cglib06 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//第一个
Callback costTimeCallback = (MethodInterceptor) (Object o, Method method, Object[] objects, MethodProxy methodProxy) -> {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
};
//第二个
Callback fixdValueCallback = (FixedValue) () -> "路人甲Java";
CallbackHelper callbackHelper = new CallbackHelper(Service4.class, null) {
@Override
protected Object getCallback(Method method) {
return method.getName().startsWith("insert") ? costTimeCallback : fixdValueCallback;
}
};
enhancer.setSuperclass(Service4.class);
enhancer.setCallbacks(callbackHelper.getCallbacks());
enhancer.setCallbackFilter(callbackHelper);
Service4 proxy = (Service4) enhancer.create();
System.out.println("---------------");
proxy.insert1();
System.out.println("---------------");
proxy.insert2();
System.out.println("---------------");
System.out.println(proxy.get1());
System.out.println("---------------");
System.out.println(proxy.get2());
}
}
案例7:实现通用的统计任意类方法耗时代理类
public class CostTimeProxy implements MethodInterceptor {
//目标对象
private Object target;
public CostTimeProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
//调用被代理对象(即target)的方法,获取结果
Object result;
result = method.invoke(target, objects);
// result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
}
/**
* 创建任意类的代理对象
*
* @param target
* @param <T>
* @return
*/
public static <T> T createProxy(T target) {
CostTimeProxy costTimeProxy = new CostTimeProxy(target);
Enhancer enhancer = new Enhancer();
enhancer.setCallback(costTimeProxy);
enhancer.setSuperclass(target.getClass());
return (T) enhancer.create();
}
}
常见问题
method.invoke 原理
jdk动态代理死循环
常见在将 proxy 代理对象传入到 method.invoke中
method.invoke 一路追踪到一个native方法
$Proxy0 中的方法
try {
super.h.invoke(this, m3, new Object[]{var1});
}
由于动态代理生成的对象每一个方法都会进入到 处理器的 invoke 方法,而 method.invoke 每一次执行过一遍这个 invoke 因此形成了死循环。
为什么 jdk 动态代理只能代理接口
public class JdkProxyClass {
public static void main(String[] args) {
InvocationHandler h = (proxy1, method, args1) -> {
return "success";
};
//方式一
A jdkProxy = (A) Proxy.newProxyInstance(A.class.getClassLoader(), new Class[]{A.class}, h);
}
}
class A {
}
--------
Exception in thread "main" java.lang.IllegalArgumentException: com.example.lurenjia.spring.c15.A is not an interface
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at com.example.lurenjia.spring.c15.JdkProxyClass.main(JdkProxyClass.java:18)
还记得我们获取过的 Proxy0.class 这个文件么,注意这个类继承了 Proxy类。
public final class $Proxy0 extends Proxy implements Msg
众所周知,java是不支持多重继承的,所以 jdk 动态代理在实现上确定了只能处理接口。
那么在源码中是哪一步继承的 Proxy 呢 ?
为什么 jdk 动态代理生成的代理类要继承 Proxy
试想一下,如果不继承 Proxy 这个类会发生什么呢?
区别
jdk动态代理只能为接口创建代理。
Enhancer既能够代理普通的class,也能够代理接口。
都是在运行期生成字节码。
jdk通过反射机制,cglib通过fastclass机制。
模板方法
意图
强调步骤的顺序。
模式的特征
抽象类abstract 和需要子类重写的abstract 方法。
实例
喝茶跟喝咖啡抽象成四个方法,第一步和最后一步是所有子类相同方法。
中间的两个步骤是子类所独有的方法。
public abstract class drink {
/**
* 封装四个步骤
*/
final void ToDrink() {
first();
second();
third();
fourth();
}
/**
*
*/
abstract void second();
/**
*
*/
abstract void third();
void first() {
System.out.println("第一步添水");
}
void fourth() {
System.out.println("第四步清洗");
}
}
喝咖啡独特步骤
public class Coffee extends drink {
/**
* 独特的步骤由自己进行重写
*/
@Override
void second() {
System.out.println("第二步倒入咖啡");
}
/**
* 独特的步骤自己重写
*/
@Override
void third() {
System.out.println("第三步倒入白糖");
}
}
喝茶独特步骤
public class Tea extends drink {
@Override
void second() {
System.out.println("第二步倒入茶叶");
}
@Override
void third() {
System.out.println("第三步倒入茶杯");
}
}
分别执行"喝"这一过程
public class Client {
public static void main(String[] args) {
drink d1 = new Coffee();
d1.ToDrink();
System.out.println("-----------");
drink d2 = new Tea();
d2.ToDrink();
}
}
结果如下
第一步添水
第二步倒入咖啡
第三步倒入白糖
第四步清洗
-----------
第一步添水
第二步倒入茶叶
第三步倒入茶杯
第四步清洗
优点
- 封装不变部分,扩展可变部分。
- 把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。
- 提取公共部分代码,便于维护。
- 行为由父类控制,子类实现
缺点
- 算法骨架需要改变时需要修改抽象类。
- 按照设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度。
设计模式的区别
策略模式和状态模式的区别?
策略和模板区别?
模板定义大的框架,策略用来定义定义小细节。