参考文章:
Cyc2018-设计模式
六种简单的单例设计模式
JAVA设计模式之单例模式在开发中的最常用实现方式
简单工厂模式、工厂模式以及抽象工厂模式(具体)
JAVA设计模式之抽象工厂模式
Java动态代理的两种实现方法
动态代理的两种方式以及区别
设计模式的作用:
需求经常变更,而设计模式的存在就是为了抵御需求变更。
1、单例模式
枚举单例模式如何防止反射攻击
单例模式存在的问题
1、初始化的线程安全问题
2、反序列化破坏单例
ObjectInputStream使用readObject的方法来实现对象的反序列化,readObject()方法会调用一个名为readOrdinaryObject的方法,该方法会通过反射的方式调用无参构造方法新建一个对象。
反序列化破坏单例
public class SerializableDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// Write to file
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
oos.writeObject(Singleton.getSingleton());
// Read from file
File file = new File("tempFile");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Singleton newInstance = (Singleton) ois.readObject();
System.out.println(newInstance == Singleton.getSingleton());
}
}
//false
解决办法:
1. 在单例类中增加一个 readResolve() 方法,方法中返回单例实例。因为 readOrdinaryObject() 方法在通过反射创建完对象后,会有一个条件判断,如果对象所在的类存在 readResolve() 方法,则会改为使用 readResolve() 方法获取实例对象,而不是利用反射创建出来的对象
参考:Java序列化破坏单例模式的解决方案N种(好文)
关于“如何破坏单例“我说了好几种方式,面试官却说:我其实随便问问,没想到你真会
import java.io.Serializable;
/**
* 使用双重校验锁方式实现单例
*/
public class Singleton implements Serializable{
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
private Object readResolve() {
return singleton;
}
}
2. 使用枚举单例模式
反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。所以枚举类不能通过反射来创建对象
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
参考:
简单工厂模式
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
实现过程:
把实例化的操作单独放到一个工厂类中,这个类有一个方法,这个方法会根据形参的值返回不同的子类实例。这个类就称为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
好处:
这样做能把用户类和具体子类的实现解耦,用户类不再需要知道有哪些子类以及应当实例化哪个子类。而只需要向工厂类提供一些必要的信息,就能从工厂中取得它想要的类实例。用户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如某个子类的类名发生改变或者增加了某个子类,那么所有的客户类都要进行修改。如果使用了简单工厂,用户类不需要知道子类的细节,并且就算子类发生改变,也只要在工厂类的创建对象方法中增加一个分支判断就可以 了。
缺点:
每次增加子类或者删除子类对象都需要对简单工厂类进行修改。不符合开闭原则,所谓的开闭原则就是对扩展开放,对修改封闭,就是最好不要修改工厂的方法,而是采用功能的扩展的方式来达到目的。另外,随着子类数量的增加,简单工厂类会变得很庞大臃肿、耦合性高,所以就引入了工厂方法设计模式。
代码实现
interface Product{
}
class ConcreteProduct implements Product{
}
class ConcreteProduct1 implements Product{
}
class ConcreteProduct2 implements Product{
}
public class SimpleFactory {
public Product createProduct(int type){
if(type == 1){
return new ConcreteProduct1();
}else if(type == 2){
return new ConcreteProduct2();
}else{
return new ConcreteProduct();
}
}
}
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(2);
// do something
}
工厂方法模式
工厂方法模式是对简单工厂模式进一步的解耦,因为在工厂方法模式中是一个子类对应一个工厂类,而这些工厂类都实现于一个抽象接口。这相当于是把原本会因为业务代码而庞大的简单工厂类,拆分成了一个个的工厂类,这样代码就不会都耦合在同一个类里了。我们需要哪个类就创建哪个类的工厂,通过这个工厂来获取对象。
优点
1. 符合开闭原则,如果需要增加某个子产品,只需要增加一个创建该产品的产品工厂,这个产品工厂继承了抽象工厂,没有修改原有的代码。所以工厂方法模式克服了简单工厂会违背开-闭原则的缺点,又保持了封装对象创建过程的优点。
2. 工厂方法把实例化操作推迟到子类工厂,可以提高性能
缺点
随着产品数量的增加,具体产品工厂的会越来越多。
工厂方法模式代码:
interface Product{
}
class ConcreteProduct1 implements Product{
}
class ConcreteProduct2 implements Product{
}
abstract class Factory{
public abstract Product createProduct();
}
class ConcreteFactory1 extends Factory{
@Override
public Product createProduct() {
return new ConcreteProduct1();
}
}
class ConcreteFactory2 extends Factory{
@Override
public Product createProduct() {
return new ConcreteProduct2();
}
}
public static void main(String[] args) {
Factory concreteFactory1 = new ConcreteFactory1();
Product product1 = concreteFactory1.createProduct();
Factory concreteFactory2 = new ConcreteFactory2();
Product product2 = concreteFactory2.createProduct();
}
抽象工厂模式
抽象工厂模式是工厂方法模式的一个版本延伸,同样必须先有一个抽象工厂,这个工厂有很多子类产品工厂,但是这些产品工厂不再是只用来创建一个产品,而是用来创建一个配套系列的产品,创建一个产品系列家族。比如一个配套产品需要两个组件,组件1 和 组件 2, 如果我需要 A 套产品,我就只需要创建 A 工厂,A 工厂就可以为我提供 A1, A2,两个组件, 如果我需要 B套产品,我就只需要创建 B工厂,B 工厂就可以为我提供 B1, B2,两个组件,这样我们通过一个具体的工厂就可以获得一个产品链,一个配套系列的产品家族。
比如比如一个创建汽车的工厂,这个工厂有两个方法,一个方法用于创建空调对象,另一个方法用来创建一个没有空调的车对象。这样宝马工厂就用来创建宝马的空调,创建宝马的车体,奔驰工厂就用来创建适用于奔驰车的空调和奔驰车的车体。这样我们通过一个具体的工厂就可以获得一个产品链,一个配套系列的产品家族。
好处
1. 抽象工厂模式最大的好处是易于交换产品系列,它只需要改变具体工厂即可使用不同的产品配置。不管是任何人的设计都无法去完全防止需求的更改,或者项目的维护,那么我们的理想便是让改动变得最小、最容易,
2. 抽象工厂模式的另一个好处就是它封装了具体的创建实例过程,使得我们不用去管这些创建实例的细节,简化了开发,减少了代码量
缺点
1. 每次获取实例对象都要创建工厂,如果用户程序非常多,就可能需要创建非常多的具体工厂。
2. 如果我们需要在一个系列中增加一个产品,那么需要在顶级工厂中增加一个方法,从而需要改动所有的具体工厂。
抽象工厂模式代码实现:
产品类:
abstract class AbstractProduct1{
}
abstract class AbstractProduct2{
}
class ProductA1 extends AbstractProduct1{
}
class ProductB1 extends AbstractProduct1{
}
class ProductA2 extends AbstractProduct2{
}
class ProductB2 extends AbstractProduct2{
}
工厂类:
abstract class Factory{
abstract public AbstractProduct1 createProduct1();
abstract public AbstractProduct2 createProduct2();
}
class ProductAFactory extends Factory{
@Override
public AbstractProduct1 createProduct1() {
return new ProductA1();
}
@Override
public AbstractProduct2 createProduct2() {
return new ProductA2();
}
}
class ProductBFactory extends Factory{
@Override
public AbstractProduct1 createProduct1() {
return new ProductB1();
}
@Override
public AbstractProduct2 createProduct2() {
return new ProductB2();
}
}
public static void main(String[] args) {
// 创建一个家族A产品
Factory factoryA = new ProductAFactory();
AbstractProduct1 A1 = factoryA.createProduct1();
AbstractProduct2 A2 = factoryA.createProduct2();
}
动态代理设计模式
就是在目标类的基础上增加切面逻辑,生成增强的目标类
动态的代理的实现方式主要有两种,分别是 CGLib 动态代理和 JDK动态代理
JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:
JDK代理只能对实现接口的类生成代理,没有实现接口的类不能被代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,因为通过继承类的实现方式,所以不能代理final修饰的类。
cglib通过修改字节码生成子类,创建效率较低,执行效率高;jdk动态代理的方式创建代理对象效率较高,但是因为通过反射机制代理对象,所以执行效率较低,
jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势
CGlib 代码实现
class Actor2{
public void basicPerform(float money){
System.out.println("拿到钱 " + money + ",开始基本表演");
}
public void dangerPerform(float money){
System.out.println("拿到钱 " + money + ", 开始危险表演");
}
}
public class CGLibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String name = method.getName();
Object rtValue = null;
Float money = (Float)args[0];
if("basicPerform".equals(name)){
if(money > 2000){
args[0] = money / 2;
rtValue = methodProxy.invokeSuper(o, args);
}
}
if("dangerPerform".equals(name)){
if(money > 5000){
args[0] = money / 2;
rtValue = methodProxy.invokeSuper(o, args);
}
}
return rtValue;
}
}
class Client2{
public static void main(String[] args) {
CGLibProxy cglibProxy = new CGLibProxy();
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Actor2.class); // 设置Enhancer 的父类
enhancer.setCallback(cglibProxy);
// 创建代理对象
Actor2 actor = (Actor2) enhancer.create();
actor.basicPerform(3400);
actor.dangerPerform(6500);
}
}
CGLib 的匿名内部类实现
class Client2{
public static void main(String[] args) {
final Actor2 actor2 = new Actor2();
// 创建代理对象
Actor2 cglibActor = (Actor2)Enhancer.create(actor2.getClass(), new MethodInterceptor(){
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws InvocationTargetException, IllegalAccessException {
String name = method.getName();
Object rtValue = null;
Float money = (Float)args[0];
if("basicPerform".equals(name)){
// 基本演出
if(money > 2000){
rtValue = method.invoke(actor2, money / 2);
}
}
if("dangerPerform".equals(name)){
if(money > 5000){
rtValue = method.invoke(actor2, money / 2);
}
}
return rtValue;
}
});
cglibActor.basicPerform(3000);
cglibActor.dangerPerform(6500);
}
}
jdk动态代理代码实现
interface IActor{
// 基本表演
public void basicPerform(float money);
// 危险表演
public void dangerPerform(float money);
}
class Actor implements IActor{
@Override
public void basicPerform(float money){
System.out.println("拿到钱 " + money + ",开始基本表演");
}
@Override
public void dangerPerform(float money){
System.out.println("拿到钱 " + money + ", 开始危险表演");
}
}
public class JDKProxy implements InvocationHandler{
// 需要代理的对象
public Object targetObject;
/*public Object newProxy(Object targetObject){
// 将目标对象传入进行代理
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(), this);
}*/
public JDKProxy(Object target){
targetObject = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
Object rtValue = null;
String name = method.getName();
Float money = (Float)args[0];
if("basicPerform".equals(name)){
if(money > 2000){
rtValue = method.invoke(targetObject, money / 2);
}
}
if("dangerPerform".equals(name)){
if(money > 5000){
rtValue = method.invoke(targetObject, money / 2);
}
}
return rtValue;
}
}
class Client{
public static void main(String[] args) {
// 创建被代理对象
IActor actor = new Actor();
// 创建 Handler
JDKProxy JDKInvocationHandler = new JDKProxy(actor);
// 创建代理对象
IActor actorProxy = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
actor.getClass().getInterfaces(), JDKInvocationHandler);
actorProxy.basicPerform(3000);
actorProxy.dangerPerform(7000);
}
}
JDK 动态代理的匿名内部类实现