java反射讲解及反射优化

前言:反射是java语言的重要一部分,反射可以在运行期间动态执行访问类,方法及字段,会大大提高框架的灵活性,所以像很多开源框架大量使用反射动态生成对象,例如spring的bean,mybatis的mapper。但反射的性能一直是被诟病的。 java中创建对象有几种方式? 1.使用new关键字 2.使用clone方法 3.使用反序列化4.使用反射 5.使用Unsafe

1.一个小demo,使用反射实例化对象并且调用该对象里的方法
1.1 定义一个Student对象

/**
 * @Author feng ye
 * @Date 2021/6/30 0:48
 * @Description
 */
public class Student {
    public void toSleep(){
        System.out.println("toSleep方法被执行了...");
    }
}
复制代码

1.2定义一个属性文件内容是该类的全限定名以及类中方法

image.png

1.3 读取配置文件种的信息,使用反射实例化Student对象以及调用toSleep方法

/**
 * @Author feng ye
 * @Date 2021/6/30 0:41
 * @Description
 */
public class ReadSource {

    public static void main(String[] args) throws Exception{
        //  可以创建任意类的对象,可以执行任意方法
        //  前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReadSource.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        //3.加载该类进内存
        Class cls = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        Method[] methods = cls.getMethods();
        System.out.println("methods.length :"+ methods.length);
        for (Method m : methods){
            System.out.println("类名:"+cls.getSimpleName()+" ,方法名"+m.getName());
        }
        //6.执行方法
        method.invoke(obj);
    }
}
复制代码

执行结果:

image.png

上面就是使用反射结合spi的方式可以实现动态生成对象,我们只需要在配置文件替换类名和方法名称就行

2.反射之Class对象讲解
获取一个class对象有三种方式,虽然方式不同,但获取的都是同一个class对象。因为class文件只会被jvm加载一次

image.png

* Class对象功能:
   * 获取功能:
      1. 获取成员变量们
         * Field[] getFields() :获取所有public修饰的成员变量
         * Field getField(String name)   获取指定名称的 public修饰的成员变量

         * Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
         * Field getDeclaredField(String name)
      2. 获取构造方法们
         * Constructor<?>[] getConstructors()
         * Constructor<T> getConstructor(类<?>... parameterTypes)

         * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
         * Constructor<?>[] getDeclaredConstructors()
      3. 获取成员方法们:
         * Method[] getMethods()
         * Method getMethod(String name, 类<?>... parameterTypes)

         * Method[] getDeclaredMethods()
         * Method getDeclaredMethod(String name, 类<?>... parameterTypes)

      4. 获取全类名
         * String getName()
复制代码

3.反射之对Filed对象操作
3.1定义一个Person类

```
public class Person {

    public String a;
    protected String b;

    String c;
    String d = "我是d";

    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public Person() {
    }

    private Person(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("show方法被执行");
    }

    private void show2() {
        System.out.println("私有的 show2方法被执行");
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "a='" + a + ''' +
                ", b='" + b + ''' +
                ", c='" + c + ''' +
                ", d='" + d + ''' +
                ", age=" + age +
                ", name='" + name + ''' +
                '}';
    }

    public void eat() {
        System.out.println("eat...");
    }

    public void eat(String food) {
        System.out.println("eat..." + food);
    }

    private void sum(Integer num) {
        System.out.println("sum方法被执行了..." + num);
    }
}
```
复制代码

3.2 使用反射访问类中的不用权限的属性以及对属性赋值

/**
 * @Author feng ye
 * @Date 2021/6/30 0:23
 * @Description 使用反射获取Class对象的成员变量【属性】
 * 可以对其进行赋值,取值;包括不同的修饰可以对齐进行操作【public,protected,private】
 */
public class ReflectDemo02 {

    /**
     * Class对象功能:
     * 获取功能:
     * 获取成员变量们
     * Field[] getFields()
     * Field getField(String name)
     * Field[] getDeclaredFields()
     * Field getDeclaredField(String name)
     */
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
             1. 获取成员变量们
                 * Field[] getFields()
                 * Field getField(String name)

                 * Field[] getDeclaredFields()
                 * Field getDeclaredField(String name)
         */
        //1.Field[] getFields()获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println("遍历获取所有public修饰的成员变量field: " + field);
        }

        //2.Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a 的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println("获取person的属性a名称: "+value);
        //设置a的值
        a.set(p, "张三");
        System.out.println("设置person的属性a的值: " + p);

        System.out.println("#############分隔符############");

        //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //开启暴力访问,可以访问类的私有属性
            declaredField.setAccessible(true);
            Object a1 = declaredField.get(p);
            System.out.println("--------------->a1: " + a1);
            System.out.println("--------------->declaredField: " + declaredField);
        }
        //Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println("获取person类中属性d的值: "+value2);

    }

}
复制代码

执行结果

image.png

4.发射访问类中的方法

public class ReflectDemo04 {

    /**
     * Class对象功能:
     * 获取成员方法们:
     * Method[] getMethods()
     * Method getMethod(String name, Class<?>... parameterTypes)
     * Method[] getDeclaredMethods()
     * Method getDeclaredMethod(String name, Class<?>... parameterTypes)
     *
     * //获取类名
     * String getName()
     */
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);

        Method eat_method2 = personClass.getMethod("eat", String.class);
        //执行方法
        eat_method2.invoke(p, "饭");

        System.out.println("---------------分隔符1---------------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println("获取所有public修饰的方法,methodName: "+method);
        }

        System.out.println("---------------分隔符2---------------");

        //获取private修饰的sum方法
        Method sum = personClass.getDeclaredMethod("sum",Integer.class);
        sum.setAccessible(true);
        sum.invoke(p,1);

    }
复制代码

执行结果:

image.png

5.反射为什么会变慢
以下是oracle官网原话:
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
大意是说:反射的类型是动态解析的,这将导致JVM无法实施某些特定的优化(具体来说,就是我们常说的JIT优化),在性能敏感和频繁调用的方法上,应该尽量避免使用反射。

5.1静态加载和动态加载:下面是我们用notePad++编写的一个ClssLoad_.java文件,然后尝试用cmd去 javac去(分开截图了)

Image.png

Image.png

静态加载:就是在编译的时候将我们编写的java文件编译成class字节码文件,因为Dog是静态加载,如果该类不存在则会抛出异常[classNofoundException]

Image.png

动态加载:Person就是动态加载,所以即使当该Person不存在的时候,编译阶段也不会报错。 只有在执行到case "2",动态加载该Person类时才会报错。

结论:可以得出一个结论(为什么反射慢,影响性能)。因为反射是在动态加载的时候才会将对象的字节码文件加载到jvm。而且访问反射相关的属性,会有安全检查等耗时判断。【可以将动态加载对比的理解为java对象的懒加载,因为在动态加载的过程需要做一些事情,所以慢】

反射的优化:


作者:初心不忘976
链接:https://juejin.cn/post/7007410196330840071
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值