java基础 day-17 设计模式(单例,工厂,代理),静态动态代理,数据库连接池(动态代理)

设计模式:是一种思维方式,组织类的方式。

  • 单例模式:Singleton模式。指一个类,仅能创建唯一实例。
  • 实现
  • 使用类变量,提前创建类的实例,饿汉式-不管用不用到,都会在内存中占据空间。线程安全的。
    一上来我就把对象给new好了,来了可以直接“吃” 了
package javaPro1;

public class Single {
    //1、私有化构造函数
	private Single() {

	}

	// 2、new一个Single对象
	private static final Single SINGLE = new Single();

	// 3、创建一个方法,让外面的类可以拿到对象的引用
	public static Single getInstance() {
		return SINGLE;
	}
}


package javaPro1;

public class Single {
	//线程安全的
	private Single() {

	}	
	public static Single getInstance() {

		return SingleHolder.SINGLE;
	}
	private  static class SingleHolder{
		private static final Single SINGLE = new Single();
	}
	
}

  • 懒汉式:不会在内存中创建空间,返回唯一变量时,才进行创建。非线程安全的。
    (要是有人问单例的延迟加载方式值得就是这种方式)一开始我不给你new对象,你来找我,我再给你new对象。
package javaPro1;

public class Single {
    //1、私有化构造函数
	private Single() {

	}

	// 2、声明一个对象的引用
	private static Single SINGLE;

	// 3、判断一下,如果对象为null,创建对象
	public static Single getInstance() {
		if(SINGLE == null) {
			SINGLE = new Single();
		}
		return SINGLE;
	}
}
  • 懒汉式,保证线程安全-双检锁
package javaPro1;

public class Single {
    //1、私有化构造函数
	private Single() {

	}

	// 2、声明一个对象的引用
	private volatile static Single SINGLE;

	// 3、判断一下,如果对象为null,创建对象
	public static Single getInstance() {
		//保证线程安全
		if(SINGLE == null) {
			synchronized (SINGLE) {
				//双检锁
				if(SINGLE == null) {
						SINGLE = new Single();
				}
			
			}			
		}
		return SINGLE;
	}
}

  • 测试类
package javaPro1;

public class Test1 {
	public static void main(String[] args) {
		
		Single m1 = Single.getInstance();
		Single m2 = Single.getInstance();
		System.out.println(m1 == m2);//true
	}
}

以下内容参考自菜鸟教程

  • 工厂模式:创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
  • 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
  • 主要解决:主要解决接口选择的问题。
  • 何时使用:我们明确地计划不同条件下创建不同实例时。
  • 如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
  • 关键代码:创建过程在其子类执行
  • 实现在这里插入图片描述
  • 步骤 1、创建一个接口:
public interface Shape {
   void draw();
}
  • 步骤 2、创建实现接口的实体类。
public class Rectangle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
public class Square implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
public class Circle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}
  • 步骤3、创建一个工厂,生成基于给定信息的实体类的对象。
public class ShapeFactory {
    
   //使用 getShape 方法获取形状类型的对象
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}
  • 步骤4、使用该工厂,通过传递类型信息来获取实体类的对象。
public class FactoryPatternDemo {
 
   public static void main(String[] args) {
      ShapeFactory shapeFactory = new ShapeFactory();
 
      //获取 Circle 的对象,并调用它的 draw 方法
      Shape shape1 = shapeFactory.getShape("CIRCLE");
 
      //调用 Circle 的 draw 方法
      shape1.draw();
 
      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
 
      //调用 Rectangle 的 draw 方法
      shape2.draw();
 
      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");
 
      //调用 Square 的 draw 方法
      shape3.draw();
   }
}
  • 步骤5、执行程序,输出结果:

在这里插入图片描述

  • 代理模式:在代理模式(Proxy Pattern)中,某个类代理另-一个类的全部或部分操作,代理的过程中,有可能对目标类的职责进行功能的改变,校验,或增强。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
  • 意图:为其他对象提供一种代理以控制对这个对象的访问。
  • 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
  • 何时使用:想在访问一个类时做一些控制。
  • 如何解决:增加中间层。
  • 关键代码:实现与被代理类组合。
  • 应用实例: 1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。
  • 实现
    在这里插入图片描述
  • 步骤1、创建一个接口
public interface Image {
   void display();
}
  • 步骤2、创建实现接口的实体类。
public class RealImage implements Image {
 
   private String fileName;
 
   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
 
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
 
   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }

  • 步骤3、当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。
public class ProxyPatternDemo {
   
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");
 
      // 图像将从磁盘加载
      image.display(); 
      System.out.println("");
      // 图像不需要从磁盘加载
      image.display();  
   }
}
  • 步骤4、执行程序,输出结果
    在这里插入图片描述

静态代理

以下内容参考自博客园周有才

  • 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
  • 示例:我们平常去电影院看电影的时候,在电影开始的阶段是不是经常会放广告呢?电影是电影公司委托给影院进行播放的,但是影院可以在播放电影的时候,产生一些自己的经济收益,比如卖爆米花、可乐等,然后在影片开始结束时播放一些广告。
  • 现在用代码来进行模拟。
  • 首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 Movie,代表电影播放的能力。
package com.pro2;

public interface Movie {
	void play();
}

  • 然后,我们要有一个真正的实现这个 Movie 接口的类,和一个只是实现接口的代理类。这个表示真正的影片。它实现了 Movie 接口,play() 方法调用时,影片就开始播放。那么 Proxy 代理呢?
package com.pro2;

public class RealMovie implements Movie {

	@Override
	public void play() {
		System.out.println("您正在观看的电影为:肖申克的救赎!");
		
	}

}

  • Cinema 就是 Proxy 代理对象,它有一个 play() 方法。不过调用 play() 方法时,它进行了一些相关利益的处理,那就是广告。现在,我们编写测试代码。
package com.pro2;

public class Cinema implements Movie {
	RealMovie realMovie;

	public Cinema(RealMovie realMovie) {
		super();
		this.realMovie = realMovie;
	}

	@Override
	public void play() {
		guanggao(true);
		realMovie.play();
	    guanggao(false);
	}

	public void guanggao(boolean isStart) {
		if (isStart) {
			System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
		} else {
			System.out.println("电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!");
		}
	}

}

  • 测试代码
package com.pro2;

public class ProxyTest {
	public static void main(String[] args) {
        RealMovie realmovie = new RealMovie();

        Movie movie = new Cinema(realmovie);

        movie.play();
	}
}

  • 输出结果:
    在这里插入图片描述

动态代理

  • 既然是代理,那么它与静态代理的功能与目的是没有区别的,唯一有区别的就是动态与静态的差别。
  • 那么在动态代理的中这个动态体现在什么地方?
    上一节代码中 Cinema 类是代理,我们需要手动编写代码让 Cinema 实现 Movie 接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 Movie 接口的代理,而不需要去定义 Cinema 这个类。这就是它被称为动态的原因。
  • 示例:假设有一个大商场,商场有很多的柜台,有一个柜台卖茅台酒。我们进行代码的模拟。
  • 卖酒的一个接口
package com.pro3;

public interface SellWine {

    void sellwine();

}

  • 实现接口,卖茅台酒
package com.pro3;

public class MaotaiJiu implements SellWine{

	public void sellwine() {
		System.out.println("我卖的是茅台酒!");
		
	}

}

  • 代理可对其内容做修改和补充
package com.pro3;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {

	    public static void main(String[] args) {
	        SellWine a1 = new MaotaiJiu();

	        // 可以创建动态代理
	        SellWine a2 = (SellWine)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
	            new Class<?>[] {SellWine.class}, new InvocationHandler() {
	                // 拦截所代理的类的行为
	                @Override
	                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	                    System.out.println("动态代理可添加,在卖酒动作前");
	                    // a1.sellwine();
	                    method.invoke(a1, args);
	                    System.out.println("动态代理可添加,在卖酒动作后");
	                    return null;
	                }
	            });
	        a2.sellwine();
	    }
	
}

  • 语法
  • Proxy
/*
loader 自然是类加载器,一般写作:Thread.currentThread().getContextClassLoader()
interfaces 代码要用来代理的接口
h 一个 InvocationHandler 对象
*/
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • InvocationHandler类
    InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
  • invoke() 方法
    在这里插入图片描述

数据库连接池(使用动态代理)

package com.situ.chapter5;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;

/**
 * 数据库连接池
 * 
 */
public class ConnectionPool {
    private static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";
    private final String driver;
    private final String url;
    private final String username;
    private final String password;
    private final int initialSize;// 初始化连接数
    private int maxConnections = 15;// 最大连接数
    private List<PooledConnection> connections = new ArrayList<>();// 存储所有数据库连接

    public List<PooledConnection> getConnections() {
        return connections;
    }

    public ConnectionPool(String driver, String url, String username, String password, int initialSize) {
        super();
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
        this.initialSize = initialSize;

        // 初始化连接池
        initPool();
    }

    public ConnectionPool(String url, String username, String password, int initialSize) {
        this(MYSQL_DRIVER, url, username, password, initialSize);
    }

    public int getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    /**
     * 初始化连接池
     */
    private void initPool() {
        for (int i = 0; i < initialSize; i++) {
            // 原始连接
            Connection conn = buildConnection();
            // 池连接
            PooledConnection pc = new PooledConnection();
            // 创建代理
            conn = buildConnectionProxy(pc);
            pc.setConnection(conn);

            // 存储连接的代理实例
            connections.add(pc);
        }
    }

    /**
     * 创建数据库连接
     * 
     * @return
     */
    private Connection buildConnection() {
        try {
            Class.forName(driver);
            return DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            throw new RuntimeException("获取数据库连接失败,请确认后再试,或联系管理员", e);
        }
    }

    /**
     * 对某一个连接创建对应的代理
     * 
     * @param conn
     * @return
     */
    private Connection buildConnectionProxy(PooledConnection pc) {
        Connection result = (Connection)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
            new Class<?>[] {Connection.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (method.getName().equals("close")) {
                        pc.setUsed(false);// 表示释放,不再使用
                        return null;
                    } else {
                        return method.invoke(pc.getConnection(), args);
                    }
                }
            });
        return result;
    }

    /**
     * 获取数据库连接
     * 
     * @return
     */
    public Connection getConnection() {
        for (PooledConnection pc : connections) {
            if (!pc.isUsed()) {
                pc.setUsed(true);// 设置成使用状态
                return pc.getConnection();
            }
        }

        if (connections.size() < this.maxConnections) {
            Connection conn = buildConnection();
            PooledConnection pc = new PooledConnection();
            conn = buildConnectionProxy(pc);
            pc.setConnection(conn);
            connections.add(pc);

            pc.setUsed(true);
            return pc.getConnection();
        }

        throw new RuntimeException("连接池已用完");
    }

    /**
     * 维护一个是否使用状态的连接
     * 
     * @author snow1k
     * @date 2022/04/06
     */
    public static class PooledConnection {
        private Connection connection;
        private boolean used = false;

        public boolean isUsed() {
            return used;
        }

        public void setUsed(boolean used) {
            this.used = used;
        }

        public Connection getConnection() {
            return connection;
        }

        public void setConnection(Connection connection) {
            this.connection = connection;
        }

    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值