四、反射机制

1. 定义

  • 反向探知,在程序运行过程中动态获取类的相关属性。
  • 对于给定的一个类,可以获取类的所有属性和方法。

2. 使用

public class User {
    public void sayHello() {
        System.out.println("hello");
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        Class<User> clazz = User.class; //获取一个类对象
        User user = clazz.newInstance(); //获取一个实例对象
        Method method = clazz.getDeclaredMethod("sayHello"); //获取类中的方法
        method.invoke(user); //通过反射执行方法
    }
}

3. 反射的优缺点

优点

  • 增加程序的灵活性,避免固有逻辑写死到程序中
  • 代码相当简洁,可以提高程序的复用性
public class Test {
    public static void main(String[] args) throws Exception {
        Animal animal = sayHello("cat");
        animal.sayHello();
    }
    // 现在Animal下有两个实现类,两个判断,但是如果新添加一个实现类,还需要再次修改这块代码,不够灵活
    public static Animal sayHello(String name) {
        if ("dog".equals(name)) {
            return new Dog();
        }
        if ("cat".equals(name)) {
            return new Cat();
        }
        return null;
    }
}

使用反射改进,增加灵活性

public class Test {
    public static void main(String[] args) throws Exception {
        Animal animal = sayHello(Cat.class.getName());
        animal.sayHello();
    }
    // 添加新的实现类,只需要传入新实现类的全路径名即可,不用修改这块代码
    public static Animal sayHello(String name) {
        try { // 根据类的全路径名反射获取类
            Class clazz = Class.forName(name);
            return (Animal) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

上面只是举例说明反射的灵活性,实际中并不建议这样用,可以用Java多态的特性“声明接口,创建实现类”实现这一点

public class Test {
    public static void main(String[] args) throws Exception {
        Animal animal = new Cat(); //声明接口Animal,创建实现类Cat
        Animal animal2 = new Dog(); //声明接口Animal,创建实现类Dog
        animal.sayHello();
        animal2.sayHello();
    }
}

缺点

  • 相比于直接调用,反射有比较大的性能消耗,速度比较慢
  • 内部暴露和安全隐患

性能比较

public class Test {
    public static void main(String[] args) throws Exception {
        long startTime = System.currentTimeMillis();
        int length = 10000;
        for (int i = 0; i < length; i++) {
            Animal animal = new Cat();
            animal.sayHello();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("对象调用"+ length +"次耗时:" + (endTime-startTime) + " ms");
        long startTime2 = System.currentTimeMillis();
        for (int i = 0; i < length; i++) {
            Animal animal = sayHello(Cat.class.getName());
            animal.sayHello();
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("反射调用"+ length +"次耗时:" + (endTime2-startTime2) + " ms");
    }
}

反射慢的原因

  • 调用了native方法
  • 每次newInstance都会做安全检查,比较耗时

4. 反射的操作

反射获取类对象的方式

Class clazz = Cat.class;
Class clazz2 = Class.forName("com.lucifer.tree.Cat");
Class clazz3 = new Cat().getClass();
Class clazz4 = clazz3.getClassLoader().loadClass("com.lucifer.tree.Cat");

反射获取类的相关结构

int clazzModifier = clazz.getModifiers(); // 获取类的修饰符 1
Package clazzPackage = clazz.getPackage(); // package com.lucifer.tree
String clazzName = clazz.getName(); // com.lucifer.tree.Cat
String canonicalName = clazz.getCanonicalName(); // com.lucifer.tree.Cat
String simpleName = clazz.getSimpleName(); // Cat
ClassLoader classLoader = clazz.getClassLoader(); // sun.misc.Launcher$AppClassLoader@18b4aac2
Class superClass = clazz.getSuperclass(); // class java.lang.Object
Annotation[] annotations = clazz.getAnnotations(); // [Ljava.lang.annotation.Annotation;@677327b6
Class<?>[] interfaces = clazz.getInterfaces(); //[Ljava.lang.Class;@14ae5a5
java.lang.reflect. Modifier
Modifier and TypeConstant FieldValue
public static final intABSTRACT1024
public static final intFINAL16
public static final intINTERFACE512
public static final intNATIVE256
public static final intPRIVATE2
public static final intPROTECTED4
public static final intPUBLIC1
public static final intSTATIC8
public static final intSTRICT2048
public static final intSYNCHRONIZED32
public static final intTRANSIENT128
public static final intVOLATILE64

反射获取类的属性

// 获取当前类和父类的公有属性
Field[] fields = clazz.getFields();
// 获取当前类所有属性,包括私有属性
Field[] fields2 = clazz.getDeclaredFields();
// 获取当前类和父类的公有方法
Method[] methods = clazz.getMethods();
// 获取当前类所有方法,包括私有方法
Method[] methods2 = clazz.getDeclaredMethods();

Cat cat = new Cat();
// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("miaomiao");
privateMethod.setAccessible(true); //获取私有方法需要放开访问权限,否则报错
privateMethod.invoke(cat);
// 调用静态方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null);

反射获取构造器

// 获取所有公有的构造器
Constructor<?>[] constructors = clazz.getConstructors();
// 获取所有构造器,包括私有的
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 根据参数类型获取指定构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
// 如果是私有构造器,需要放开访问权限
declaredConstructor.setAccessible(true);
// 可以通过构造器获取实例对象
declaredConstructor.newInstance("小花");
// 直接通过newInstance获取对象
Cat cat3 = (Cat) clazz.newInstance();
// 底层本质上还是通过构造器获取实例对象
tmpConstructor.newInstance((Object[])null);

单例的漏洞

问题:正常调用单例类对外提供的方法,只能获取到唯一的一个实例,但是通过反射机制可以获取到不同的实例

原因:反射可以调用私有的构造器造成的

// 通过单例对外提供的方法拿到唯一的实例
SingleInstanceClass instanceClass1 = SingleInstanceClass.getInstance();
SingleInstanceClass instanceClass2 = SingleInstanceClass.getInstance();
SingleInstanceClass instanceClass3 = SingleInstanceClass.getInstance();
// 通过反射获取私有构造器,可以获取到新的实例
Constructor declaredConstructor = instanceClass1.getClass().getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingleInstanceClass instanceClass4 = (SingleInstanceClass) declaredConstructor.newInstance();

解决方法:在私有构造中加入判断逻辑,结合异常处理

public class SingleInstanceClass {
    public static SingleInstanceClass instance;
    private SingleInstanceClass() {
        // 处理通过反射获取实例的漏洞
        if (instance != null) {
            throw new RuntimeException();
        }
    }
    public static SingleInstanceClass getInstance() {
        if (instance == null) {
            instance = new SingleInstanceClass();
        }
        return instance;
    }
}

反射的应用场景

  • jdbc封装
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mysql";
String user = "root";
String pwd = "root";
Connection connection = DriverManager.getConnection(url, user, pwd);
  • SpringIoC
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-ioc.xml");
User user = (User)ac.getBean("user");

// ioc的底层本质上还是反射
public Object getBean(String name) throws BeansException {
    this.assertBeanFactoryActive();
    return this.getBeanFactory().getBean(name);
}

protected final void refreshBeanFactory() throws BeansException {
    if (this.hasBeanFactory()) {
        this.destroyBeans();
        this.closeBeanFactory();
    }

    try {// 创建对应IOC容器对象
        DefaultListableBeanFactory beanFactory = this.createBeanFactory();
        beanFactory.setSerializationId(this.getId());
        this.customizeBeanFactory(beanFactory);
        // 将配置文件中的<bean>解析成一个BeanDefinition对象
        this.loadBeanDefinitions(beanFactory);
        synchronized(this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    } catch (IOException var5) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
    }
}

// 获取文件路径
InputStream inputStream = encodedResource.getResource().getInputStream();
// SAX 加载配置文件
Document doc = this.doLoadDocument(inputSource, resource);
// 将配置文件解析成BeanDefinition对象
this.registerBeanDefinitions(doc, resource);

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, "import")) {
        this.importBeanDefinitionResource(ele);
    } else if (delegate.nodeNameEquals(ele, "alias")) {
        this.processAliasRegistration(ele);
    } else if (delegate.nodeNameEquals(ele, "bean")) {
        this.processBeanDefinition(ele, delegate);
    } else if (delegate.nodeNameEquals(ele, "beans")) {
        this.doRegisterBeanDefinitions(ele);
    }
}

protected abstract Object createBean(String var1, RootBeanDefinition var2, Object[] var3) throws BeanCreationException;

Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);

BeanUtils.instantiateClass(constructorToUse, new Object[0]);
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException { 
    ReflectionUtils.makeAccessible(ctor);//放开访问权限
    return ctor.newInstance(args);//底层还是使用的反射机制,反射获取构造器获取对象实例
}
  • jdbcTemplate
  • Mybatis

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值