单例模式是设计模式中最简单的一种设计模式,常用于数据库连接池、Spring默认的bean等
单例模式是一种创建型模型,这个词我们后面还会提到。
一个类负责创建自己的对象,但是需要确保只有一个对象被创建了,这个类提供了一种访问它对象的唯一方式。同时需要隐藏自己的构造方法。
一般单例模式都是支持两种实现思路,一种是饿汉式,一种是懒汉式,饿汉式就是在类加载时就实例化一个对象,而懒汉式则是需要第一次使用的时候才实例化对象,但是懒汉式的实现方式一般需要通过双重校验也就是double cheack的方式来实现。
饿汉式的实现是比较简单的,也就是在类加载的时候就实例化对象,但是存在一个问题,这个类加载了但是我没有使用,会有额外的空间浪费。
public class SingletonHungry {
private static final SingletonHungry singletonHungry = new SingletonHungry();
private SingletonHungry(){}
public static SingletonHungry getSingletonHungry(){
return singletonHungry;
}
}
public class Singleton {
/**
* 该成员变量也就是唯一实例化的对象
*/
private static volatile Singleton singleton;
/**
* 同时为了防止被初始化,需要将构造函数私有化
*/
private Singleton(){}
/**
* 提供静态方法用来获取唯一单例
* @return 单例对象
*/
public static Singleton getSingleton(){
if(singleton==null){
/*这里需要使用类级别的锁而不是直接锁住singleton的原因是
*如果singleton==null,那么就会抛出NullPointerException
*异常原因是:Cannot enter synchronized block because
*"Singleton.singleton" is null
*/
synchronized (Singleton.class){
if (singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1==singleton2);
}
}
代理模式的核心是,允许通过代理对象控制对被代理对象的访问。主要用于在访问对象时添加额外的功能。
代理模式在Java中可以分为静态代理和动态代理,本质上的目的都是一致的。
只不过静态代理需要手动编写代理类,代理类需要实现与目标相同的接口,在内部需要持有一个目标对象的引用。
动态代理则是运行时动态生成的,不需要为每个对象手动编写代理类,但是目标对象至少必须实现一个接口。
接下来我们就来尝试以对方法加log的任务为例,实现两种代理。
两种代码都需要有一个接口和对接口的实现,所以在这里定义一个接口和并给出它的一个实现。
其实写到动态代理那块的时候,我才意识到这个Method接口的取名并不太妙,因为Java反射包里就有一个类叫Method,但是事已至此,实在是懒得改了,就先这样。
package Proxy;
public interface Method {
public void method();
}
package Proxy;
import java.util.concurrent.TimeUnit;
public class MethodImpl implements Method{
@Override
public void method() {
System.out.println("Dealing with method");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Finished dealing");
}
}
这时候我们就实现一个方法,来代理这个MethodImpl,目的是要给它加上执行时间的log,当然为了演示简单起见,我们直接打印在控制台上了
package Proxy;
import java.util.Date;
public class StaticProxy implements Method{
private Method target;
public StaticProxy(Method target){
this.target = target;
}
@Override
public void method() {
Date dateStart = new Date();
target.method();
Date dateFinished = new Date();
long timeCost = dateFinished.getTime()-dateStart.getTime();
System.out.println("Cost time:"+timeCost);
}
public static void main(String[] args) {
Method staticProxy = new StaticProxy(new MethodImpl());
staticProxy.method();
}
}
静态代理其实存在一个问题,灵活性较低,因为每当需要为一个新的服务类型提供代理时,都需要手动去编写相应的代理类。
但是Java为我们提供了动态代理类的实现方式。
动态代理的实现其实是依赖Java反射包下的Proxy
类的newProxyInstance()
方法,需要实现一个InvocationHandler
接口,并重写invoke
方法
package Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Date;
public class MyDynamicProxy {
static class MyDynamicProxyHandler implements InvocationHandler{
Object target;
public MyDynamicProxyHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
Date dateStart = new Date();
Object res = method.invoke(target,args);
Date dateFinished = new Date();
long timeCost = dateFinished.getTime()-dateStart.getTime();
System.out.println("Cost time:"+timeCost);
return res;
}
}
public static Object getProxy(Object method){
return Proxy.newProxyInstance(
method.getClass().getClassLoader(), method.getClass().getInterfaces(),new MyDynamicProxyHandler(method)
);
}
public static void main(String[] args) {
Method staticProxy = (Method) MyDynamicProxy.getProxy(new MethodImpl());
staticProxy.method();
}
}
装饰者模式与代理模式看起来似乎有一点相似,但是两者的侧重点不同。
我们做一个简单的实现就可以理解了。
首先还是定义一个接口,我希望它能提供method方法
package Decorate;
public interface MyMethod {
public void method();
}
然后是对它的实现
package Decorate;
public class OriginMethod implements MyMethod{
@Override
public void method() {
System.out.println("It is the origin method");
}
}
接下来我们要实现装饰器,首先是装饰器要继承原始接口
package Decorate;
public interface MyMethodDecorate extends MyMethod{
}
然后实现装饰器1
package Decorate;
public class DecorateMethod implements MyMethodDecorate{
MyMethod originMethod;
public DecorateMethod(MyMethod originMethod){
this.originMethod = originMethod;
}
@Override
public void method() {
System.out.println("I decorate it with first decoration!");
originMethod.method();
}
}
然后实现装饰器2并调用
package Decorate;
public class DecorateMethod2 implements MyMethodDecorate{
MyMethod originMethod;
public DecorateMethod2(MyMethod originMethod){
this.originMethod = originMethod;
}
@Override
public void method() {
System.out.println("I decorate it with second decoration!");
originMethod.method();
}
public static void main(String[] args) {
MyMethod originMethod = new OriginMethod();
originMethod = new DecorateMethod(originMethod);
originMethod = new DecorateMethod2(originMethod);
originMethod.method();
}
}
I decorate it with second decoration!
I decorate it with first decoration!
It is the origin method
装饰器模式和代理模式的核心差距就在于,装饰器把它装饰成一个功能更多的,更加多样化的内容,而代理模式的核心是控制对代理方法的访问。