抵御反射机制调用单例类私有构造器

Java 单例模式一般有两种实现:

    1.暴露静态final字段

Java代码 
  1. public class Elvis {  
  2.   
  3.     public static final Elvis INSTANCE = new Elvis();  
  4.       
  5.     private Elvis(){;;;}  
  6.       
  7.     public void leaveTheBuilding() {;;;}  
  8.   
  9. }  

 

  2.暴露静态方法

Java代码 
  1. public class Elvis {  
  2.   
  3.     private static final Elvis INSTANCE = new Elvis();  
  4.       
  5.     private Elvis(){;;;}  
  6.       
  7.     public static Elvis getInstance() { return INSTANCE;}  
  8.     public void leaveTheBuilding() {;;;}  
  9.   
  10. }  

 

    如果借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器,抵御这种方法可以在创建第二次构造器时抛出异常来解决。

 

    自定义异常:

Java代码 
  1. public class InitializationException extends RuntimeException {  
  2.   
  3.     /** 
  4.      *  
  5.      */  
  6.     private static final long serialVersionUID = 1L;  
  7.   
  8.     public InitializationException(String msg) {  
  9.         super(msg);  
  10.     }  
  11. }  

 

    单例类:

Java代码 
  1. public class Singleton {  
  2.   
  3.     private static int count = 0;  
  4.     private static final Singleton INSTANCE = new Singleton();  
  5.       
  6.     private Singleton() {  
  7.         if(count == 1)   
  8.             throw new InitializationException("只能初始化一次!");  
  9.         count++;  
  10.     }  
  11.       
  12.     public static Singleton getInstance() {  
  13.         return INSTANCE;  
  14.     }  
  15. }  

 

    测试类:

Java代码 
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) throws Exception{  
  4.           
  5.         Singleton s1 = Singleton.getInstance();  
  6.         Singleton s2 = Singleton.getInstance();  
  7.           
  8.         System.out.println(s1 == s2);   // 返回 true  
  9.           
  10.         // 试图通过反射机制创建实例  
  11.         for(Constructor<?> c : s1.getClass().getDeclaredConstructors()) {  
  12.             c.setAccessible(true);  // AccessibleObject  
  13.             Singleton s3 = (Singleton)c.newInstance();  
  14.         }  
  15.     }  
  16. }  

 

    抛出的异常:

Console代码 
  1. Exception in thread "main" java.lang.reflect.InvocationTargetException  
  2.     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
  3.     at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)  
  4.     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)  
  5.     at java.lang.reflect.Constructor.newInstance(Unknown Source)  
  6.     at org.reflect.Test.main(Test.java:17)  
  7. Caused by: org.reflect.InitializationException: 只能初始化一次!  
  8.     at org.reflect.Singleton.<init>(Singleton.java:10)  
  9.     ... 5 more  

 

    JDK1.5及以后,增加了实现Singleton的第三种方法。只需编写一个包含单个元素的枚举类型。

 

    

Java代码 
  1. public enum Adam {  
  2.   
  3.     INSTANCE;   //只有一个元素  
  4.       
  5.     public void leaveTheBuilding() {;;;}   
  6. }  

 

    使用这种方法的好处是可以防止多次实例化,无偿提供了序列化机制,即使是面对复杂的序列化或者反射公鸡。

    当你试图通过反射调用枚举类型的构造器时(默认构造器为private),如果调用了setAccessible(true)方法,将会抛出IllegalArgumentException:

Java代码 
  1. Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects  
  2.     at java.lang.reflect.Constructor.newInstance(Unknown Source)  
  3.     at org.reflect.Test.main(Test.java:23)  

 

    对于前两种单例模式,为了使Singleton能够序列化,除了实现标记接口Serializable外,还需增加类似下面的方法,防止反序列化时生成“假冒”的单例类:

Java代码 
  1. private Object readResolve() {  
  2.     return INSTANCE;  
  3. }  

     而对于枚举类型,完全不用多此一举。因为枚举类型已经提供了该机制。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值