记录下学习设计模式-单例模式的写法。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
结构:
适用性:
package com.example.deesign_patterns.singleton.demo1;
//单例模式之饿汉式-静态成员变量写法
//缺点:如果该类一直不使用,就会有内存浪费的问题。但是线程是安全的
public class Singleton {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.在本类中创建本类对象
private static Singleton instance=new Singleton();
//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return instance;
}
}
package com.example.deesign_patterns.singleton.demo1;
public class Client {
public static void main(String[] args) {
//创建Singleton类的对象(错误方式),会报错,防止了外部内创建对象
//Singleton singleton=new Singleton();
//创建Singleton类的对象(正确方式)
Singleton singleton1=Singleton.getInstance();
Singleton singleton2=Singleton.getInstance();
//判断获取到的2个是否是同一个对象
System.out.println(singleton1==singleton2);//结果为true,说明是同一个对象
}
}
package com.example.deesign_patterns.singleton.demo2;
//单例模式之饿汉式-静态代码块写法
//缺点:如果该类一直不使用,就会有内存浪费的问题。但是线程是安全的
public class Singleton {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.声明Singleton类型的变量
private static Singleton instance;//初始值为null
//3.在静态代码块中进行赋值
static {
instance=new Singleton();
}
//4.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return instance;
}
}
package com.example.deesign_patterns.singleton.demo2;
public class Client {
public static void main(String[] args) {
//创建Singleton类的对象(错误方式),会报错,防止了外部内创建对象
//Singleton singleton=new Singleton();
//创建Singleton类的对象(正确方式)
Singleton singleton1= Singleton.getInstance();
Singleton singleton2= Singleton.getInstance();
//判断获取到的2个是否是同一个对象
System.out.println(singleton1==singleton2);//结果为true,说明是同一个对象
}
}
package com.example.deesign_patterns.singleton.demo3;
//单例模式之懒汉式-线程不安全写法和线程安全写法
public class Singleton {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.声明Singleton类型的变量
private static Singleton instance;//初始值为null
//3.提供一个公共的访问方式,让外界获取该对象,线程不安全写法
//缺点:线程不是安全的,在多线程下可能会创建不同对象,导致对象不是同一个
/*public static Singleton getInstance(){
//判断instance是否为null,如果为null,那就创建Singleton类对象,如果有直接返回
if(instance==null){
instance=new Singleton();
}
return instance;
}*/
//3.提供一个公共的访问方式,让外界获取该对象,线程安全写法,加一个synchronized来让方法同步,保证多线程下获取到的是同一个对象
//缺点:加了同步锁后在大并发量下会影响性能。
public static synchronized Singleton getInstance(){
//判断instance是否为null,如果为null,那就创建Singleton类对象,如果有直接返回
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
package com.example.deesign_patterns.singleton.demo3;
public class Client {
public static void main(String[] args) {
//创建Singleton类的对象(错误方式),会报错,防止了外部内创建对象
//Singleton singleton=new Singleton();
//创建Singleton类的对象(正确方式)
Singleton singleton1= Singleton.getInstance();
Singleton singleton2= Singleton.getInstance();
//判断获取到的2个是否是同一个对象
System.out.println(singleton1==singleton2);//结果为true,说明是同一个对象
}
}
package com.example.deesign_patterns.singleton.demo4;
//单例模式之懒汉式-双重检查锁方式(推荐使用的方式)
//优点:解决了单例、性能、线程安全问题
//缺点:在多线程的情况下,如果不加volatile有可能出现空指针的问题
//改进就是在步骤2那里加一个volatile关键字,保证可见性和有序性就不会出现空指针问题
public class Singleton {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.声明Singleton类型的变量
private static volatile Singleton instance;//初始值为null
//3.提供一个公共的访问方式,让外界获取该对象
public static synchronized Singleton getInstance(){
//第一次判断,如果instance的值不为null,不需要抢占锁,直接返回对象
if(instance==null){
synchronized (Singleton.class){
//第二次判断
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
package com.example.deesign_patterns.singleton.demo5;
//单例模式之懒汉式-静态内部类方式(推荐使用的方式)
//静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法,被调用时才会被加载,并初始化其静态属性。
//静态属性由于被static修饰,保证只被实例化一次,并且严格保证实例化顺序。
public class Singleton {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
//被final修饰的变量只能被赋值一次,相当于常量
private static final Singleton INSTANCE=new Singleton();
}
//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
package com.example.deesign_patterns.singleton.demo6;
//单例模式之饿汉式-枚举类方式(非常推荐使用的方式)
//枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式
public enum Singleton {
INSTANCE;
}
package com.example.deesign_patterns.singleton.demo6;
public class Client {
public static void main(String[] args) {
//让JVM帮实现单例模式
Singleton singleton1= Singleton.INSTANCE;
Singleton singleton2= Singleton.INSTANCE;
//判断获取到的2个是否是同一个对象
System.out.println(singleton1==singleton2);//结果为true,说明是同一个对象
}
}
在单例模式之懒汉式-静态内部类方式中实现Serializable类
package com.example.deesign_patterns.singleton.demo7;
import java.io.Serializable;
//单例模式之懒汉式-静态内部类方式(推荐使用的方式)
//实现序列化用来演示破坏该单例模式
public class Singleton implements Serializable {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
//被final修饰的变量只能被赋值一次,相当于常量
private static final Singleton INSTANCE=new Singleton();
}
//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
先执行writeObject2File()方法在D盘生成一个a.txt文件,然后再执行readObjectFromFile()方法。
package com.example.deesign_patterns.singleton.demo7;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Client {
public static void main(String[] args) throws Exception{
//使用序列化和反序列化破坏单例模式
//writeObject2File();
//第一次与第二次对象地址不一样,说明破坏了单例模式
readObjectFromFile();
readObjectFromFile();
}
//从文件读取数据(对象)
public static void readObjectFromFile() throws Exception{
//1.创建对象输入流对象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\a.txt"));
//2.读取对象
//JDK1.8版本ObjectInputStream里面的readObject()源码里面1690行点击readOrdinaryObject后看2215行往后的代码有个对象判断如果类中有readResolve()方法就执行这个方法
Singleton instance= (Singleton) ois.readObject();
System.out.println("读取对象为="+instance);
//3.释放资源
ois.close();
}
//向文件中写数据(对象)
public static void writeObject2File() throws Exception{
//1.获取Singleton对象
Singleton instance=Singleton.getInstance();
//2.创建对象输出流对象(将instance写入到对应目录下a.txt文件里面,文件没有会被自动生成)
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\a.txt"));
//3.写对象
oos.writeObject(instance);
//4.释放资源
oos.close();
}
}
在Singleton类加上readResolve方法即可,它会自动执行。
package com.example.deesign_patterns.singleton.demo7;
import java.io.Serializable;
//单例模式之懒汉式-静态内部类方式(推荐使用的方式)
//实现序列化用来演示破坏该单例模式
public class Singleton implements Serializable {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
//被final修饰的变量只能被赋值一次,相当于常量
private static final Singleton INSTANCE=new Singleton();
}
//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
//解决序列化后破坏单例模式方法,只需要加上该方法就可以解决,不需要改动其他任何代码
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
//readResolve()方法会被自动调用,需要看ObjectInputStream里面的readObject()方法源码
public Object readResolve(){
return SingletonHolder.INSTANCE;
}
}
重新测试结果:
先执行writeObject2File()方法,在D盘生成一个a.txt文件:
重新执行readObjectFromFile()方法结果如下:
package com.example.deesign_patterns.singleton.demo7;
//单例模式之懒汉式-静态内部类方式(推荐使用的方式)
//反射破坏单例模式
public class Singleton {
//1.私有构造方法,防止外部类实例化该类
private Singleton(){}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
//被final修饰的变量只能被赋值一次,相当于常量
private static final Singleton INSTANCE=new Singleton();
}
//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
package com.example.deesign_patterns.singleton.demo8;
import java.lang.reflect.Constructor;
public class Client {
public static void main(String[] args) throws Exception{
//使用反射破坏单例模式
//1.获取Singleton的字节码对象
Class clazz=Singleton.class;
//2.获取无参构造方法对象(因为Singleton对象构造方法是私有的,所以需要使用下面来获取)
Constructor cons= clazz.getDeclaredConstructor();
//3.取消访问检查
cons.setAccessible(true);
//4.创建Singleton对象
Singleton s1= (Singleton) cons.newInstance();
Singleton s2= (Singleton) cons.newInstance();
System.out.println(s1==s2);//结果为false说明破坏了单例模式
}
}
在Singleton类中的构造方法加同步锁
package com.example.deesign_patterns.singleton.demo8;
//单例模式之懒汉式-静态内部类方式(推荐使用的方式)
//反射破坏单例模式解决方式
public class Singleton {
private static boolean flag=false;
//1.私有构造方法,防止外部类实例化该类
private Singleton(){
synchronized (Singleton.class){
//判断flag的值是否是true,如果是true,说明非第一次访问,直接抛一个异常,如果是false,说明第一次访问
if(flag){
throw new RuntimeException("不能创建多个对象");
}
//将flag的值设置为true
flag=true;
}
}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
//被final修饰的变量只能被赋值一次,相当于常量
private static final Singleton INSTANCE=new Singleton();
}
//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
再次执行Client类的main方法结果如下:
抛出了我们自己设置的异常
Runtime类在JDK源码中是使用的单例模式之饿汉式-静态成员变量写法
package com.example.deesign_patterns.singleton.demo9;
import java.io.IOException;
import java.io.InputStream;
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
//获取Runtime类的对象,使用的是饿汉式静态成员变量方式
Runtime runtime=Runtime.getRuntime();
//调用runtime的方法exec,参数要的是一个windows命令
Process process=runtime.exec("ipconfig");
//调用process对象的获取输入流的方法
InputStream is = process.getInputStream();
byte[] arr=new byte[1024*1024*100];
//读取数据
int len= is.read(arr);//返回读到的字节的个数
//将字节数组转换为字符串输出到控制台
System.out.println(new String(arr,0,len,"GBK"));
}
}