1.适配器模式简单来使用:
Adapter实现新接口,原接口及其实现类保持不变,并由Adapter来持有实现类的对象,此为对象适配模式。
Adapter实现新接口,并继承原接口的实现类,此为类适配模式。
这两种适配方式既可以使用新接口的方法,又可以使用原接口的方法,还可以在Adapter中进行适配。
2.单例模式:懒汉式线程不安全,饿汉式线程安全,但如果不使用造成内存浪费
public class Singleton { //一个静态的实例 private static Singleton singleton; //私有化构造函数 private Singleton(){} //给出一个公共的静态方法返回一个单一实例 public static Singleton getInstance(){ if (singleton == null) { singleton = new Singleton(); } return singleton; } }
上述代码为懒汉式
public class Singleton { private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } }
上述代码为饿汉式
以上两者具有特点:1.静态事例私有化 2.构造方法私有化 3.给出一个公共的静态方法返回实例
再看下面synchronized同步代码块的类型
package com.audience.admin.demo; public class Singleton { private static Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if (singleton==null){ //同步代码块 synchronized (Singleton.class){ if (singleton==null){ singleton = new Singleton(); } } } return singleton; } }
上面这种方式,因为JVM会针对字节码进行调优,而其中的一项调优便是调整指令的执行顺序,从而有可能会出现:先将内存地址赋给对象,然后再进行初始化构造器。
以上面代码来说,就是先将分配好的内存地址指给singleton,然后再进行初始化构造器,如果在初始化构造器之前,有线程使用了singleton,会认为singleton对象已经
实例化了,所以会产生莫名的错误。
而且,假如线程1和线程2同时走到同步代码块,线程1抢到了锁,线程2阻塞,那么线程1创建singleton对象,释放锁,然后线程2获取锁,但是线程1创建对象之后,同步
singleton对象到主内存的操作并非是原子性的操作,假如未同步完成,线程2又创建了singleton对象,那么就会出现问题。
对于上述情况,需要在私有静态成员变量加volatile关键字修饰(private static volatile Singleton singleton;),原因如下:
1、volatile关键字保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这个新值对其他线程来说是立即可见的(原因:使用volatile关键字会
强制将修改的值立即写入到主内存,并且使其他线程缓存的该变量的值失效,如果其他线程需要操作该变量,则需要重新从主内存中读取)
2、禁止进行代码指令重排序
依据volatile的这两个条件,可以正确创建单利。
再看看静态内部类的单利模式
package com.audience.admin.demo; public class SingletonInstance { private SingletonInstance(){} public static SingletonInstance getSingletonInstance(){ return SingletonInner.singletonInstance; } private static class SingletonInner{ static SingletonInstance singletonInstance = new SingletonInstance(); } }
3.简单工厂模式:工厂提供静态方法,依据传入的参数返回产品(或实例)。缺点:根据参数使用if判断返回产品,不符合开闭原则
4.工厂方法模式:工厂方法模式中定义了一个工厂接口,而具体的创建工作推迟到具体的工厂类,它是对简单工厂模式中的工厂类进一步抽象化,弥补了简单工厂模式对修改开放的诟病。缺点:如果产品很多,需要创建很多工厂类比较臃肿。
5.抽象工厂模式:产品A Iphone, B HuaWei,C Galaxy这三种手机,产品a Mac,b Honor,c samsung这三种电脑,那么A和a,B和b,C和c被称为同一类型(同一公司)。如果使用简单工厂模式去生产这6个产品,那么最少需要6个if判断,如果使用工厂方法模式生产这些产品,最少创建6工厂。使用抽象工厂模式,最顶层接口工厂生产手机和电脑,第一个工厂生产A和a,第二个工厂生产B和b,第三个工厂生产C和c,只需要三个工厂即可。
声明手机接口
public interface Phone { //声明手机接口,为了简单就不声明方法 }
声明电脑接口
public interface Notebook { //声明电脑接口,为了简单就不声明方法 }
Iphone类
public class Iphone implements Phone { public Iphone() { System.out.println("Iphone"); } }
Mac类
public class Mac implements Notebook { public Mac() { System.out.println("Mac"); } }
声明手机和电脑的工厂接口
public interface Factory { public Phone createPhone(); public Notebook createNotebook(); }
工厂A 生产A和a产品 由使用者调用工厂方法即可
public class FactoryA implements Factory { @Override public Phone createPhone() { return new Iphone(); } @Override public Notebook createNotebook() { return new Mac(); } }
6.代理模式:提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。代理分为静态代理和动态代理
静态代理:静态代理在使用时,需要定义接口或者父类,被代理对象和代理对象一起实现相同的接口或者继承相同的父类。
使用:1.被代理对象和代理对象实现相同接口 2.持有接口引用(或持有被代理对象引用) 3.在代理对象中调用被代理对象的方法,调用前后可对目标功能扩展
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类太臃肿,同时,一旦接口增加方法,目标对象与代理对象都要维护。
动态代理(JDK动态代理):不需要实现接口,代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
使用:代理类所在包:java.lang.reflect.Proxy,JDK实现代理只需要使用newProxyInstance方法,如下
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
该方法是Proxy类的静态方法,三个参数依次是
ClassLoader loader,指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h,事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
使用:在代理对象Proxy的新建代理实例方法中,必须要获得类的加载器、类所实现的接口、还有一个拦截方法的句柄。在句柄的invoke中如果不调用method.invoke则方法不会执行。在invoke前后添加通知,就是对原有类进行功能扩展了。代码如下
package Proxy.jdkProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { DAOInterface userDao = new UserDao(); DAOInterface proxy = (DAOInterface) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces() , new InvocationHandler() { //回调方法 拦截到目标对象的时候执行 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置通知"); Object o = method.invoke(userDao, args);//调用拦截到的方法 return o; } }); proxy.delete(); } }
cglib动态代理:要求类不能使final,代理对象继承目标对象,目的也是为了代理对象能直接调用目标对象的方法。
浙公网安备 33010602011771号