设计模式(创建型)
设计模式总体可分为创建型模式,结构型模式,行为型模式
创建型模式:单例模式,工厂模式,原型模式
单例模式
概念:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点,既就是提供一个公共的方法,然后为之返回一个实例
优点:只生成一个实例,内存开销少,一般用于读取配置文件
实现方式:共五种,常见的有两种
常见:饿汉式:线程安全,调用效率高,但是不能延时加载
懒汉式:线程安全,调用效率不高,但是可以延时加载
其他:
双重检测锁式:由于JVM底层内部模型原因,偶尔出错,不建议使用
静态内部类实现:线程安全,调用效率高,可进行延时加载
枚举单例:线程安全,调用效率高,不能延时加载
饿汉式:
通俗来讲,特别饿,一上来就先创建,类初始化时候,立即加载这个对象,然后将构造器私用,通过公共方法返回创建好的对象
/** * * 饿汉式 * */ public class Singel01 {
//类初始化时候,立即加载这个对象,没有延时加载的优势,加载类时,是天然的线程安全 private static Singel01 s = new Singel01();
private Singel01(){ } //方法没有同步,调用效率高 public static Singel01 instance(){ return s; } }
|
懒汉式:
真正用的时候才创建,资源利用效率高,但是每次调用都同步,并发效率低
/* * 懒汉式 */ public class Singel02 implements Serializable{
//类初始化时候,不加载这个对象(延时加载,真正用的时候才加载) private static Singel02 s;
private Singel02(){
} //方法同步,调用效率低 public static synchronized Singel02 instance(){ if(s==null){ s = new Singel02(); } return s; }
}
|
双重检测锁实现:
这个模式将同步内容下方到内部,提高执行效率,不必每次获取对象是都进行同步,只有第一次才进行同步,创建以后就不同步,但是由于编译器优化的原因和JVM底层内部模型的原因,会出问题,不建议使用
/* * 双重检测锁实现 */ public class Singel03 {
private static Singel03 s = null;
public static Singel03 instance(){ if(s==null){ Singel03 sc; synchronized (Singel03.class) { sc = s; if(sc==null){ sc = new Singel03(); } } s = sc; } return s; }
}
|
静态内部类:
外部类没有static属性,则不会像饿汉式那样立即加载对象,只有真正调用时候,才会加载静态内部类,加载类时,线程是安全的,构造的对象是static final类型,从而保证了线程安全,兼备了并发高效调用和延迟加载的优势
/* * 通过静态内部类实现 */ public class Single04 {
private static class SinleInstance{ private static final Single04 s = new Single04(); }
private Single04(){
} public static Single04 instance(){ return SinleInstance.s; } }
|
枚举类:
/* * 枚举类实现 */ public enum Singel05 {
instance;
public void instance(){
} }
|
如何选用?
占用资源少,不需要延时加载
枚举好于饿汉式
占用资源大,需要延时加载
静态内部类好于懒汉式
在以上5中实现单例模式中,除了枚举类,其他四种都可以通过反射和反序列化进行破解,但是枚举不行,枚举是真正意义上的天然线程安全
反射破解懒汉式:
//通过反射来实现破解,调用私有的构造器 Class clazz = Class.forName("com.sxt.first.Singel02"); Constructor<Singel02> c = clazz.getDeclaredConstructor(null); c.setAccessible(true); Singel02 s3 = c.newInstance(); Singel02 s4 = c.newInstance(); System.out.println(s3); System.out.println(s4); |
防止反射破解:
/* * 抛出异常阻止反射 */ private Singel02(){ if(s!=null){ throw new RuntimeException(); } } |
通过反序列化漏洞实现破解
//通过反序列化的方式构造多个对象 FileOutputStream fos = new FileOutputStream("d:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt")); Singel02 s6 = (Singel02) ois.readObject(); System.out.println(s6); |
防止反序列化破解
//反序列化时候,如果定义了readResolve(),则直接返回此方法指定的对象 private Object readResolve(){ return s; } |
原型模式
通过new产生一个对象需要非常繁琐的数据准备或者访问权限,可以使用原型模式
实现方式就是对象内存的复制,内存的复制及其麻烦,但是在java中提供了对象的clone方法,使得变为简单
一某个对象为原型,复制出新的对象,新的对象具有圆形的特点
优势有:效率高,避免了执行构造方法的过程
实现:类实现Cloneable接口和clone方法
使用场景:通过new产生一个对象需要繁琐的数据准备,则可以使用原型模式
/* * 原型模式 */ public class Sheep implements Cloneable,Serializable{
private String name; private String birthday;
@Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone();//直接调用Object对象的clone方法 return obj; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getBirthday() { return birthday; }
public void setBirthday(String birthday) { this.birthday = birthday; }
public Sheep(String name, String birthday) { this.name = name; this.birthday = birthday; }
public Sheep() { }
}
|
public class test { public static void main(String[] args) throws CloneNotSupportedException, Exception {
Sheep sheep1 = new Sheep("多利","1955-03-06"); sheep1.setName("西安工程大学"); System.out.println(sheep1.getName()); /*Sheep sheep2 = (Sheep) sheep1.clone(); sheep1.setName("西安科技大学"); sheep2.setName("西安工程大学"); System.out.println(sheep1); System.out.println(sheep1.getName()); System.out.println(sheep1.getBirthday());
System.out.println(sheep2); System.out.println(sheep2.getName()); System.out.println(sheep2.getBirthday());*/ /* * 通过序列化和反序列化实现深复制 */ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(sheep1);
byte[] bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); Sheep sheep2= (Sheep)ois.readObject(); System.out.println(sheep2.getName()); sheep2.setName("西安科技大学"); System.out.println(sheep2.getName()); System.out.println(sheep2.getBirthday());
} } |
测试原型模式相比传统创建对象效率
public class Sheep2 implements Cloneable{
@Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); return obj; } //创建对象的时候,让等待10毫秒时间 public Sheep2(){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
|
/** * 测试效率问题 * */ public class test2 {
//采用new的方式创建对象 public static void testnew(int size){ long start = System.currentTimeMillis(); for(int i=0;i<size;i++){ Sheep2 s1 = new Sheep2(); } long end = System.currentTimeMillis(); System.out.println("new耗时:"+(end-start)); } //采用clone方式创建对象 public static void testclone(int size) throws Exception{ long start = System.currentTimeMillis(); Sheep2 s = new Sheep2(); for(int i=0;i<size;i++){ Sheep2 s2 = (Sheep2) s.clone(); } long end = System.currentTimeMillis(); System.out.println("clone耗时:"+(end-start)); }
public static void main(String[] args) throws Exception {
test2.testnew(1000); test2.testclone(1000); } }
|
工厂模式
实现了创建者和调用者的分离
具体可分为:简单工厂模式,工厂方法模式,抽象工厂模式
简单工厂模式:用来生产同一等级结构中的任意产品(对于新的产品,需要修改已有的代码)
工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
抽象工厂模式:用来生产不同产品族的全部产品(对于新增加的产品,无能为力,只能增加产品族)
注:工厂方法虽然满足了ocp原则,但是造成了类的增多,管理起来很不方便,因此在实际开发中,一般都采用的是简单工厂模式
代码··~~~~~~~ |