单例设计模式
介绍:
单例模式是 Java 中最简单的设计模式之一。它提供了一种创建对象的最佳方式 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建 这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
结构:
实现:
饿汉式:类加载就会导致该单例对象被创建
实现一:静态变量方式
public class Singleton {
private Singleton ( ) { }
private static Singleton singleton = new Singleton ( ) ;
public static Singleton getSingleton ( ) {
return singleton;
}
}
注意:该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象singleton。singleton对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
实现二:静态代码块方式
public class Singleton {
private Singleton ( ) {
}
private static Singleton singleton;
static {
singleton = new Singleton ( ) ;
}
public static Singleton getSingleton ( ) {
return singleton;
}
}
注意:该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。
懒汉式:类加载不会导致该单例对象被创建,而是首次使用该对象才会创建
实现一:线程不安全
public class Singleton {
private Singleton ( ) { }
private static Singleton singleton;
public static Singleton getSingleton ( ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
return singleton;
}
注意:从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题
实现二:线程安全
public class Singleton {
private Singleton ( ) { }
private static Singleton singleton;
public static synchronized Singleton getSingleton ( ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
return singleton;
}
}
注意:该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。
实现三:双重检查锁
对于 getInstance()方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。
public class Singleton {
private Singleton ( ) { }
private static volatile Singleton singleton;
public static Singleton getSingleton ( ) {
if ( singleton == null ) {
synchronized ( Singleton . class ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
}
}
return singleton;
}
}
注意:在多线程模式下,可能会出现空指针异常,出现问题的原因时jvm在实例化对象的会进行优化和指令重排的操作 解决:只需要使用volatile关键字,该关键字可以保证可见性和有序性 添加 volatile关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题
实现四:静态内部类
public class Singleton {
private Singleton ( ) { } ;
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton ( ) ;
}
public static Singleton getSingleton ( ) {
return SingletonHolder . SINGLETON ;
}
}
因为在jvm加载外部类时候,不会加载内部类,而只有调用内部类的方法或者属性时,才会被加载,并且初始化属性,由于被static修饰,保证只被初始化一次 在没有加锁的情况下,保证了多线程的安全,并且没有任何性能影响和浪费
恶汉式:
实现一:枚举方式
public enum Singleton {
SINGLETON;
}
枚举类型是线程安全的,并且只会被装载一次,写法简单,且是唯一一个不会被破坏的单例模式
破坏单例模式
序列化反序列化
public class Client {
public static void main ( String [ ] args) throws IOException , ClassNotFoundException {
writeObject2File ( ) ;
Singleton singleton = readObjectFromFile ( ) ;
Singleton singleton1 = readObjectFromFile ( ) ;
System . out. println ( singleton1== singleton) ;
}
public static Singleton readObjectFromFile ( ) throws IOException , ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ( "D:\\BlackHorse\\Project\\shejimoshi\\a.txt" ) ) ;
Singleton singleton = ( Singleton ) ois. readObject ( ) ;
ois. close ( ) ;
return singleton;
}
public static void writeObject2File ( ) throws IOException {
Singleton singleton = Singleton . getSingleton ( ) ;
ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ( "D:\\BlackHorse\\Project\\shejimoshi\\a.txt" ) ) ;
oos. writeObject ( singleton) ;
oos. close ( ) ;
}
}
注意:NotSerializableException:无序列化异常,说明没有实现Serializable接口
反射
public class Client {
public static void main ( String [ ] args) throws Exception {
Class < Singleton > singletonClass = Singleton . class ;
Constructor < Singleton > declaredConstructor = singletonClass. getDeclaredConstructor ( ) ;
declaredConstructor. setAccessible ( true ) ;
Singleton singleton = declaredConstructor. newInstance ( ) ;
Singleton singleton1 = declaredConstructor. newInstance ( ) ;
System . out. println ( singleton1 == singleton) ;
}
}
解决问题:
解决序列化反序列化破解单例
public class Singleton implements Serializable {
private Singleton ( ) { }
private static Singleton singleton;
public static synchronized Singleton getSingleton ( ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
return singleton;
}
public Object readResolve ( ) {
return singleton;
}
}
解决反射破解单例
public class Singleton {
public static boolean flag = false ;
private Singleton ( ) {
if ( flag) {
throw new RuntimeException ( "不能创建多个对象" ) ;
}
flag = true ;
} ;
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton ( ) ;
}
public static Singleton getSingleton ( ) {
return SingletonHolder . SINGLETON ;
}
}
```# 单例设计模式
## 介绍:
- 单例模式是 Java 中最简单的设计模式之一。它提供了一种创建对象的最佳方式
- 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建
- 这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
## 结构:
- 单例类:只能创建一个对象
- 访问类:使用单例类
## 实现:
## 饿汉式:类加载就会导致该单例对象被创建
### 实现一:静态变量方式
```java
public class Singleton {
private Singleton ( ) { }
private static Singleton singleton = new Singleton ( ) ;
public static Singleton getSingleton ( ) {
return singleton;
}
}
注意:该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象singleton。singleton对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
实现二:静态代码块方式
public class Singleton {
private Singleton ( ) {
}
private static Singleton singleton;
static {
singleton = new Singleton ( ) ;
}
public static Singleton getSingleton ( ) {
return singleton;
}
}
注意:该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。
懒汉式:类加载不会导致该单例对象被创建,而是首次使用该对象才会创建
实现一:线程不安全
public class Singleton {
private Singleton ( ) { }
private static Singleton singleton;
public static Singleton getSingleton ( ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
return singleton;
}
注意:从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题
实现二:线程安全
public class Singleton {
private Singleton ( ) { }
private static Singleton singleton;
public static synchronized Singleton getSingleton ( ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
return singleton;
}
}
注意:该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。
实现三:双重检查锁
对于 getInstance()方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。
public class Singleton {
private Singleton ( ) { }
private static volatile Singleton singleton;
public static Singleton getSingleton ( ) {
if ( singleton == null ) {
synchronized ( Singleton . class ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
}
}
return singleton;
}
}
注意:在多线程模式下,可能会出现空指针异常,出现问题的原因时jvm在实例化对象的会进行优化和指令重排的操作 解决:只需要使用volatile关键字,该关键字可以保证可见性和有序性 添加 volatile关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题
实现四:静态内部类
public class Singleton {
private Singleton ( ) { } ;
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton ( ) ;
}
public static Singleton getSingleton ( ) {
return SingletonHolder . SINGLETON ;
}
}
因为在jvm加载外部类时候,不会加载内部类,而只有调用内部类的方法或者属性时,才会被加载,并且初始化属性,由于被static修饰,保证只被初始化一次 在没有加锁的情况下,保证了多线程的安全,并且没有任何性能影响和浪费
恶汉式:
实现一:枚举方式
public enum Singleton {
SINGLETON;
}
枚举类型是线程安全的,并且只会被装载一次,写法简单,且是唯一一个不会被破坏的单例模式
破坏单例模式
序列化反序列化
public class Client {
public static void main ( String [ ] args) throws IOException , ClassNotFoundException {
writeObject2File ( ) ;
Singleton singleton = readObjectFromFile ( ) ;
Singleton singleton1 = readObjectFromFile ( ) ;
System . out. println ( singleton1== singleton) ;
}
public static Singleton readObjectFromFile ( ) throws IOException , ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ( "D:\\BlackHorse\\Project\\shejimoshi\\a.txt" ) ) ;
Singleton singleton = ( Singleton ) ois. readObject ( ) ;
ois. close ( ) ;
return singleton;
}
public static void writeObject2File ( ) throws IOException {
Singleton singleton = Singleton . getSingleton ( ) ;
ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ( "D:\\BlackHorse\\Project\\shejimoshi\\a.txt" ) ) ;
oos. writeObject ( singleton) ;
oos. close ( ) ;
}
}
注意:NotSerializableException:无序列化异常,说明没有实现Serializable接口
反射
public class Client {
public static void main ( String [ ] args) throws Exception {
Class < Singleton > singletonClass = Singleton . class ;
Constructor < Singleton > declaredConstructor = singletonClass. getDeclaredConstructor ( ) ;
declaredConstructor. setAccessible ( true ) ;
Singleton singleton = declaredConstructor. newInstance ( ) ;
Singleton singleton1 = declaredConstructor. newInstance ( ) ;
System . out. println ( singleton1 == singleton) ;
}
}
解决问题:
解决序列化反序列化破解单例
public class Singleton implements Serializable {
private Singleton ( ) { }
private static Singleton singleton;
public static synchronized Singleton getSingleton ( ) {
if ( singleton == null ) {
singleton = new Singleton ( ) ;
}
return singleton;
}
public Object readResolve ( ) {
return singleton;
}
}
解决反射破解单例
public class Singleton {
public static boolean flag = false ;
private Singleton ( ) {
if ( flag) {
throw new RuntimeException ( "不能创建多个对象" ) ;
}
flag = true ;
} ;
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton ( ) ;
}
public static Singleton getSingleton ( ) {
return SingletonHolder . SINGLETON ;
}
}