设计模式
23种设计模式,一个没少。比较常用的:单例、工厂、适配器、装饰者、代理、观察者这几个模式。其他的做了解。
创建型模式
单例模式
作用:确保一个类只有一个实例,只对外提供一个访问该实例的全局访问点。
特征:
1、无法通过new得到实例,构造器是被private修饰的
2、一般通过getInstance()的方法来获取它们的实例,而getInstance方法是对象的引用,并不是真正的new一个对象
对于单例模式,又分为这几类
饿汉式单例模式
我的理解就是饿汉就是初始化就对对象加载,没有延时
public class SingletonDemo1 {
//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
private static SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1(){
}
//方法没有同步,调用效率高!
public static SingletonDemo1 getInstance(){
return instance;
}
}
懒汉式加载(线程不安全)
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式加载(线程安全)
public class SingletonDemo2 {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
private static SingletonDemo2 instance;
private SingletonDemo2(){
//私有化构造器
}
//方法同步,调用效率低!
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
双重检查锁
注意:
双重检测锁式:涉及到JVM底层内部模型,偶尔会出问题。不建议使用
public class SingletonDemo3 {
private volatile static SingletonDemo3 instance = null;
public static SingletonDemo3 getInstance() {
if (instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo3.class) {
if(sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
private SingletonDemo3() {
}
}
静态内部类
这个方法需要熟悉使用,很多用到单例的地方用这个方法就差不多可以了,优点:线程安全,调用效率高,并且实现了延时加载!
public class SingletonDemo4 {
private static class SingletonClassInstance {
private static final SingletonDemo4 instance = new SingletonDemo4();
}
private SingletonDemo4(){
}
//方法没有同步,调用效率高!
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
}
枚举实现单例模式
这个方法也很好地避免了多线程同步的问题,并且还能够反序列化重新创建对象。
public enum SingletonDemo5 {
//这个枚举元素,本身就是单例对象!
INSTANCE;
}
实际上,这里的单例模式并不是真正的单例,当出现反射和反序列化的时候就会变成原来的普通对象
反射测试
//懒汉测试类
public class SingletonDemo06 /*implements Serializable*/{
private static SingletonDemo06 instace;
private SingletonDemo06() {
//需要私有化构造器
}
//方法需要同步,调用效率低
public static synchronized SingletonDemo06 getInstace() {
if(null == instace)
{
instace = new SingletonDemo06();
}
return instace;
}
}
通过反射调用私有构造器
public static void main(String[] args) throws Exception, SecurityException {
SingletonDemo06 s1 = SingletonDemo06.getInstace();
SingletonDemo06 s2 = SingletonDemo06.getInstace();
System.out.println(s1);
System.out.println(s2);
//通过反射的方式调用私有构造器
Class<SingletonDemo06> clz = (Class<SingletonDemo06>) SingletonDemo06.class;
Constructor<SingletonDemo06> cc = clz.getDeclaredConstructor();
cc.setAccessible(true);
SingletonDemo06 s3 = (SingletonDemo06)cc.newInstance();
SingletonDemo06 s4 = (SingletonDemo06)cc.newInstance();
System.out.println(s3);//Singleton.SingletonDemo06@2ff4acd0
System.out.println(s4);//Singleton.SingletonDemo06@54bedef2
}
很显然反射后的对象跟单例的hashcode不一样
解决反射的方式
在私有构造器加一层判断
public class SingletonDemo06 /*implements Serializable*/{
private static SingletonDemo06 instace;
private SingletonDemo06() {
//需要私有化构造器
if(null != instace)
{
throw new RuntimeException();//防止反射下创建新的对象
}
}
//方法需要同步,调用效率低
public static synchronized SingletonDemo06 getInstace() {
if(null == instace)
{
instace = new SingletonDemo06();
}
return instace;
}
}
反序列化Demo
public static void main(String[] args) throws Exception, SecurityException {
SingletonDemo06 s1 = SingletonDemo06.getInstace();
SingletonDemo06 s2 = SingletonDemo06.getInstace();
System.out.println(s1);//Singleton.SingletonDemo06@279f2327
System.out.println(s2);//Singleton.SingletonDemo06@279f2327
//通过反射的方式调用私有构造器
// Class<SingletonDemo06> clz = (Class<SingletonDemo06>) SingletonDemo06.class;
// Constructor<SingletonDemo06> cc = clz.getDeclaredConstructor();
// cc.setAccessible(true);
// SingletonDemo06 s3 = (SingletonDemo06)cc.newInstance();
// SingletonDemo06 s4 = (SingletonDemo06)cc.newInstance();
// System.out.println(s3);//Singleton.SingletonDemo06@2ff4acd0
// System.out.println(s4);//Singleton.SingletonDemo06@54bedef2
//通过反序列化的方式构造多个对象
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"));
SingletonDemo06 s3 = (SingletonDemo06) ois.readObject();
System.out.println(s3);//Singleton.SingletonDemo06@26a1ab54
}
解决方法readResolve()直接返回指定的单例对象
public class SingletonDemo06 implements Serializable{
private static SingletonDemo06 instace;
private SingletonDemo06() {
//需要私有化构造器
if(null != instace)
{
throw new RuntimeException();//防止反射下创建新的对象
}
}
//方法需要同步,调用效率低
public static synchronized SingletonDemo06 getInstace() {
if(null == instace)
{
instace = new SingletonDemo06();
}
return instace;
}
//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws Exception{
return instace;
}
}
总结:
单例模式的优点:
1、单例模式只产生一个实例,减少了性能的开销
2、当一个对象需要很多资源的时候,例如读取配置、产生依赖等,可以直接在启动时产生单例,让他永久驻留内存
3、单例可以让全局使用,达到优化资源共享
五种单例模式的实现方式比较:
- 饿汉式:线程安全,调用效率高。 但是,不能延时加载
- 懒汉式:线程安全,调用效率不高。 但是,可以延时加载
- 双重检测锁式:由于JVM底层内部模型原因,偶尔会出问题。不建议使用
- 静态内部类式:线程安全,调用效率高。 但是,可以延时加载
- 枚举单例:线程安全,调用效率高,不能延时加载
单例模式的应用:
1、Windows的Task Manager(任务管理器)、回收站就是很典型的单例模式
2、 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作 ,否则内容不好追加。
3、spring容器的管理
工厂模式
主要是为了实现创建者和调用者的分离。通过工厂的方法代替new实例化对象,将实现类、创建对象统一管理和控制,达到调用者与实现者的解耦合。
简单工厂模式
一个抽象的接口,多个抽象接口的实现类,一个工厂类。弊端:一般是静态方法,对新增加的类必须修改代码。
public intereface Car{
void run();
}
public class Audi implements Car {
@Override
public void run() {
System.out.println("奥迪生产");
}
}
public class byd implements Car{
@Override
public void run() {
System.out.println("比亚迪生产");
}