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.抽象工厂模式:产品 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,代理对象继承目标对象,目的也是为了代理对象能直接调用目标对象的方法。

 

posted on 2019-02-18 11:34  audience7510  阅读(107)  评论(0)    收藏  举报