最近谷咕咕在学习设计模式,都说不会设计模式的程序员是读不懂框架源码的,而且写出的代码是架构底下的。行吧,那只好卷一下,看看各个设计模式的优点。
这里先是看了狂神的视频,然后对照菜鸟教程浏览了一下,其中不懂的知识点就去百度,当然百度了也不懂的,大可不必太深究。总结:还是要自己敲代码,运行,看结果去理解。光看要是不懂的话不用太烦,反正是挺晦涩的。
首先看最简单的设计模式:
单例模式
提供创建的对象的最佳方式。
注意:
1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所以其他对象提供这一实例。
关键:构造器私有,私有保证只能自己创建自己的实例。
实现单例的多种方式
1.懒汉式
通俗来讲,就是比较懒,不用的时候就没有这个实例,用到了才创建对象实例。
懒汉式,下面的3的代码就是在懒汉式上写的。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
要是想线程安全就锁上类
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.饿汉式
饿汉式其实就是在类装载的时候就实例化,容器造成垃圾对象。
package single;
/**
* @author ymgu
* @version V1.0
* @Package single
* @date 2022/3/21 10:59
*/
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
3.双重校验锁
所谓的双重校验锁其实就是使用synchronized 锁住类,然后volatile防止指令重排
package single;
import java.lang.reflect.Constructor;
/**
* @author ymgu
* @version V1.0
* @Package single
* @date 2022/3/21 9:58
*/
public class LazyMan {
//单例模式一定要构造器私有
private LazyMan(){
synchronized (LazyMan.class){
if(lazyMan!=null){
throw new RuntimeException("不要通过反射破坏单例");
}
}
}
//防止指令重排volatile
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan==null){
//锁住该类,防止出现多线程创建多个对象
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan=new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
LazyMan lazyMan=LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null);
//破坏其的私有权限
declaredConstructor.setAccessible(true);
LazyMan lazyMan2=declaredConstructor.newInstance();
System.out.println(lazyMan);
System.out.println(lazyMan2);
}
}
4.静态内部类
今天内部类的实现方式和双重校验锁效果一样,但是可以延迟初始化,这一块吧,我就没有深究。
package single;
import com.sun.org.apache.bcel.internal.classfile.InnerClass;
/**
* @author ymgu
* @version V1.0
* @Package single
* @date 2022/3/21 10:08
*/
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
5.枚举
如果通过反射来破坏,就会报异常,enum是实现了方式反射的
package single;
import java.lang.reflect.Constructor;
/**
* @author ymgu
* @version V1.0
* @Package single
* @date 2022/3/21 10:34
*/
//反射不能破坏枚举的单例
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle enumSingle1=EnumSingle.INSTANCE;
EnumSingle enumSingle2=EnumSingle.INSTANCE;
System.out.println(enumSingle1);
System.out.println(enumSingle2);
Constructor<EnumSingle> declaredConstructor=EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle3=declaredConstructor.newInstance();
System.out.println(enumSingle3);
}
}
最后引用菜鸟的话
经验之谈:一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。