文章目录
什么是设计模式
是一种编程的套路与经验,具有普遍性并可以反复使用。是面向对象设计原则的实际运用,充分体现了类的封装性,继承性多态性与组合关系的充分理解。
P.S: 也是我们程序猿进阶要学的东西
设计模式的分类
创建型模式
描述了怎样创建对象,特点是把对象的创建与使用进行分离,创建型模式提供了1.单例,2.原型,3.工厂方法,4.抽象工厂,建造者等五种创建型模式.
结构型模式
描述如何将类或者对象组成某种更大的结构。包括代理,适配器,桥接,装饰,外观,享元,组合等结构模式
行为型模式
描述类或对象之间如何协作完成单个对象无法完成的任务,以及怎样去分配职责。包括模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等11种行为型
什么是单例模式 Singleton
提供了一种创建对象的最佳方式,类创建自己的对象实例,确保单一的类只能创建单一的对象,这个类还提供了单一访问该对象的方式,可以直接访问而不用实例化。这种单例模式有利于节省内存,限制了实例的个数,有利于gc。
单例模式的结构
1.单例类:指只创建一个实例的类
2.访问类:是使用单例类的类
单例模式应用场景
不需要多个实例的使用场景,例如在连接池,线程池中。还有一个所有用户公用的在线人数计数器,类似于b站上的现在观看的人数。
单例模式的实现
懒汉模式
类加载不会导致该实例被创建,在首次使用时才创建对象
懒汉式:线程不安全
public class Singleton{
//私有的构造方法
private Singleton(){};
//声明Singleton类型的变量
private static Singleton instance;
//判断该对象是否为空,为空则进行赋值
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
懒汉式:线程安全
public class Singleton{
//私有的构造方法
private Singleton(){};
//声明Singleton类型的变量
private static Singleton instance;
//判断该对象是否为空,为空则进行赋值
//为避免线程不安全问题,在方法里加上synchronized锁
public static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
懒汉式:双重检查锁
为了避免线程安全方法中加锁所带来的的性能问题,而且getInstance()大部分都是读操作,所以可以不用所有线程都持有锁。
public class Singleton{
//私有的构造方法
private Singleton(){};
//声明Singleton类型的变量
//为避免多线程的空指针问题,加volatile
private static volatile Singleton instance;
//判断该对象是否为空,为空则进行赋值
public static Singleton getInstance(){
//第一重检查:如果instance不为null,可直接返回对象不用加锁
if(instance==null){
//同步代码块,对字节码加锁
synchronized(Singleton.class){
//二重检查
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查锁所带来的问题:多线程时的空指针警告!解决方法:在声明对象时增加volatile关键字
懒汉式:静态内部类(这个方法大大的好)
public class Singleton{
//私有的构造方法
private Singleton(){};
//在静态内部类SingletonHolder中创建实例
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
//外部访问该对象的方法
public static Singleton getInstance(){
//返回的是内部类中的实例
return Singleton.INSTANCE;
}
}
在加载Singleton类是不会初始化INSTANCE,只有调用了getInstance()方法时才会在加载内部类SingletonHolder时初始化INSTANCE。该方法在开源项目中常用,没有使用任何锁,既保证了多线程安全,又没有性能影响与空间浪费。
饿汉式
类加载时就创建了对象实例,静态成员变量。
饿汉式特点
1.私有的构造方法
2.在本类中创建本类的对象
3.提供公共的访问方式让外界获取该对象
4.缺陷是类加载时就创建了对象实例,如果一直未使用该对象且比较大时,会造成内存的浪费
饿汉式构造方法1:静态成员变量,在声明的时候直接赋值
//饿汉式:静态成员变量
public class Singleton{
//私有构造方法
private Singleton(){};
//在类中创建对象
private static Singleton instance = new Sigleton();
//外界对该对象的访问方式
public static Singleton getInstance(){
return instance;
}
}
饿汉式实现2:静态代码块
//饿汉式:静态代码块
public class Singleton{
//私有构造方法
private Singleton(){};
//声明Singleton类型的变量
private static Singleton instance;
//再在静态代码块中对该变量赋值
static{
instance = new Singleton;
}
//对外提供该类对象的访问方法
public static Singleton getInstance(){
return instance;
}
}
饿汉式实现3:枚举类型
//饿汉式:枚举类型
public enum Singleton{
INSTANCE;
}
完全线程安全,不会被破坏
单例模式存在的问题
单例模式的破坏:指通过某种方法使Singleton可以创建多个对象。
序列化和反序列化破坏单例模式
解决方法:在Singleton中添加一个readResolve方法,在反序列化中被调用。当进行反序列化中,会自动调用该方法会自动返回。
方法原理: 在源码中定义了如果有readResolve()方法,就会调用该方法的值,如果没有定义就会返回new的对象
反射破坏单例模式
通过反射创建两次对象创建的不是同一个对象
解决方法:添加一个flag来判断是否是第一次创建,如果是第二次创建就抛出异常
单例模式在JDK中的使用
Runtime类
Runtime类是通过饿汉式中的静态成员变量直接赋值实现的
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
...
}
如何使用Runtime类:直接通过Runtime中的getRuntime()来访问而不是new一个。
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
//获取Runtime类对象
Runtime runtime = Runtime.getRuntime();
//返回 Java 虚拟机中的内存总量。
System.out.println(runtime.totalMemory());
//返回 Java 虚拟机试图使用的最大内存量。
System.out.println(runtime.maxMemory());
//创建一个新的进程执行指定的字符串命令,返回进程对象
Process process = runtime.exec("ipconfig");
//获取命令执行后的结果,通过输入流获取
InputStream inputStream = process.getInputStream();
byte[] arr = new byte[1024 * 1024* 100];
int b = inputStream.read(arr);
System.out.println(new String(arr,0,b,"gbk"));
}
}