前言
Java设计模式
一、单例模式
饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
这是实现一个安全的单例模式的最简单粗暴的写法,这种实现方式我们称之为饿汉式。之所以称之为饿汉式,是因为肚子很饿了,想马上吃到东西,不想等待生产时间。这种写法,在类被加载的时候就把Singleton实例给创建出来了。 饿汉式的缺点就是,可能在还不需要此实例的时候就已经把实例创建出来了,没起到lazy loading的效果。优点就是实现简单,而且安全可靠。
懒汉式
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
相比饿汉式,懒汉式显得没那么“饿”,在真正需要的时候再去创建实例。在getInstance方法中,先判断实例是否为空再决定是否去创建实例,看起来似乎很完美,但是存在线程安全问题。在并发获取实例的时候,可能会存在构建了多个实例的情况。所以,需要对此代码进行下改进。
public class SingletonSafe {
private static volatile SingletonSafe singleton;
private SingletonSafe() {
}
public static SingletonSafe getSingleton() {
if (singleton == null) {
synchronized (SingletonSafe.class) {
if (singleton == null) {
singleton = new SingletonSafe();
}
}
}
return singleton;
}
}
这里采用了双重校验的方式,对懒汉式单例模式做了线程安全处理。通过加锁,可以保证同时只有一个线程走到第二个判空代码中去,这样保证了只创建 一个实例。这里还用到了volatile关键字来修饰singleton,其最关键的作用是防止指令重排。
枚举
在effective java(这本书真的很棒)中说道,最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
============================
public class Main {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
静态内部类
public class Singleton {
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
二、代理模式
语雀:Java静态代理和动态代理(JDK、cglib)
1、Java静态代理
代码是写死的。
以相亲(blinddate)为例:
1.定义一个接口
interface Subject{
public void blindDate(String s);
}
2.定义委托类
class Person implements Subject{
public void blindDate(String s){
System.out.println("i am blinddating with "+s);
}
}
3.定义代理
class DatgingAgency implments Subject{
private Subject target;
public DatgingAgency(Subject target){
this.target=target;
}
public String beforeBlindDate(String s){
return s;
}
public void blindDate(String s){
String s=beforeBlindDate(String s);
target.blindDate(s);
}
}
4.测试
public static void main(args[]){
Person p=new Person();
DatingAgency da=new DatingAgency(p);
da.blindDate("志林姐姐");
}
总结:静态代理,说白了就是代理类已经写死了,程序在运行期需要加载代理类时,将事先在本地编译(或者其他方式)好的class文件加载到JVM供虚拟机使用。静态代理需要开发者自己去实现。
2、JDK动态代理
源于Java设计模式中的代理模式。 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 如果是纯代理模式的案例,那么代理对象WanMeiLiProxy实现的是Gril接口。 为什么使用代理模式(什么场景会使用代理模式) 添加日志、判断权限、事务、、、、、、
反射技术
动态代理运用了大量反射机制,首先简单看看反射的运用:
JDK动态代理示例
代理的是接口。
(1)定义接口
public interface Girl {
void date();
void watchMovie();
}
(2)真实对象
定义委托类,委托类是真正的执行者
public class WangMeiLi implements Girl{
@Override
public void date() {
System.out.println("我是王美丽");
}
@Override
public void watchMovie() {
System.out.println("喜欢看电影");
}
}
(3)代理对象(处理器对象)
首先介绍Java中关于动态代理的两个主要接口和类; java.lang.reflect.Proxy[?prɑ?ksi] :这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。 // 最主要的方法:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。 // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象 // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行 Object invoke(Object proxy, Method method, Object[] args)
定义一个实现了InvocationHandler接口的类,这个类须绑定目标对象(即委托类),并利用Proxy的静态方法返回动态代理类的实例
public class WanMeiLiProxy implements InvocationHandler{
Girl girl;
public WanMeiLiProxy(Girl girl) {
this.girl = girl;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
method.invoke(girl, args);
System.out.println("后置增强");
return null;
}
}
(4)调用端
public class ProxyTest {
public static void main(String[] args) {
WangMeiLi wangMeiLi = new WangMeiLi();
WanMeiLiProxy wanMeiLiProxy = new WanMeiLiProxy(wangMeiLi);
Girl girl = (Girl) Proxy.newProxyInstance(wangMeiLi.getClass().getClassLoader(), wangMeiLi.getClass().getInterfaces(), wanMeiLiProxy);
girl.date();
}
}
3、Cglib动态代理
Cglib动态代理是针对代理的类, 动态生成一个子类, 然后子类覆盖代理类中的方法, 如果是private或是final类修饰的方法,则不会被重写。 CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
|