## 本文章仅适用于自我学习,若发生侵权,call我,删
Java设计模式
1. 目的
(1)是为了让程序具有更好的代码重用性 (即:相同功能的代码,不用多次编写)
(2)可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
(3)可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
(4)可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
(5)使程序具有高内聚低耦合的特性
**
1、 策略模式
含义:定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换
如图所示:
其中,Context是上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用;Strategy是策略类,用于定义所有支持算法的公共接口;ConcreteStrategy是具体策略类,封装了具体的算法或行为,继承于Strategy。
代码
**
在这里插入代码片
```/**
*
*/
/*1. Context上下文,
Context上下文角色,也叫Context封装角色,
起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,
封装可能存在的变化。*/
public class Context {
Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
//上下文接口
public void contextInterface() {
strategy.algorithmInterface();
}
}
//策略角色(StrategyInterface)
public interface StrategyInterface {
//定义一个方法策略
public void operate();
}
//具体策略角色
public class StrategyImp implements StrategyInterface{
public void operate() {
System.out.println("找乔国老帮忙,给吴国施加压力");
}
}
//实施的角色
public class StrategyMain {
public static void main(String[] args) {
Context context ;
System.out.println("---------刚到吴国拆第一个-----------");
context = new Context(new StrategyImp());
context.operate();//执行
System.out.println("\n\n\n\n\n\n\n\n\n\n");
System.out.println("---------刘备乐不思蜀了,拆第二个---------");
context = new Context(new StrategyImpTwo());
context.operate();
System.out.println("\n\n\n\n\n\n\n\n\n\n");
System.out.println("------------孙权的小兵追来了,咋办?拆第三个---------");
context = new Context(new StrategyImpThree());
context.operate();
}
}
2、代理模式
内容:提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。代理模式有三种形式,第一种静态代理,第二种动态代理,第三种通过Cglib实现代理模式。
1. 静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
- 代码实现
public interface IUserDao {
void save();
}
public class UserDao implements IUserDao{
/* (non-Javadoc)
* @see ProxyPatterns.IUserDao#save()
*/
public void save() {
// TODO Auto-generated method stub
System.out.println("已经保存数据");
}
}
public class UserDaoProxy {
private IUserDao iUserDao;
public UserDaoProxy(IUserDao iUserDao){
this.iUserDao = iUserDao;
}
public void save(){
System.out.println("开始事务...");
iUserDao.save();
System.out.println("提交事务");
}
}
public class UserDaoTest {
public static void main(String[] args) {
UserDaoProxy userDaoProxy;
userDaoProxy = new UserDaoProxy(new UserDao());
userDaoProxy.save();
}
}
**总结:**可以做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护.。
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式
动态代理模式
特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
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:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
- 代码表示:
public class UserDao implements IUserDao{
/* (non-Javadoc)
* @see ProxyPatterns.IUserDao#save()
*/
public void save() {
// TODO Auto-generated method stub
System.out.println("已经保存数据");
}
}
public class ProxyFactory {
private Object target;
//维护一个目标对象
public ProxyFactory(Object target) {
// TODO Auto-generated constructor stub
this.target = target;
}
//给目标对象生成代理对象
public Object getInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
});
}
}
public class UserDaoProxyTest {
public static void main(String[] args) {
// 目标对象
IUserDao target = new UserDao();
// 【原始的类型 class cn.itcast.b_dynamic.UserDao】
System.out.println(target.getClass());
// 给目标对象,创建代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getInstance();
// class $Proxy0 内存中动态生成的代理对象
System.out.println(proxy.getClass());
proxy.save();
}
}
通过Cglib实现代理模式
- 内容:
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
- Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring
AOP和synaop,为他们提供方法的interception(拦截) - Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
/**
* 目标对象,没有实现任何接口
*/
public class UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
/**
* 测试类
*/
public class Test {
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}
3、单例模式
内容:
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
缺点:即使单例没被用到也会创建,浪费内存
- 代码演示
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return instance;
}
}
关键点:
- 构造函数不对外开放,一般为private。
- 通过一个静态方法或者枚举返回单例类对象。
- 确保单例类的对象有且只有一个,尤其在多线程情况下。
- 确保单例类对象在反序列化时不会重新构建对象
实现方式:分为5种
饿汉、懒汉、双重校验锁、静态内部类和枚举
- (1)饿汉模式
代码实现
public class Singleton {
private static Singleton singleton = null;
static{
singleton = new Singleton();
}
private Singleton(){
}
public static Singleton getInstance() {
return singleton;
}
}
- (2)懒汉模式
代码实现
public class LazySingleton {
private static LazySingleton singleton;
public LazySingleton() {
// TODO Auto-generated constructor stub
}
public static synchronized LazySingleton getInstance(){
if(singleton == null){
singleton = new LazySingleton();
}
return singleton;
}
}
- synchronized关键字保证了同步,在多线程情况下单例的唯一性。
- 存在问题:即使instance已经存在,每次调用getInstance依然会进行同步,这样就会消耗不必要的资源。
总结:懒汉模式的优点是只有在使用时才会实例化单例对象,在一定程度上节约了资源;缺点是第一次加载时需要进行实例化,反应稍慢;最大问题是每次调用都会进行同步吗,造成不必要的同步开销。这种模式一般不建议使用。
- (3)双重校验锁
代码实现
public class DLSingleton {
private static DLSingleton singleton;
//Double Check Lock单例模式
//懒汉模式的改进
//但仍然存在隐患
public DLSingleton() {
// TODO Auto-generated constructor stub
}
public static DLSingleton getInstance(){
if(singleton == null){//第一层判断主要是为了避免不必要的同步
synchronized(DLSingleton.class){
if (singleton == null) {//第二层判空是为了在null情况下创建实例
singleton = new DLSingleton();
}
}
}
return singleton;
}
}
问题:但由于java编译器允许处理器乱序执行,上述顺序2、3是不能保证的,可能是1-2-3也可能是1-3-2;如果是后者,3执行了已经非空,再走2会出现问题,这就是DCL失效
- 解决: volatile关键字
private volatile static DLSingleton instance = null;
总结:
优点:
- 资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。
缺点:
-
第一次加载时反应稍慢;由于java内存模型的原因偶尔会失败,在高并发环境下也有一定的缺陷,虽然概率很小。
DCL模式是使用最多的单例实现方式 -
代码实现
public class InnerSingleton {
public InnerSingleton() {
// TODO Auto-generated constructor stub
}
public static InnerSingleton getInstance(){
return InnerSingleton.getInstance();
}
private static class InnerSingletonHolder{
private static final InnerSingleton instance = new InnerSingleton();
}
}
总结:
第一次加载InnerSingleton类时并不会初始化instance,只有在第一次调用InnerSingleton的getInstance方法时才会导致instance被初始化。因此,第一次调用getInstance方法会导致虚拟机加载InnerSingleton类,这种方法不仅能保证线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,所以这也是一种推荐的单例模式实现方法
- (5)枚举
代码实现
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
//do sth ...
}
}
----------------先写到这,下次有时间再总结---------