本文共 24655 字,大约阅读时间需要 82 分钟。
饿汉式和懒汉式都必须是要会手写的!
就是一上来就把类中的所有数据对象都给new出来了。所以饿汉式可能会存在内存浪费的情况!
package com.codeyu.designPattern.single;import java.security.PublicKey;//饿汉式单例public class Hungry { //饿汉式一上来就会把这些加载进内存。所以可能会导致内存的浪费 private Byte[] bytes1 = new Byte[1024*1024]; private Byte[] bytes2 = new Byte[1024*1024]; private Byte[] bytes3 = new Byte[1024*1024]; private Byte[] bytes4 = new Byte[1024*1024]; //构造器私有 private Hungry() { } private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance() { return HUNGRY; }}
DCL懒汉式中使用到了volatile关键字,是为了避免指令重排!
解决了懒汉式内存浪费的缺点。
package com.codeyu.designPattern.single;public class LazyMan { //单例模式一定要构造器私有 private LazyMan() { } private volatile static LazyMan lazyMan; //下述方法在单线程下确实单例OK的,但是在多线程下就不保证了! public static LazyMan getInstance() { if(lazyMan == null) { lazyMan = new LazyMan(); } return lazyMan; } //适用于多线程的单例模式 //双重检测锁模式的懒汉式单例:DCL懒汉式 public static LazyMan getInstance() { if(lazyMan == null) { synchronized (LazyMan.class) { if(lazyMan == null) { lazyMan = new LazyMan(); } } } return lazyMan; } /** 上述DCL懒汉式也不是完全绝对安全的。因为lazyMan = new LazyMan();这行代码不是原子性操作! * 它经历的过程是: * 1.分配内存空间 * 2.执行构造操作,初始化对象 * 3.将引用指向初始化后的对象内存 * 因为是三步操作,所以底层可能会经历指令重排。比如我们期望的是执行123,真实情况可能是执行132 * * 当走132时,假设A线程先执行完1和3,但是对象还没有初始化,此时线程B进来发现内存空间已经分配了,故 * 线程B会认为lazyMan!=null,线程B会直接返回lazyMan,而此时A线程还未初始化lazyMan,如果 * 此时外部代码使用该对象就会会产生问题了! * 所以为了避免指令重排,还需要在引用变量前加上一个volatile关键字。以保证其不会被指令重排 */}
但是单例模式也是可能会被破环的,可以被反射破坏:
public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); Classclazz = LazyMan.class; Constructor declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode());}//命令行打印输出:1915910607284720968//两个对象的hashcode不一样,说明单例模式被破坏
针对上述问题,仍然可以进一步改进,在私有构造函数中添加代码如下:
//单例模式一定要构造器私有private LazyMan() { synchronized (LazyMan.class){ if (lazyMan != null){ throw new RuntimeException("不要试图使用反射破坏异常!"); } }}//此时命令行会抛出异常:java.lang.RuntimeException: 不要试图使用反射破坏异常!
当然,还可以进一步又使单例模式出现问题,然后进一步也有相应的影对方法!
直接代码测试:写了一个枚举类+一个Test类。通过枚举的获得一个枚举对象,然后通过反射的方式获得一个枚举对象,看看会抱什么错?
package com.codeyu.designPattern.single;import java.lang.reflect.Constructor;public enum EnumSingle { INSTANCE; public EnumSingle getInstance() { return INSTANCE; }}class Test{ public static void main(String[] args) throws Exception{ EnumSingle instance1 = EnumSingle.INSTANCE; ClassenumClazz = EnumSingle.class; Constructor constructor = enumClazz.getDeclaredConstructor(); constructor.setAccessible(true); EnumSingle instance2 = constructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); }}//运行上述代码,报错如下所示:就是所枚举中没有无参构造方法。Exception in thread "main" java.lang.NoSuchMethodException: com.codeyu.designPattern.single.EnumSingle. ()
很明显,上述错误不是我们所期望的错误。因为我们看Constructor类的newInstance()方法如下所示:
那么按照上述的NoSuchMethodException报错去查找原因:
先去Idea中out目录下看字节码.class文件:
发现确实存在一个私有构造啊,为什么运行时idea会报错NoSuchMethodException呢。既然这样那就去反编译以下看看:
#反编译命令行指令,在.class文件所在文件夹运行下述指令:javap -p 字节码文件全名#或javap -c 字节码文件全名
idea和反编译后的文件都提示说存在私有构造,但运行代码时却提示NoSuchMethodException。可能是idea和官方提供的反编译软件骗了我们,接下来采用更专业的反编译软件。编译后发现确实不是无参构造了,而是一个继承自父类的有参构造:
修改反射获取的构造函数,然后再次测试:
class Test{ public static void main(String[] args) throws Exception{ EnumSingle instance1 = EnumSingle.INSTANCE; ClassenumClazz = EnumSingle.class; Constructor constructor = enumClazz.getDeclaredConstructor(String.class, int.class); constructor.setAccessible(true); EnumSingle instance2 = constructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); }}//运行代码后,这一期报错:Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
的确是所期望的异常!所以Enum类的单例模式不能通过反射破坏,因为反射的newInstance()方法中的源码明确指定了不能通过反射创建枚举!
简单工厂模式其内部的方法都是静态的。若要扩展,则需要去修改源代码。
package com.codeyu.designPattern.factory;public class CarFactory { public static Car getCar(String car) { if(car.equals("五菱")){ return new WuLing(); } else if(car.equals("特斯拉")){ return new TesLa(); } else { return null; } }}//上述工厂类如果想要添加新车“大众”,则需要去修改源代码!public class Consumer { public static void main(String[] args) { //工厂方法获取对象 Car car = CarFactory.getCar("五菱"); }}
简单工厂模式缺点:
如果想要添加新的产品,则必须去修改源代码!
即简单工厂模式不满足OOP中的开闭原则!
即每一个汽车品牌都有一个自己的工厂,即将车工厂抽象成接口。而简单工厂模式就相当于所有的车均由同一个工厂生产。
工厂方法模式相比于简单工厂模式,它需要创建多个工厂,需要定义的类变多了。
此时当需要添加一个新车“大众”时,我们只需要添加两个新类即可,即一个车类,一个车工厂类,而不需要去修改原来的代码,即此时满足了开闭原则,可以随时的进行动态扩展:
//车类public class DaZhong implements Car { @Override public void name() { System.out.println("大众"); }}//车工厂类public class DaZhongFactory implements CarFactory { @Override public Car getCar() { return new DaZhong(); }}
结构复杂度方面:简单工厂模式更占优势
代码复杂度方面:简单工厂模式更占优势
编程复杂度方面:简单工厂模式更占优势
管理上的复杂度方面:简单工厂模式更占优势
一般而言:还是简单工厂模式更占优势!
但:
根据设计模式来看:更推荐用工厂方法模式!满足开闭原则!
根据实际业务来看:更推荐简单工厂模式!
//=================下述为两个产品接口的定义package com.codeyu.designPattern.factory.abstract1;//手机产品接口public interface PhoneProduct { void start(); void shutDown(); void callUp(); void sendSms();}package com.codeyu.designPattern.factory.abstract1;//路由器产品接口public interface RouterProduct { void start(); void shutDown(); void openWifi(); void Setting();}//=================上述两种产品接口的实现类package com.codeyu.designPattern.factory.abstract1;//华为手机public class HuaWeiPhone implements PhoneProduct { @Override public void start() { System.out.println("开启华为手机"); } @Override public void shutDown() { System.out.println("关闭华为手机"); } @Override public void callUp() { System.out.println("华为打电话"); } @Override public void sendSms() { System.out.println("华为发短信"); }}package com.codeyu.designPattern.factory.abstract1;//小米手机public class XiaoMiPhone implements PhoneProduct { @Override public void start() { System.out.println("开启小米手机"); } @Override public void shutDown() { System.out.println("关闭小米手机"); } @Override public void callUp() { System.out.println("小米打电话"); } @Override public void sendSms() { System.out.println("小米发短信"); }}package com.codeyu.designPattern.factory.abstract1;//华为路由器public class HuaWeiRouter implements RouterProduct { @Override public void start() { System.out.println("开启华为路由器"); } @Override public void shutDown() { System.out.println("关闭华为路由器"); } @Override public void openWifi() { System.out.println("华为路由器打开wifi"); } @Override public void Setting() { System.out.println("华为路由器设置"); }}package com.codeyu.designPattern.factory.abstract1;//小米路由器public class XiaoMiRouter implements RouterProduct { @Override public void start() { System.out.println("开启小米路由器"); } @Override public void shutDown() { System.out.println("关闭小米路由器"); } @Override public void openWifi() { System.out.println("小米路由器打开wifi"); } @Override public void Setting() { System.out.println("小米路由器设置"); }}//=================下述定义抽象产品工厂package com.codeyu.designPattern.factory.abstract1;//抽象产品工厂public interface ProductFactory { //生产手机 PhoneProduct phoneProduct(); //生产路由器 RouterProduct routerProduct();}//=================下述分别定义小米工厂和华为工厂package com.codeyu.designPattern.factory.abstract1;public class HuaWeiFactory implements ProductFactory { @Override public PhoneProduct phoneProduct() { return new HuaWeiPhone(); } @Override public RouterProduct routerProduct() { return new HuaWeiRouter(); }}package com.codeyu.designPattern.factory.abstract1;public class XiaoMiFactory implements ProductFactory { @Override public PhoneProduct phoneProduct() { return new XiaoMiPhone(); } @Override public RouterProduct routerProduct() { return new XiaoMiRouter(); }}//============下述为最后的测试代码:package com.codeyu.designPattern.factory.abstract1;public class Client { public static void main(String[] args) { System.out.println("==============小米产品测试=============="); XiaoMiFactory xiaoMiFactory = new XiaoMiFactory(); PhoneProduct xiaomiPhone = xiaoMiFactory.phoneProduct(); RouterProduct xiaomiRouter = xiaoMiFactory.routerProduct(); xiaomiPhone.callUp(); xiaomiRouter.openWifi(); }}//打印输出为:==============小米产品测试==============小米打电话小米路由器打开wifi
为什么要学代理模式?因为这就是SpringAOP的底层。
而SpingAOP和SpringMVC是必问的面试考题。
代理模式分类:
角色分析:
1.接口
//租房接口public interface Rent { void rent();}
2.真实角色
//房东public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子!"); }}
3.代理角色
package com.codeyu.designPattern.proxy.static1;//中介代理,帮房东租房子//因为中介代理房东去租房子,所以它也要实现Rent接口public class Proxy implements Rent{ private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } @Override public void rent() { seeHouse(); host.rent(); contract(); Charge(); } //看房,中介帮房东做的事情,且不必劳烦房东 public void seeHouse() { System.out.println("中介带你看房!"); } //收中介费 public void Charge() { System.out.println("收中介费!"); } //签租赁合同 public void contract() { System.out.println("签租赁合同!"); }}
4.客户端访问代理角色
package com.codeyu.designPattern.proxy.static1;public class Client { public static void main(String[] args) { //没有代理模式的租房子,直接找房东 Host host = new Host(); host.rent(); //有代理模式的租房子,通过中介来租房 //代理,中介帮房东租房子,但是代理角色一般会有一些附属操作! Proxy proxy = new Proxy(host); //代理房东 proxy.rent(); }}
//假设有一个UserSrevice接口,实现增删改查功能package com.codeyu.designPattern.proxy.static1.demo02;public interface UserService { void add(); void delete(); void update(); void query();}//用一个实现类来实现UserService接口package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户!"); } @Override public void delete() { System.out.println("删除了一个用户!"); } @Override public void update() { System.out.println("修改了一个用户!"); } @Override public void query() { System.out.println("查询了一个用户!"); }}//Client类使用UserServiceImpl类来实现增删改查package com.codeyu.designPattern.proxy.static1.demo02;public class Client { public static void main(String[] args) { UserService userService = new UserServiceImpl(); userService.add(); }}//===================================//但是,假设现在想要给UserServiceImpl类中的每个方法添加日志功能//一种方式是直接去修改业务代码,将每一个方法中添加一句日志输出package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{ @Override public void add() { //日志功能 System.out.println("使用了add()方法"); System.out.println("增加了一个用户!"); } @Override public void delete() { //日志功能 System.out.println("使用了delete()方法"); System.out.println("删除了一个用户!"); } @Override public void update() { //日志功能 System.out.println("使用了update()方法"); System.out.println("修改了一个用户!"); } @Override public void query() { //日志功能 System.out.println("使用了query()方法"); System.out.println("查询了一个用户!"); }}
但是上述添加日志功能的实现修改了业务代码,我们所期望的是尽量少或者不要修改业务代码。这时,就可以通过代理的方式来实现:
//原来的业务代码只用执行业务;package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户!"); } @Override public void delete() { System.out.println("删除了一个用户!"); } @Override public void update() { System.out.println("修改了一个用户!"); } @Override public void query() { System.out.println("查询了一个用户!"); }}//通过代理方法来添加日志功能;package com.codeyu.designPattern.proxy.static1.demo02;public class UserServiceProxy implements UserService{ private UserServiceImpl userService; //通过set方法注入UserServiceImpl public void setUserService(UserServiceImpl userService) { this.userService = userService; } //日志功能使用一个日志方法来实现 public void log(String str) { System.out.println("[Debug] 使用了"+str+"方法"); } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); }}//Client演示使用方式:package com.codeyu.designPattern.proxy.static1.demo02;public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); //使用代理类 UserServiceProxy userServiceProxy = new UserServiceProxy(); userServiceProxy.setUserService(userService); userServiceProxy.add(); }}
上述使用代理方法的好处就是业务代码只用去实现业务相关的功能,日志功能的使用由代理类来完成,让代码整体的耦合度更低!
Proxy是位于java.lang.reflect
包下的类,InvocationHandler是位于java.lang.reflect
包下的接口。
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类!
Proxy作用:
作用:调用处理程序,并返回一个结果的!
//=======1.首先有一个租房接口=======package com.codeyu.designPattern.proxy.dynamic1.demo03;//租房接口public interface Rent { void rent();}//======2.然后有一个Host类实现了Rent接口=======package com.codeyu.designPattern.proxy.dynamic1.demo03;//房东public class Host implements Rent { @Override public void rent() { System.out.println("房东要出租房子!"); }}//=======3.然后我们去写一个实现了InvocationHandler接口的类=======package com.codeyu.designPattern.proxy.dynamic1.demo03;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//=========4.等会儿我们会用这个类自动生成代理类========public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成得到代理对象 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } @Override //处理代理实例,并放回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result; }}//==========5.Client的测试用例=========package com.codeyu.designPattern.proxy.dynamic1.demo03;public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理角色:现在没有 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们要调用的接口对象! pih.setRent(host); Rent proxy = (Rent)pih.getProxy(); proxy.rent(); }}//=====6.命令行打印输出:======房东要出租房子!
通过上述代码可以看出,我们的动态代理代码中并没有真实的代理解决,我们只定义了一个实现了InvocationHandler接口的处理类:ProxyInvocationHandler,该处理类就做两件事情,一个是invoke()
:即执行它真正要执行的方法;另一个是得到代理类的方法,即getProxy()
方法,改方法会返回一个代理实例。
在Client类的测试代码中,怎么去得到代理类实例并设定要代理的角色呢?
答:首先通过:
pih.setRent(host);
设定要代理的角色。
然后通过:
Rent proxy = (Rent)pih.getProxy();
**获得动态代理对象!**即动态代理实例!
然后通过代理实例来实现租房:
proxy.rent();
实际上,上述方法:proxy.rent();
实际上是调用的ProxyInvocationHandler
类中的invoke()
方法:
@Override//处理代理实例,并放回结果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result;}
只不过该invoke()
方法的内部是通过method.invoke(rent, args);
只是去调用了Rent
接口的rent()
方法,所以如果我们要实现代码的横切,即添加上:seeHouse()、fare()等方法的话,只用在上述方法中添加接口,如下:
@Override//处理代理实例,并放回结果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //看房 seeHouse(); //动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result; //收费 fare();}public void seeHouse() { System.out.println("中介带看房子!");}public void fare() { System.out.println("收中介费!");}//此时仍然通过Client类进行测试:package com.codeyu.designPattern.proxy.dynamic1.demo03;public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理角色:现在没有 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们要调用的接口对象! pih.setRent(host); //这里的porxy就是动态生成的 Rent proxy = (Rent)pih.getProxy(); proxy.rent(); }}//=====6.命令行打印输出:======中介带看房子!房东要出租房子!收中介费!
实际上核心全在ProxyInvocationHandler类中,它指明了代理谁;生成代理类对象;以及调用代理程序。在下方代码中用123标记出来:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//=========public class ProxyInvocationHandler implements InvocationHandler { //1.被代理的接口(代理谁) private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //2.生成得到代理类对象 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } @Override //3.调用代理程序 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result; }}
//======1.已知UserService接口======package com.codeyu.designPattern.proxy.static1.demo02;public interface UserService { void add(); void delete(); void update(); void query();}//======2.UserService接口的实现类======package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户!"); } @Override public void delete() { System.out.println("删除了一个用户!"); } @Override public void update() { System.out.println("修改了一个用户!"); } @Override public void query() { System.out.println("查询了一个用户!"); }}//======3.可以把下方的ProxyInvocationHandler类当作一个模板,用以处理以后需要用到代理的地方======package com.codeyu.designPattern.proxy.dynamic1.demo04;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//万能的InvocationHandler实现类模板public class ProxyInvocationHandler implements InvocationHandler { //1.代理谁 private Object target; public void setTarget(Object target) { this.target = target; } //2.生成得到代理类对象 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override //3.处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质就是使用反射机制实现! Object result = method.invoke(target, args); return result; }}//======4.Client类进行测试======package com.codeyu.designPattern.proxy.dynamic1.demo04;import com.codeyu.designPattern.proxy.static1.demo02.UserService;import com.codeyu.designPattern.proxy.static1.demo02.UserServiceImpl;import java.lang.reflect.InvocationHandler;public class Client { public static void main(String[] args) { //真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色:通过ProxyInvocationHandler类中的方法来获取 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设定要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService)pih.getProxy(); //使用代理来完成业务 proxy.add(); }}//======5.命令行打印输出======增加了一个用户!
可以将上述的ProxyInvocationHandler
类当作一个工具类,用以初一其他的需要用到代理的地方!
接下来还是实现之前关于UserServiceImpl类中的方法所说到的一个功能,即调用每一个方法时,输出一个日志,那么可以按照下述方式进行:
//万能的InvocationHandler实现类模板public class ProxyInvocationHandler implements InvocationHandler { //1.代理谁 private Object target; public void setTarget(Object target) { this.target = target; } //2.生成得到代理类对象 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override //3.处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); //动态代理的本质就是使用反射机制实现! Object result = method.invoke(target, args); return result; } public void log(String msg) { System.out.println("调用了"+msg+"方法"); }}//执行下述方法;public class Client { public static void main(String[] args) { //真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色:通过ProxyInvocationHandler类中的方法来获取 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设定要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService)pih.getProxy(); //使用代理来完成业务 proxy.add(); }}//打印如数日志:调用了add方法增加了一个用户! //执行下述方法;public class Client { public static void main(String[] args) { //真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色:通过ProxyInvocationHandler类中的方法来获取 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设定要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService)pih.getProxy(); //使用代理来完成业务 proxy.delete(); }}//打印如数日志:调用了delete方法增加了一个用户!
上述也是通过反射的方式去获得的方法名!
转载地址:http://dqozi.baihongyu.com/