疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java 抵御反射调用私有构造器


 

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

  1.暴露静态final字段

  public class Elvis {

  public static final Elvis INSTANCE = new Elvis();

  private Elvis(){;;;}

  public void leaveTheBuilding() {;;;}

  }

  public class Elvis {

  public static final Elvis INSTANCE = new Elvis();

  private Elvis(){;;;}

  public void leaveTheBuilding() {;;;}

  }

  2.暴露静态方法

  public class Elvis {

  private static final Elvis INSTANCE = new Elvis();

  private Elvis(){;;;}

  public static Elvis getInstance() { return INSTANCE;}

  public void leaveTheBuilding() {;;;}

  }

  public class Elvis {

  private static final Elvis INSTANCE = new Elvis();

  private Elvis(){;;;}

  public static Elvis getInstance() { return INSTANCE;}

  public void leaveTheBuilding() {;;;}

  }

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

  自定义异常:

  public class InitializationException extends RuntimeException {

  /**

  *

  */

  private static final long serialVersionUID = 1L;

  public InitializationException(String msg) {

  super(msg);

  }

  }

  public class InitializationException extends RuntimeException {

  /**

  *

  */

  private static final long serialVersionUID = 1L;

  public InitializationException(String msg) {

  super(msg);

  }

  }

  单例类:

  public class Singleton {

  private static int count = 0;

  private static final Singleton INSTANCE = new Singleton();

  private Singleton() {

  if(count == 1)

  throw new InitializationException("只能初始化一次!");

  count++;

  }

  public static Singleton getInstance() {

  return INSTANCE;

  }

  }

  public class Singleton {

  private static int count = 0;

  private static final Singleton INSTANCE = new Singleton();

  private Singleton() {

  if(count == 1)

  throw new InitializationException("只能初始化一次!");

  count++;

  }

  public static Singleton getInstance() {

  return INSTANCE;

  }

  }

  测试类:

  public class Test {

  public static void main(String[] args) throws Exception{

  Singleton s1 = Singleton.getInstance();

  Singleton s2 = Singleton.getInstance();

  System.out.println(s1 == s2); // 返回 true

  // 试图通过反射机制创建实例

  for(Constructor c : s1.getClass().getDeclaredConstructors()) {

  c.setAccessible(true); // AccessibleObject

  Singleton s3 = (Singleton)c.newInstance();

  }

  }

  }

  public class Test {

  public static void main(String[] args) throws Exception{

  Singleton s1 = Singleton.getInstance();

  Singleton s2 = Singleton.getInstance();

  System.out.println(s1 == s2); // 返回 true

  // 试图通过反射机制创建实例

  for(Constructor c : s1.getClass().getDeclaredConstructors()) {

  c.setAccessible(true); // AccessibleObject

  Singleton s3 = (Singleton)c.newInstance();

  }

  }

  }

  抛出的异常:

  Exception in thread "main" java.lang.reflect.InvocationTargetException

  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

  at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)

  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)

  at java.lang.reflect.Constructor.newInstance(Unknown Source)

  at org.reflect.Test.main(Test.java:17)

  Caused by: org.reflect.InitializationException: 只能初始化一次!

  at org.reflect.Singleton.(Singleton.java:10)

  ... 5 more

  Exception in thread "main" java.lang.reflect.InvocationTargetException

  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

  at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)

  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)

  at java.lang.reflect.Constructor.newInstance(Unknown Source)

  at org.reflect.Test.main(Test.java:17)

  Caused by: org.reflect.InitializationException: 只能初始化一次!

  at org.reflect.Singleton.(Singleton.java:10)

  ... 5 more

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

  public enum Adam {

  INSTANCE; //只有一个元素

  public void leaveTheBuilding() {;;;}

  }

  public enum Adam {

  INSTANCE; //只有一个元素

  public void leaveTheBuilding() {;;;}

  }

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

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

  Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects

  at java.lang.reflect.Constructor.newInstance(Unknown Source)

  at org.reflect.Test.main(Test.java:23)

  Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects

  at java.lang.reflect.Constructor.newInstance(Unknown Source)

  at org.reflect.Test.main(Test.java:23)

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

  private Object readResolve() {

  return INSTANCE;

  }

  private Object readResolve() {

  return INSTANCE;

  }

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