单例
单例模式是设计模式中比较简单的过程,就不说那么废话了,网上的写法有很多,下面给出的是我认为最可靠的用法。
/**
* 单例模式 要考虑的三点因素
* <p>
* 1. 线程安全
* 2. 延迟加载
* 3. 序列化与反序列化安全
*/
public class Singleton {
//使用volatile保证线程的可见性,
private static volatile Singleton singleton = null;
//这里写个私有的构造函数是防止在外部可以直接new这个对象
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {//添加判断减少排队,提高效率
//加锁保证线程安全
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
代理模式
代理模式 分为 静态单例和动态代理,接下来以老板和员工(小明)对话的形式来说明
老板:小明啊,我给你出个题吧,飞的的动作,可以用小鸟飞,用代码给我设计一下
小明:简单呀,先设计了一个 flyAble接口 ,小鸟实现一下不就行啦吗
interface FlyAble {
void fly();
}
class XiaoNiao implements FlyAble {
@Override
public void fly() {
int i = 0;
while (i < 100) {
System.out.println("小鸟飞呀飞......");
i++;
}
}
}
老板: 如果让你不动小鸟飞实现的那个方法,你再把小鸟飞了多长时间算出来,怎么设计呢?
小明:你妹的,事真多,想了片刻后,说 在建立一个类(小鸟代理类) 把小鸟注入一下,应该就可以了,来,看下代码:
class XiaoNiaoProxy implements FlyAble {
private XiaoNiao xiaoNiao;
XiaoNiaoProxy(XiaoNiao xiaoNiao) {
this.xiaoNiao = xiaoNiao;
}
@Override
public void fly() {
long start = System.currentTimeMillis();
xiaoNiao.fly();
long end = System.currentTimeMillis();
System.out.println("飞了==" + String.valueOf(end - start) + "毫秒");
}
}
接着咱们看一下测试过程:
public class ProxyDemo {
public static void main(String[] args) {
XiaoNiao xiaoNiao = new XiaoNiao();
XiaoNiaoProxy xiaoNiaoProxy = new XiaoNiaoProxy(xiaoNiao);
xiaoNiaoProxy.fly();
}
}
很显然小明回答的还算可以,其实看到这里就应该明白了静态代理是怎么回事了,起到一个作用就是在不能动原来代码的基础上,可以实现对原来功能的操作,就这么简单。
接下来先总结一下静态代理的特点:
- 实现和目标类相同的接口
- 需要在代理类中注入目标类
- 代理类需要手动编写
接下了继续上面的对话
老板:想不想再多涨点钱呀,想的话,再问一个问题,在上面你编写的这个代理类,如果你不去手动编写这个代理类,而是动态生成你应该怎么做呢?
小明:他大爷的,有完没完了,但是小明为了钱,想到了动态代理的实现,于是乎写出了下面的代码:
/**
* @Author ZQ
* @Description //动态代理
* @Date 2019/4/1 17:25
* @Param
* @return
**/
class XiaoNiaoInvoHandler implements InvocationHandler {
/*注入目标类*/
private Object obj;
XiaoNiaoInvoHandler (Object object) {
this.obj = object;
}
/**
* @return java.lang.Object
* @Author ZQ
* @Description //TODO
* @Date 2019/4/1 17:50
* @Param [o: 对应的目标类, method 目标类中的方法, args:目标类中方法对应的参数]
**/
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
//参数1 : 目标类,参数2:对应的参数,直接传入就行
method.invoke(obj, args);
long end = System.currentTimeMillis();
System.out.println("飞了==" + String.valueOf(end - start) + "毫秒");
return null;
}
}
下面在看下测试过程
public class ProxyDemo {
public static void main(String[] args) {
XiaoNiao xiaoNiao = new XiaoNiao();
InvocationHandler handler = new XiaoNiaoInvoHandler(xiaoNiao);
/**
* @Author ZQ
* @Description // Proxy.newProxyInstance(目标类的加载器,目标类实现的接口, 实现InvocationHandler的类);
**/
FlyAble flyAble = (FlyAble) Proxy.newProxyInstance(XiaoNiao.class.getClassLoader(),
XiaoNiao.class.getInterfaces(), handler);
flyAble.fly(2);
}
}
老板: 牛逼呀,小伙子,下个月直接提升为CTO。
从此小明踏上了人生巅峰,开启了浪比的生活。。。。
其实到这里动态代理的实现和用法应该明白了。
总结一下动态代理的实现:
- 需要建立一个类实现InvocationHandler接口,并在这个类中注入目标类
- 需要通过调用Proxy.newProxyInstance(目标类的加载器,目标类实现的接口, 实现InvocationHandler接口的类)来生成代理类。从而达到动态代理的效果。
- 然后调用目标类实现的接口中的方法就动态调用了。
在动态代理中其实只要明确那个是目标类,然后按照动态代理的套路去使用,是很好使用的。
静态代理和动态代理区别:
其实主要的区别就是动态代理可以动态的生成代理类;只不过是生成代理类过程不需要你自己手动编写了,Proxy.newProxyInstance()这个方法会动态生成相应的代理类,从而达到代理的效果。
解密动态代理
这会在回过头来看看静态代理他都干了些什么事
- 需要注入目标类
- 实现了和目标类一样的接口
- 然后在自己的实现中调用目标类的方法,并在该方法前后可以进行某些操作
既然在静态代理中需要这三个条件,那么同样动态代理也需要这三个条件。
现在看下Proxy.newProxyInstance(目标类类加载器,目标类实现的接口,实现InvocationHandler接口的类)。
这个方法(先不考虑第三个参数)把需要生成代理类的参数传过去了,然后通过java中的反射机制,生成代理类。
接下来在看下第三个参数,其实是可上方的第三个条件相对应的,就是需要你自定义需要再目标类中对他进行什么操作,就也就是InvocationHandler中的invoke()方法。
至于通过java的反射怎样生成动态代理,这里就不做讨论了。