主頁 > 後端開發 > 面試問反射 你能跟面試官聊多少呢

面試問反射 你能跟面試官聊多少呢

2021-10-13 22:17:06 後端開發

本來打算寫一篇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 有興趣的讀者自行研究~~

四、嘮嘮

我看過一些原始碼,其實一般都只會用到

反射可能會帶來一些安全問題,我們一般在重構專案或者是處理一個很復雜的業務的時候才會使用,一般情況我們寫業務代碼用不到反射,

源代碼地址:
https://github.com/githubforliming/ReflectionTest.git
https://github.com/githubforliming/JavassistTest.git

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/311720.html

標籤:java

上一篇:Swift如何在目標視圖加載前觸發"準備分離"?

下一篇:Java中int、double、char等基礎資料型別的取值范圍

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more