第五章 继承(2)

反射

        在Java领域中,反射是一个很强大有很复杂的机制。反射定义: 能够分析类能力的程序称为反射。

        Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

        反射的核心类Class类(类对象)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
        /**
         * 反射的三种方式:
         *  1、 Class clazz = People.class;
         *  2、 Class clazz2 = Class.forName("People");
         *  3、  People p2 = new People();
         *       Class clazz3 = p2.getClass();
         */
        // 反射获取类对象
        Class clazz = People.class;
        // 反射实例化
        People p = (People) clazz.newInstance();
        // 获取所有属性
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 设置属性可修改
            field.setAccessible(true);
            System.out.println(field.getName() + " : " + field.get(p)); // a : 10
            // 修改属性的值(破坏封装性private修饰的值也可被修改)
            field.set(p, 20);
            System.out.println(field.getName() + " : " + field.get(p)); // a : 20
        }
        // 获取所有方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            method.setAccessible(true);
            // 方法执行,私有和公有都能获取并执行。
            System.out.println(method.getName() + " : " + method.invoke(p, null));
            //test : 父类方法test()
            //pri : null
            //getStr : hello
        }
        //获取无参构造函数
        Constructor con = clazz.getDeclaredConstructor();
        //通过无参Constructor对象创建Person类的新实例
        Object obj = con.newInstance();
        System.out.println(obj); // People@79b4d0f
    }
}

class People {
    private int a = 10;

    private void pri() {

    }
    public String test() {
        return "父类方法test()";
    }
    public String getStr() {
        return "hello";
    }
}

  反射会破坏封装性,同时也会破坏单例模式。

public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
        Singleton singleton = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();
        System.out.println("singleton的hash值: " + System.identityHashCode(singleton) + " singleton2的hash值: "
                + System.identityHashCode(singleton2) + " 是否同一个对象: " + (singleton2 == singleton));
        //输出结果: singleton的hash值: 93122545 singleton2的hash值: 93122545 是否同一个对象: true
        Class clazz = Singleton.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton singleton3 = (Singleton) constructor.newInstance();
        System.out.println("singleton的hash值: " + System.identityHashCode(singleton) + " singleton3的hash值: "
                + System.identityHashCode(singleton3) + " 是否同一个对象: " + (singleton3 == singleton));
        //输出结果: singleton的hash值: 93122545 singleton3的hash值: 403424356 是否同一个对象: false 
    }
}

/**
 * 双重校验锁实现的单例模式
 */
class Singleton {
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

        防止单例被破坏的实现原理,反射破坏单例模式重点在于反射能够获取到单例的构造方法,并且调用构造方法构建对象。这种方式的防范可以在构造方法中加入校验:

/**
 * 双重校验锁实现的单例模式
 */
class Singleton {
    private volatile static Singleton singleton;
    private Singleton (){
        if(singleton != null){
            throw new RuntimeException("禁止反射调用构造方法创建对象");
        }
    }
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

实现的效果如下:

但是这种方式有一个重要前提,反射机制不能优先创建对象,否则还是会失效:

public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
        Class clazz = Singleton.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton singleton3 = (Singleton) constructor.newInstance();
        
        Singleton singleton = Singleton.getSingleton();
        System.out.println("singleton的hash值: " + System.identityHashCode(singleton) + " singleton3的hash值: "
                + System.identityHashCode(singleton3) + " 是否同一个对象: " + (singleton3 == singleton));
        //输出结果: singleton的hash值: 93122545 singleton3的hash值: 2083562754 是否同一个对象: false
    }

枚举

        比较完美的解决单例模式被反射破坏的一种方式是采用枚举方式。

public class Main {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
        SingletonEnum singleton = SingletonEnum.INSTANCE;
        SingletonEnum singleton2 = SingletonEnum.INSTANCE;
        System.out.println("singleton的hash值: " + System.identityHashCode(singleton) + " singleton2的hash值: "
                + System.identityHashCode(singleton2) + " 是否同一个对象: " + (singleton2 == singleton));
        // 输出结果: singleton的hash值: 93122545 singleton2的hash值: 93122545 是否同一个对象: true
        Class clazz = SingletonEnum.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonEnum singleton3 = (SingletonEnum) constructor.newInstance();
        System.out.println("singleton的hash值: " + System.identityHashCode(singleton) + " singleton3的hash值: "
                + System.identityHashCode(singleton3) + " 是否同一个对象: " + (singleton3 == singleton));
        // 抛出异常
    }
}

enum SingletonEnum {
    INSTANCE;
    public SingletonEnum getInstance() {
        return INSTANCE;
    }
}

与创建对象的顺序无关:

public class Main {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
        Class clazz = SingletonEnum.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonEnum singleton3 = (SingletonEnum) constructor.newInstance();
        SingletonEnum singleton = SingletonEnum.INSTANCE;
        System.out.println("singleton的hash值: " + System.identityHashCode(singleton) + " singleton3的hash值: "
                + System.identityHashCode(singleton3) + " 是否同一个对象: " + (singleton3 == singleton));
        // 输出结果: singleton的hash值: 93122545 singleton2的hash值: 93122545 是否同一个对象: true
    }
}

enum SingletonEnum {
    INSTANCE;
    public SingletonEnum getInstance() {
        return INSTANCE;
    }
}

执行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值