Java面试总结-设计模式

单例

单例模式是设计模式中比较简单的过程,就不说那么废话了,网上的写法有很多,下面给出的是我认为最可靠的用法。

/**
 * 单例模式  要考虑的三点因素
 * <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();
    }
}

很显然小明回答的还算可以,其实看到这里就应该明白了静态代理是怎么回事了,起到一个作用就是在不能动原来代码的基础上,可以实现对原来功能的操作,就这么简单。
接下来先总结一下静态代理的特点:

  1. 实现和目标类相同的接口
  2. 需要在代理类中注入目标类
  3. 代理类需要手动编写

接下了继续上面的对话
老板:想不想再多涨点钱呀,想的话,再问一个问题,在上面你编写的这个代理类,如果你不去手动编写这个代理类,而是动态生成你应该怎么做呢?
小明:他大爷的,有完没完了,但是小明为了钱,想到了动态代理的实现,于是乎写出了下面的代码:

/**
 * @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。
从此小明踏上了人生巅峰,开启了浪比的生活。。。。

其实到这里动态代理的实现和用法应该明白了。
总结一下动态代理的实现:

  1. 需要建立一个类实现InvocationHandler接口,并在这个类中注入目标类
  2. 需要通过调用Proxy.newProxyInstance(目标类的加载器,目标类实现的接口, 实现InvocationHandler接口的类)来生成代理类。从而达到动态代理的效果。
  3. 然后调用目标类实现的接口中的方法就动态调用了。
    在动态代理中其实只要明确那个是目标类,然后按照动态代理的套路去使用,是很好使用的。

静态代理和动态代理区别:
其实主要的区别就是动态代理可以动态的生成代理类;只不过是生成代理类过程不需要你自己手动编写了,Proxy.newProxyInstance()这个方法会动态生成相应的代理类,从而达到代理的效果。

解密动态代理

这会在回过头来看看静态代理他都干了些什么事

  1. 需要注入目标类
  2. 实现了和目标类一样的接口
  3. 然后在自己的实现中调用目标类的方法,并在该方法前后可以进行某些操作

既然在静态代理中需要这三个条件,那么同样动态代理也需要这三个条件。

现在看下Proxy.newProxyInstance(目标类类加载器,目标类实现的接口,实现InvocationHandler接口的类)。
这个方法(先不考虑第三个参数)把需要生成代理类的参数传过去了,然后通过java中的反射机制,生成代理类。
接下来在看下第三个参数,其实是可上方的第三个条件相对应的,就是需要你自定义需要再目标类中对他进行什么操作,就也就是InvocationHandler中的invoke()方法。
至于通过java的反射怎样生成动态代理,这里就不做讨论了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值