涉及反射与枚举,先跳转:
反射与枚举
首先写一个饿汉式的单例,利用反射创建对象,得到哈希code不一样的两个对象,说明,反射破坏了单例,步骤看注释:
import java.lang.reflect.Constructor;
//反射,
public class Reflex {
private Reflex(){
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Reflex Reflex;
public static Reflex getInstance(){
if (Reflex==null) {
synchronized (Reflex.class){
if (Reflex==null) {
Reflex=new Reflex();//不是一个原子性操作
}
}
}
return Reflex;
}
public static void main(String[] args) throws Exception {//异常抛到最大
Reflex instance=Reflex.getInstance();//正常创建
Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
declaredConstructor.setAccessible(true);//无视了私有的构造器
Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象
//如果被破坏,就是不一样的hashcode
System.out.println(instance);
System.out.println(instance2);
}
}
解决方法,在构造器中加入锁:
import java.lang.reflect.Constructor;
//反射,
public class Reflex {
private Reflex(){
synchronized (Reflex.class){
if (Reflex!=null) {
throw new RuntimeException("不要试图用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Reflex Reflex;
public static Reflex getInstance(){
if (Reflex==null) {
synchronized (Reflex.class){
if (Reflex==null) {
Reflex=new Reflex();//不是一个原子性操作
}
}
}
return Reflex;
}
public static void main(String[] args) throws Exception {//异常抛到最大
Reflex instance=Reflex.getInstance();//正常创建
Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
declaredConstructor.setAccessible(true);//无视了私有的构造器
Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象
//如果被破坏,就是不一样的hashcode
System.out.println(instance);
System.out.println(instance2);
}
}
可是如果在创建对象中,两个都是用反射来创建对象,那么得到的还是不一样的hashcode,所以:
//反射,
public class Reflex {
private static boolean bianliang=false;//红绿灯法,标识位,见多线程详解
private Reflex() {
synchronized (Reflex.class) {
if (bianliang == false) {//不通过反编译,是找不到这个关键字的
bianliang = true;
} else {
throw new RuntimeException("不要试图用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Reflex Reflex;
public static Reflex getInstance(){
if (Reflex==null) {
synchronized (Reflex.class){
if (Reflex==null) {
Reflex=new Reflex();//不是一个原子性操作
}
}
}
return Reflex;
}
public static void main(String[] args) throws Exception {//异常抛到最大
Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
declaredConstructor.setAccessible(true);//无视了私有的构造器
Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象
Reflex instance=declaredConstructor.newInstance();//通过反射来new一个对象
//如果被破坏,就是不一样的hashcode
System.out.println(instance);
System.out.println(instance2);
}
}
在构造器加入一个标识位,通过非当前的对象,谁都不知道,但假设找到了这个关键字但还是可以破坏的:
//反射,
public class Reflex {
private static boolean bianliang=false;//红绿灯法,标识位,见多线程详解
private Reflex() {
synchronized (Reflex.class) {
if (bianliang == false) {//不通过反编译,是找不到这个关键字的
bianliang = true;
} else {
throw new RuntimeException("不要试图用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Reflex Reflex;
public static Reflex getInstance(){
if (Reflex==null) {
synchronized (Reflex.class){
if (Reflex==null) {
Reflex=new Reflex();//不是一个原子性操作
}
}
}
return Reflex;
}
public static void main(String[] args) throws Exception {//异常抛到最大
Field bianliang = Reflex.class.getDeclaredField("bianliang");//获取字段
bianliang.setAccessible(true);//破坏
Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
declaredConstructor.setAccessible(true);//无视了私有的构造器
Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象
bianliang.set(instance2,false);//值改回来
Reflex instance=declaredConstructor.newInstance();//通过反射来new一个对象
//如果被破坏,就是不一样的hashcode
System.out.println(instance);
System.out.println(instance2);
}
}
单例又被破坏了,所以魔高一尺道高一丈,分析源码
这里告诉我们如果这个类型是一个枚举类型,那么告诉我们不能使用反射破坏枚举,枚举是jdk1.5出来的,自带单例模式
写个枚举:
测试,对象是否唯一
//枚举本身也是是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;//测试,对象是否唯一
}
}
class Test{
public static void main(String[] args) {
EnumSingle instance = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println(instance);
System.out.println(instance2);
}
}
target文件说,是个无参构造函数,我们测试一下是不是假的,尝试破坏枚举的单例:
import java.lang.reflect.Constructor;
//枚举本身也是是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;//测试,对象是否唯一
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(enumSingle);
}
}
注意报错,报的是这个枚举类里,没有一个空参的构造方法
正常的应该是告诉我们:
throw new IllegalArgumentException(“Cannot reflectively create enum objects”);
通过反编译:反编译calss文件,:javap -p EnumSingle .class(cmd)中,查看结果,没用,使用jad反编译
(使用jad -s java EnumSingle .class)
用了一个有参构造器,所以我们在代码中改成有参的参数
import java.lang.reflect.Constructor;
//枚举本身也是是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;//测试,对象是否唯一
}
}
class Test{
//Exception in thread "main" java.lang.NoSuchMethodException:
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(enumSingle);
}
}
得到结果(正确结果):
现在我们才知道,反射确实不能破坏枚举的单例