java 反射 私有构造,Java 抗御反射调用私有构造器

本文探讨了Java中单例模式的三种实现方式,包括静态final字段、静态方法和枚举法。重点介绍了如何通过反射机制防止多次实例化,并强调了枚举法的优势,如序列化安全性和不可反射创建。还讨论了如何确保序列化安全性和避免假冒单例的技巧。
摘要由CSDN通过智能技术生成

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

1.暴露静态final字段

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() {;;;}

}

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

自定义异常:

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 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

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

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)

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

private Object readResolve() {

return INSTANCE;

}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值