关于java:面试官问你反射你能回答多少

23次阅读

共计 19870 个字符,预计需要花费 50 分钟才能阅读完成。

原本打算写一篇 Mybatis 的 Mapper 代理源码简略浏览,发现其中有用到动静代理,想着要不先写一篇动静代理吧,后果发现 Jdk 的动静代理波及到反射的常识,所以最初决定写一篇反射相干的文章。

读者敌人,在学习技术的时候千万不要像我写文章这样,学一个知识点不要被其余知识点困死,陷入有限循环中。

对于动静代理和反射大略晓得做啥的就能妥妥的看 Mybatis 的源码。

一、对于反射的了解

1. 什么是反射

Java 的反射机制是运行状态中,对于任意一个类,都可能晓得这个类的所有属性和办法。对于任意一个对象,都可能调用他的任意办法、获取他的属性、批改局部类信息,这种动静获取信息以及动静调用对象办法的性能成为 Java 语言的反射机制。

一句话就是:运行时动静调用某个类或对象的办法、获取属性、批改局部类信息等。

2. 什么时候用

我了解的第一是获取某个类的公有属性或办法,第二是很多框架中通过配置文件加载 Java 对象的时候须要。

3. 还有什么

不要陷死到实践中,就很不错了。

二、反射我来了

小白鼠类:

package test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
@MyAnotation("测试注解")
public class Mouse {public Mouse(){}
    private Mouse(String name) {this.name = name;}
    // 公有变量
    private String password = "123456";
    @MyFiledAnotation("字段注解测试")
    public String name = "小小太阳";

    //
    public void say(String msg){System.out.println(msg+"叨逼叨叨逼叨...");
    }

    //
    public void run(){System.out.println("奔跑吧, 向着太阳奔跑...");
    }

    @MyMethodAnotation("办法注解测试")
    private void takeAShower(){System.out.println("脱光光,洗白白...");
    }

    @Override
    public String toString() {
        return "Mouse{" +
                "password='" + password + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}
package test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程  
 * 分享一个生存在互联网底层做着增删改查的码农的感悟与学习
 * ---》类注解 
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnotation {String value() default "默认值";
}
package test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程  
 * 分享一个生存在互联网底层做着增删改查的码农的感悟与学习
 * --》办法注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnotation {String value() default "默认值";
}
package test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 分享一个生存在互联网底层做着增删改查的码农的感悟与学习
 * --> 字段注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFiledAnotation {String value() default "默认值";
}
2.1 获取 Class 对象
import test.Mouse;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Test {public static void main(String[] args) throws ClassNotFoundException {
        // 形式 1 通过对象获取
        Mouse mouse = new Mouse();
        Class<?> c1 = mouse.getClass();
        // 形式 2 通过 forName
        Class<?> c2 = Class.forName("test.Mouse");
        // 形式 3 间接通过类获取
        Class<Mouse> c3 = Mouse.class;

        System.out.println("c1 == c2 :" + (c1 == c2));
        System.out.println("c2 == c3 :" + (c2 == c3));
        
        // 输入:// c1 == c2 :true
        // c2 == c3 :true
    }
}
2.2 看看 Class 都有哪些货色
import test.MyFiledAnotation;
import test.MyMethodAnotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Test02 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {Class<?> c2 = Class.forName("test.Mouse");

        // 1. 类名
        System.out.println("包名. 类名"+c2.getName());
        System.out.println("类名"+c2.getSimpleName());
        System.out.println("包名. 类名"+c2.getTypeName());
        System.out.println("包名. 类名"+c2.getCanonicalName());

        // 2. 获取所有属性 包含 private
        // c2.getFields() 不能获取 private 的属性
        Field[] declaredFields = c2.getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {Field filed = declaredFields[i];
            System.out.println("字段名称:"+filed.getName());
            Annotation[] annotations = filed.getAnnotations();
            for (int j = 0; j < annotations.length; j++) {Annotation annotation = annotations[j];
                System.out.println(filed.getName()+"的注解:"+annotation);
                if (annotation instanceof MyFiledAnotation) {MyFiledAnotation my = (MyFiledAnotation) annotation;
                    System.out.println("能够依据注解获取自定义相干内容:"+my.value());
                }
            }
        }
        //
        // 2.2  通过指定字段名获取
        // 抛异样:java.lang.NoSuchFieldException  getField 不能获取 private 属性
        try {Field name01 = c2.getField("password");
        } catch (Exception e) {System.out.println("异样:"+e);
        }
        // 2.3 getDeclaredField 能够获取 private 的属性
        Field name02 = c2.getDeclaredField("password");
        System.out.println("getField 能够获取 private 属性:"+name02);

        // 3. 获取所有办法
        // getMethods 获取不到 private 的办法,getDeclaredMethods 能够
        Method[] methods = c2.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {Method method = methods[i];
            String name = method.getName();
            // 无关的办法 wait equals hashCode toString getClass notify notifyAll 等
            if (!"say".equals(name) && !"run".equals(name) && !"takeAShower".equals(name)) {continue;}
            System.out.println("办法名:"+method.getName());
            Annotation[] annotations = method.getAnnotations();
            for (int j = 0; j < annotations.length; j++) {Annotation annotation = annotations[j];
                System.out.println(name+"注解:"+annotation);
                if(annotation instanceof MyMethodAnotation) {MyMethodAnotation my = (MyMethodAnotation) annotation;
                    System.out.println("能够依据注解获取自定义相干内容:"+my.value());
                }
            }
        }
        // 3.2 获取指定办法  getDeclaredMethod(办法名,可变长度参数列表)
        // 与获取 Filed 一样 getMethod(办法名,可变长度参数列表) 也不能获取
        Method say = c2.getDeclaredMethod("say", String.class);
        Method run = c2.getDeclaredMethod("run", null);
        System.out.println("say:"+say);
        System.out.println("run:"+run);

        // 4. 非凡的办法 -》构造方法
        // 同理 getConstructors 不能获取 private 的构造方法
        Constructor<?>[] constructors = c2.getDeclaredConstructors();
        for (int i = 0; i < constructors.length; i++) {Constructor<?> c = constructors[i];
            System.out.println(c);
        }
        // 4.2 依据结构函数参数获取构造函数 个别间接用 DeclaredXXX
        Constructor<?> constructor = c2.getDeclaredConstructor(String.class);
        System.out.println("获取参数是 String 的构造函数:"+constructor);
    }
}

输入后果:包名. 类名 test.Mouse
类名 Mouse
包名. 类名 test.Mouse
包名. 类名 test.Mouse
字段名称:password
字段名称:name
name 的注解:@test.MyFiledAnotation(value= 字段注解测试)
能够依据注解获取自定义相干内容:字段注解测试
异样:java.lang.NoSuchFieldException: password
getField 能够获取 private 属性:private java.lang.String test.Mouse.password
办法名:run
办法名:say
办法名:takeAShower
takeAShower 注解:@test.MyMethodAnotation(value= 办法注解测试)
能够依据注解获取自定义相干内容: 办法注解测试
say:public void test.Mouse.say(java.lang.String)
run:public void test.Mouse.run()
public test.Mouse()
private test.Mouse(java.lang.String)
获取参数是 String 的构造函数:private test.Mouse(java.lang.String)

留神:肯定要本人亲自运行代码试试 看到的永远是你感觉会了的,然而不肯定是你会了的。
2.3 动静调用
import test.Mouse;

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

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Test03 {public static void main(String[] args) throws Exception {Class<?> c2 = Class.forName("test.Mouse");

        // 1. 动静创建对象
        // 1.1 间接调用 newInstance
        Mouse o1 = (Mouse)c2.newInstance();
        System.out.println(o1);
        // 1.2 通过结构
        Mouse o2 = (Mouse)c2.getConstructor().newInstance();
        System.out.println(o2);

        // can not access a member of class test.Mouse with modifiers "private"
        // 应答策略:setAccessible(true)

        Constructor<?> dc = c2.getDeclaredConstructor(String.class);
        dc.setAccessible(true);
        Mouse o3 = (Mouse)dc.newInstance("结构反射创建对象 name 字段值");
        System.out.println(o3);

        // 2. 获取属性值 / 批改属性值
        // 获取 password 不能间接获取
        // System.out.println(o3.password);
        Field password = c2.getDeclaredField("password");
        // 一般属性不须要设置 Accessible
        password.setAccessible(true);
        System.out.println("反射获取公有属性:"+ password.get(o3));
        // 批改属性值
        password.setAccessible(true);
        password.set(o3, "---------- 通过反射批改咯 --------");
        System.out.println("反射批改后的属性值:"+ password.get(o3));

        // 3. 动静调用办法(这个十分重要了 动静代理就会用到)// 3.1 一般办法
        Method say = c2.getDeclaredMethod("say", String.class);
        say.invoke(o3, "说点什么呢:");
        // 3.2 公有办法
        Method takeAShower = c2.getDeclaredMethod("takeAShower");
        // 解决 Class Test03 can not access a member of class test.Mouse with modifiers "private"
        takeAShower.setAccessible(true);
        takeAShower.invoke(o3);

    }
}
2.4 反射获取注解

其实这个咱们曾经在上边写过了,这里对立写一下简略应用,包含字段、办法、类注解。

import test.Mouse;
import test.MyAnotation;
import test.MyFiledAnotation;
import test.MyMethodAnotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Tes04 {public static void main(String[] args) throws Exception {Class<?> c2 = Class.forName("test.Mouse");
        // 1. 获取类注解
        // 1.1 获取所有注解
        Annotation[] annotations = c2.getAnnotations();
        for (int i = 0; i <annotations.length ; i++) {Annotation annotation = annotations[i];
            System.out.println(annotation);
            // 通过 instanceof 来判断是否是本人须要的注解
            if (annotation instanceof MyAnotation) {MyAnotation my = (MyAnotation)annotation;
                // 获取本人注解的办法 我之前写过一个文章 redis 锁避免反复点击 就用到了自定义注解
                System.out.println(my.value());
            }
        }
        // 1.2 获取指定注解
        MyAnotation myannotation = c2.getAnnotation(MyAnotation.class);
        System.out.println(myannotation.value());

        // 2. 获取办法注解
        // 应用 getMethod 会报错 ---》Exception in thread "main" java.lang.NoSuchMethodException: test.Mouse.takeAShower()
        // 上边说过了 getMethod 不能获取 private 办法
        Method takeAShower = c2.getDeclaredMethod("takeAShower");
        // 2.1 获取所有注解
        Annotation[] MethodAnnotations = takeAShower.getAnnotations();
        for (int i = 0; i < MethodAnnotations.length; i++) {Annotation annotation = MethodAnnotations[i];
            System.out.println(annotation);
            // 通过 instanceof 来判断是否是本人须要的注解
            if (annotation instanceof MyMethodAnotation) {MyMethodAnotation my = (MyMethodAnotation)annotation;
                System.out.println(my.value());
            }
        }
        // 2.2 指定注解获取
        MyMethodAnotation myAnnotation = takeAShower.getAnnotation(MyMethodAnotation.class);
        System.out.println(myAnnotation);

        // 3. 字段注解

        Field name = c2.getDeclaredField("name");
        // 3.1 获取指定字段全副注解
        Annotation[] FiledAnnotations = name.getAnnotations();
        for (int i = 0; i < FiledAnnotations.length; i++) {Annotation annotation = FiledAnnotations[i];
            System.out.println(annotation);
            // 通过 instanceof 来判断是否是本人须要的注解
            if (annotation instanceof MyFiledAnotation) {MyFiledAnotation my = (MyFiledAnotation)annotation;
                System.out.println(my.value());
            }
        }
        // 3.2 获取指定类型注解
        MyFiledAnotation annotation = name.getAnnotation(MyFiledAnotation.class);
        System.out.println("获取指定注解:"+annotation.value());

    }
}
2.5 泛型

看到一篇文章里边写了反射操作泛型,我也尝试简略写写

package test;

import java.beans.IntrospectionException;
import java.util.List;
import java.util.Map;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Person extends CC implements AA {public Map<String,Mouse> test(List<Short> list, DD<Mouse> dd, Integer i) {return null;}
}

interface AA extends BB{

}
interface BB{

}
class CC{

}
package test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class DD<T> {

}
import test.DD;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Test05 {public static void main(String[] args) throws Exception {Class<?> c2 = Class.forName("test.Person");

        // 1. 获取类实现的接口
        Type[]  interfaces = c2.getGenericInterfaces();
        for (int i = 0; i < interfaces.length; i++) {System.out.println(interfaces[i]);
        }
        // 2. 获取类继承的父类 如果没有 extends CC 这里会获取到 java.lang.Object
        // 也就是说他至多会输入一个类 java.lang.Object 兜底
        Type  t = c2.getGenericSuperclass();
        System.out.println(t);

        // 3.1 某个办法的参数泛型
        Method test = c2.getMethod("test", List.class, DD.class, Integer.class);
        Type[] genericParameterTypes = test.getGenericParameterTypes();
        for (int i = 0; i < genericParameterTypes.length; i++) {Type gt = genericParameterTypes[i];
            System.out.println("没过滤:"+gt);
            if (gt instanceof ParameterizedType) {System.out.println("过滤:"+gt);
                ParameterizedType pgt = (ParameterizedType) gt;
                Type[] arg = pgt.getActualTypeArguments();
                for (int j = 0; j < arg.length; j++) {System.out.println("过滤后获取的实在泛型类型:"+arg[j]);
                }
            }
        }

        // 3.2 某个办法的返回值泛型
        Type grt = test.getGenericReturnType();
        System.out.println("grt:"+grt);
        if (grt instanceof  ParameterizedType) {ParameterizedType pgt = (ParameterizedType) grt;
            Type[] arg = pgt.getActualTypeArguments();
            for (int j = 0; j < arg.length; j++) {System.out.println("返回实在泛型类型:"+j+" "+arg[j]);
            }
        }

    }
}

三、扩大

3.1 org.reflections.reflections

reflections 能够扫描出指定包下的指定类

示例:

pom.xml 引入

<dependencies>
    <dependency>
        <groupId>org.reflections</groupId>
        <artifactId>reflections</artifactId>
        <version>0.9.11</version>
    </dependency>
</dependencies>
package com.freeedu.test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Father {

}

package com.freeedu.test;

import com.sun.istack.internal.NotNull;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 * @create 2021-10-10 15:07
 */
@MyClassAnnotation
public class Person extends Father{

    @MyMethodAnotation
    public void test01(String str, Integer integer) { }

    @MyMethodAnotation
    public String test02(@MyParamterAnotation String str, Integer integer) {return null;}
}

package com.freeedu.test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程  分享一个生存在互联网底层做着增删改查的码农的感悟与学习
 */
public @interface MyClassAnnotation {
}
package com.freeedu.test;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程  分享一个生存在互联网底层做着增删改查的码农的感悟与学习
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={METHOD})
public @interface MyMethodAnotation {
}

package com.freeedu.test;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.METHOD;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程  分享一个生存在互联网底层做着增删改查的码农的感悟与学习
 * @create 2021-10-10 15:39
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.PARAMETER})
public @interface MyParamterAnotation {
}

下边我简略写集中我本人玩儿的办法,如果有其余须要间接. 提醒猜想有没有对应办法

或者点进源码看看,你须要的大概率这里都能提供

import com.freeedu.test.*;
import com.sun.istack.internal.NotNull;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class Test {public static void main(String[] args) {
        // 初始化 默认扫描 com.freeedu.test 包
        Reflections reflections = new Reflections("com.freeedu.test");
        // 1. 扫描某些类的子类 Father 的子类
        Set<Class<? extends Father>> subTypesOf = reflections.getSubTypesOf(Father.class);
        subTypesOf.stream().forEach(System.out::println);

        // 2. 依据办法参数扫描合乎参数的办法
        // 扫描不同的类型 须要不同的扫描工具
        // 须要指定 setScanners(new MethodParamterScanner()) 否则报错:Scanner MethodParameterScanner was not configured
        reflections = new Reflections(new ConfigurationBuilder()
                .forPackages("com.freeedu.test") // 指定扫描包
                 // 指定多中扫描工具
                .setScanners(new MethodParameterScanner(),
                        new TypeAnnotationsScanner(),
                        new SubTypesScanner())
        );
        Set<Method> methodsMatchParams = reflections.getMethodsMatchParams(String.class, Integer.class);
        methodsMatchParams.stream().forEach(System.out::println);

        // 3. 获取类上有指定注解的类 class com.freeedu.test.Person
        // 同理能够获取办法、属性上都指定注解的办法和属性
        Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(MyClassAnnotation.class);
        typesAnnotatedWith.forEach(System.out::println);

        // 4. 获取指定返回值的办法
        Set<Method> methodsReturn = reflections.getMethodsReturn(String.class);
        methodsReturn.forEach(System.out::println);

        // 其实官网曾经给了咱们一个很好用的 Utils ---> ReflectionUtils
        // 获取某个类的办法 指定可见性 + 入参个数 + 前缀
        Set<Method> test = ReflectionUtils.getAllMethods(Person.class,
                ReflectionUtils.withModifier(Modifier.PUBLIC), // 修饰符
                ReflectionUtils.withPrefix("test"), // 办法前缀
                ReflectionUtils.withParametersCount(2),// 参数总数
                ReflectionUtils.withReturnType(String.class),// 返回值类型
                ReflectionUtils.withParameters(String.class, Integer.class),// 办法参数类型
                ReflectionUtils.withAnnotation(MyMethodAnotation.class),// 办法注解 为什么不辨认 我加上了呀~~!ReflectionUtils.withAnyParameterAnnotation(MyParamterAnotation.class)// 办法参数注解
                // 还有各式各样的过滤 有趣味或有须要的敌人能够本人找找本人感兴趣的
        );
        System.out.println("符合条件的办法:");
        test.forEach(System.out::println);

        //
    }
}

3.2 org.javassist.javassist

javassist 是一个很牛 X 的货色。

先搞一个 demo 大家瞅瞅, 传入一个 Map key 值对应实体类 TestPO 字段名称 Value 值对应实体类 TestPO 字段值

咱们怎么把数据设置到实体类中呢

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 */
public class TestPO {
    public Integer id;
    public String name;
    public Integer age;

    @Override
    public String toString() {
        return "TestPO{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

手动编码 (硬)+ 反射编码 (软)+ 高级反射编码 (软变硬)

package test;

import java.util.Map;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 * @create 2021-10-10 19:41
 */
public abstract class AbstractTransferHelper {public abstract Object transfer(Map map) throws Exception;
}

package test;

import javassist.*;

import java.io.IOException;
import java.lang.reflect.Field;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 * @create 2021-10-10 20:10
 */
public class TransferUtil {
    // 这里用到了 javaassist
    // 这个就是有近似于写死代码的性能 有近似于反射的适配性 如果再加字段 这里是不必批改的
    // 判空什么的就先不做了 次要讲应用形式
    public static AbstractTransferHelper getTransferHelper(Class clazz) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException, IOException {ClassPool pool = ClassPool.getDefault();
        pool.appendSystemPath();

        // 导包
        //import java.util.HashMap;
        //import java.util.Map;
        pool.importPackage("java.util.Map");
        pool.importPackage("java.util.HashMap");
        //import test.AbstractTransferHelper
        pool.importPackage("test.AbstractTransferHelper");
        //import test.TestPO;
        pool.importPackage(clazz.getName());
        pool.importPackage(AbstractTransferHelper.class.getName());

        // 父类
        CtClass superClass = pool.getCtClass(AbstractTransferHelper.class.getName());

        // 自定义动态创建的类名
        String className = clazz.getName()+"TransferHelper";
        // 创立类 指定父类 superClass
        // Class XXXTransferHelper extends AbstractTransferHelper
        CtClass myclass = pool.makeClass(className, superClass);

        // 构造函数 public XXXTransferHelper(){}
        CtConstructor ctConstructor = new CtConstructor(new CtClass[0], myclass);
        ctConstructor.setBody("{}");
        myclass.addConstructor(ctConstructor);

        // 办法 ---
        StringBuilder sb = new StringBuilder();
        sb.append("public Object transfer(Map map) throws Exception {\n");
        // 相似:TestPO obj = new TestPO();
        sb.append(clazz.getName() +"obj = new"+clazz.getName()+"();\n");
        // 设置属性值
        Field[] fields = clazz.getFields();
        String strF = "obj.%s = map.get(\"%s\") == null ? null : String.valueOf(map.get(\"%s\"));\n";
        String strI = "obj.%s = map.get(\"%s\") == null ? null : Integer.valueOf(map.get(\"%s\").toString());\n";
        for (int i = 0; i < fields.length; i++) {Field field = fields[i];
            String name = field.getName();
            Class<?> type = field.getType();
            // 这里只写 String Integer 类型 其余我就不写了
            if (type == String.class) {// 相似 obj.name = map.get("name") == null ? null : String.valueOf(map.get("name"));
                String format = String.format(strF, field.getName(), field.getName(), field.getName());
                sb.append(format);
            } else if (type == Integer.class) {// 相似 obj.name = map.get("name") == null ? null : Integer.valueOf(map.get("name").toString());
                String format = String.format(strI, field.getName(), field.getName(), field.getName());
                sb.append(format);
            }
        }

        sb.append("return obj;\n");
        sb.append("}");

        // 创立办法
        CtMethod method = CtMethod.make(sb.toString(), myclass);
        myclass.addMethod(method);
        // 创立实体
        Class aClass = myclass.toClass();

        // myclass.writeFile("E:\\MyNote\\test");
        System.out.println(aClass);
        return (AbstractTransferHelper)aClass.newInstance();}
}

import test.AbstractTransferHelper;
import test.TestPO;
import test.TransferUtil;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生存在互联网底层,做着增删改查的码农, 不谙世事的造作
 * @create 2021-10-10 16:24
 */
public class Test {public static void main(String[] args) throws Exception {
        // 参数
        /*Map<String,Object> map =  new HashMap<>();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {TestPO res = Method02(map, TestPO.class);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);*/

        Map<String,Object> map =  new HashMap<>();
        AbstractTransferHelper helper = TransferUtil.getTransferHelper(TestPO.class);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {TestPO res = Method03(map, helper);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }



    // 手动编码 一百万次 30 毫秒左右
    private static TestPO Method01(Map<String, Object> map, Class<TestPO> testPOClass) {TestPO res =  new TestPO();
        res.id = map.get("id") == null ? null : Integer.valueOf(map.get("id").toString()) ;
        res.name = map.get("name") == null ? null : String.valueOf(map.get("name").toString()) ;
        res.age = map.get("age") == null ? null : Integer.valueOf(map.get("age").toString()) ;
        return res;
    }

    // 反射 一百万次 200~300 毫秒
    // 这个有什么益处呢 如果增加字段 这个办法是不须要批改的 而 Method01 的硬编码是须要批改的
    private static <PO> PO Method02(Map<String, Object> map, Class<PO> clazz) throws Exception {Object res = clazz.newInstance();
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {Field field = fields[i];
            field.setAccessible(true);
            // 获取字段名称
            String name = field.getName();
            // 获取字段类型
            Class<?> type = field.getType();
            // 从 Map 中获取值
            if (type ==  Integer.class) {field.set(res, map.get(name) == null ? null : Integer.valueOf(Integer.valueOf(map.get(name).toString())));
            } else if(type ==  String.class){field.set(res, map.get(name) == null ? null : String.valueOf(String.valueOf(map.get(name))));
            }
        }
        return (PO) res;
    }

    // 反射 高级版 --> 软变硬 一百万次 40 毫秒左右
    private static <PO> PO Method03(Map<String, Object> map, AbstractTransferHelper helper) throws Exception {return  (PO) helper.transfer(map);
    }
}

javaassist 的性能远远大于我写的这个 demo 有趣味的读者自行钻研~~

四、唠唠

我看过一些源码,其实个别都只会用到 1. 动态创建实例 2. 动静调用办法

反射可能会带来一些平安问题,咱们个别在重构我的项目或者是解决一个很简单的业务的时候才会应用,个别状况咱们写业务代码用不到反射。

如果面试的时候问到了面试,你最好能提前准备一个工作中用反射解决我的项目问题的例子。

还能跟面试官谈到设计模式(动静代理),如果你都懂得话,能够跟面试官谈 Java 动静代理与 CGLIB 动静代理。

还能够联合你看过的某些框架源码中哪儿用到了反射,这样会让面试官感觉你是真的用过学过,而不是背过。

源代码地址:

https://github.com/githubforl…

https://github.com/githubforl…

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0