设计模式:是一种思维方式,组织类的方式。
- 单例模式: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;
}
}
}