1. 单例模式
饿汉模式
/*
* 单例模式一:饿汉式(雏形)
* */
public class Singleton1 {
//使用private修饰,其他的类不能直接使用该变量
private static Singleton1 instance = new Singleton1();
//private修饰的构造函数,其他类不能直接调用构造函数创建一个该类的对象
private Singleton1(){
}
/*
*1.提供一个public修饰的方法,用于获取这个唯一被创建出来的变量
*2.方法为什么是静态的?
*不能new对象却想调用类方法,方法必然是静态的,静态方法只能调用静态成员,所以对象也是静态的
*3.为什么对象的访问修饰符是private?
*因为如果对象修饰符是public,那么调用Single1.instance也可以得到该对象,这样就造成了不可控
*/
public static Singleton1 getInstance(){
return instance;
}
}
懒汉式
package Singleton;
/*
* 单例模式二:懒汉式
* 懒汉式和饿汉式相比的区别就是懒汉式创建了延迟对象,但是在多线程编程中,使用懒汉式可能会造成类的对象在内存中不唯一。
*
* 懒汉式在多线程中出现的问题:
* 懒汉式由于多加了一次判断:if(instance==null),导致了线程安全性隐患。因为CPU很有可能在执行完if语句之后切向其它线程。
* 解决线程安全性问题的关键就是加上同步锁。
* 但是直接使用同步函数的方法效率十分低下,因为每次调用此方法都需要先判断锁。
* */
public class Singleton2 {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
private static Singleton2 instance = null;
//构造器私有化,禁止创建新的对象
private Singleton2(){
}
//方法同步,调用效率低
public static synchronized Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
2. 代理模式
- Proxy 是什么 ?
为其他对象提供一种代理以控制对这个对象的访问。
- Subject 类,定义了 RealSubject 和 Proxy 共用接口,这样就可以在任何使用 RealSubject 的地方使用 Proxy。
- RealSubject 类,定义 Proxy 所代表的真实实体。
- Proxy 类,保存一个引用使得代理可以访问实体,并提供一个与 Subject 的接口相同的接口,这样代理就可以用来代替实体了。
-
从举例中我们可以看出 ,由于代理与被代理实现了相同的接口,因此代理模式可以代理对象的方法,而代理模式的一个用处也就显而易见了,代理模式可以有选择的暴露对象的接口,而对访问者屏蔽一些接口,这样就实现了对对象的控制访问。
代理模式 -
代理模式的应用
- 远程代理
- 虚拟代理
- 保护代理
- 智能代理
动态代理
在静态代理中,一个代理只能代理一种类型,而且在编译阶段就确定了被代理对象。而动态代理是在运行时,通过反射实现代理,能够代理任何类型的对象。与静态代理的不同就是在代理类中不直接包含被代理对象,而是包含一个 InvocationHandler,该 InvocationHandler包含一个被代理对象,并负责分发请求给被代理对象。
一个简单的例子
首先我们需要定义一个接口:
public interface UserService {
void query();
}
然后实现这个接口:
public class UserServiceImpl implements UserService {
public void query() {
System.out.println("查询用户信息");
}
}
定义一个类,需要实现InvocationHandler:
public class MyInvocationHandler implements InvocationHandler {
Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入了invoke");
method.invoke(target);
System.out.println("执行了invoke");
return null;
}
}
然后就是Main方法了:
public class Main {
public static void main(String[] args) {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(new UserServiceImpl());
Object o = Proxy.newProxyInstance(Main.class.getClassLoader(),
new Class[]{UserService.class}
, myInvocationHandler);
((UserService)o).query();
}
}
底层原理
首先分析代理对象产生过程,最后分析代理对象的产生后的样子
产生过程:
JDK动态代理
动态代理就是要生成一个包装类对象,由于代理对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是包含一个InvocationHandler,该 InvocationHandler包含一个被代理对象,并负责分发请求给被代理对象,分发前后均可做增强。从原理可以看出,JDK 动态代理是”对象“的代理。
下面看下动态代理类到底如何调用的InvocationHandler的,为什InvocationHandler的一个invoke方法能为分发target的所有方法。我们不禁的想,如果我们可以自动生成一个类,去调用MyInvocationHandler中的invoke方法是不是就可以实现动态代理了。
3. 装饰者模式
-
装饰模式:动态给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活。
-
装饰者模式的优点
我们知道一个类的功能扩展我们可以通过继承的方式重写父类的方法来达到增强功能。那我们来看下这两种方式:
(1)类的继承(高耦合,会产生更多的子类,从而引起类的爆炸)
(2)对象组合即装饰模式(降耦,不会创造更多的子类),动态为对象添加功能,所以类应该对扩展开放,对修改关闭。 -
装饰者的特点
(1)装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2)装饰对象包含一个真实对象的引用。
(3)装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4)装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。 -
参与者
(1)Component(抽象组件又叫被装饰对象的基类)
定义一个对象接口,可以给这些对象动态地添加职责。
(2)ConcreteComponent(具体组件又叫具体被装饰对象)
定义一个对象,可以给这个对象添加一些职责。
(3) Decorator(装饰者抽象类)
维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。
(4) ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
4. 工厂模式
4.1 简单工厂
它的主要特点是需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类。有点抽象,举个例子就明白了。有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号的处理器核。客户需要什么样的处理器核,一定要显示地告诉生产工厂。下面给出一种实现方案。
4.2 工厂方法
所谓工厂方法模式,是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
听起来很抽象,还是以刚才的例子解释。这家生产处理器核的产家赚了不少钱,于是决定再开设一个工厂专门用来生产B型号的单核,而原来的工厂专门用来生产A型号的单核。这时,客户要做的是找好工厂,比如要A型号的核,就找A工厂要;否则找B工厂要,不再需要告诉工厂具体要什么型号的处理器核了。下面给出一个实现方案。
4.3 抽象工厂
工厂方法模式适用于产品种类结构单一的场合,为一类产品提供创建的接口;而抽象工厂方法适用于产品种类结构多的场合,主要用于创建一组(有多个种类)相关的产品,为它们提供创建的接口;就是当具有多个抽象角色时,抽象工厂便可以派上用场。
JDK 动态代理
- 实现 InvocationHandler 接口
- 重写 invoke() 方法
只需一个动态代理类,可以代理任何对象
(1)其三个参数分别是:ClassLoader,指定的接口及我们自己定义,InvocationHandler类。
(2)通过invoke执行代理类中的目标方法doSomething