程序员

【基础总结】反射机制

2018-10-21  本文已影响3人  灰色孤星

源代码:https://gitee.com/AgentXiao/reflection
反射的要点:
1、反射的概念(是什么、有什么用)
2、获得Class类对象
3、使用反射操作基本方法、构造方法、属性
4、反射的效率问题
5、使用反射操作泛型和注解

一、什么是反射机制

1、指的是可以在运行时加载、探知、使用编译期间完全未知的
2、程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
3、加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
4、java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身。
5、当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。
6、Class类是Reflection的根源。

二、获得Class类的对象

/**
 * @ClassName Demo01
 * @Description 测试获得Class类的对象
 * @Author xwd
 * @Date 2018/10/19 16:13
 */
public class Demo01 {
    public static void main(String[] args) {
        String computer = "pri.xiaowd.reflection.Computer";

        try {
            Class c1 = Class.forName(computer);
            Class c2 = new Computer().getClass();
            Class c3 = Computer.class;

            System.out.println(c1.hashCode());
            System.out.println(c2.hashCode());
            System.out.println(c3.hashCode());

            System.out.println(computer.getClass().hashCode() == String.class.hashCode());

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

以上代码的执行结果:

测试反射机制

三、获得类的基本信息

/**
 * @ClassName Demo02
 * @Description 获得类的基本信息(类名、构造器、方法、属性)
 * @Author xwd
 * @Date 2018/10/19 16:36
 */
public class Demo02 {
    public static void main(String[] args) {
        String computer = "pri.xiaowd.reflection.Computer";

        try {
            Class clazz = Class.forName(computer);

            //获得类名
            String className = clazz.getName();  //pri.xiaowd.reflection.Computer
            String classSimpleName = clazz.getSimpleName();  //Computer

            System.out.println(className);
            System.out.println(classSimpleName);

            //获得构造器
            //Constructor[] constructors = clazz.getConstructors();//只获得public修饰的构造器
            Constructor[] constructors = clazz.getDeclaredConstructors();//获得所有定义的构造器
            for(Constructor constructor:constructors){
                System.out.println("构造器:"+constructor);
            }
            //考虑重载问题
            Constructor c1 = clazz.getConstructor(null);//获得空构造
            Constructor c2 = clazz.getConstructor(String.class,String.class);//获得带参构造

            //获得方法
            //Method[] methods = clazz.getMethods();//只获得public修饰的方法
            Method[] methods = clazz.getDeclaredMethods();//获得全部方法
            for(Method method:methods){
                System.out.println("方法:"+method);
            }
            //考虑重载问题
            Method m1 = clazz.getMethod("getName",null);
            Method m2 = clazz.getMethod("setName",String.class);

            //获得属性
            //Field[] fields = clazz.getFields();//获得public修饰的属性
            Field[] fields = clazz.getDeclaredFields();//获得所有属性
            for(Field field:fields){
                System.out.println("属性:"+field);
            }
            Field f1 = clazz.getDeclaredField("name");//获得name属性


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、通过反射实现动态操作基本方法、构造方法、属性

/**
 * @ClassName Demo03
 * @Description 通过反射实现动态操作
 * @Author xwd
 * @Date 2018/10/19 17:13
 */
public class Demo03 {
    public static void main(String[] args) {
        String computer = "pri.xiaowd.reflection.Computer";

        try {
            Class<Computer> clazz = (Class<Computer>) Class.forName(computer);
            //通过反射调用无参构造
            Computer c1 = clazz.newInstance();
            System.out.println(c1);
            //通过反射调用有参构造
            Constructor constructor = clazz.getConstructor(String.class,String.class);
            Computer c2 = (Computer) constructor.newInstance("001","xwd");
            System.out.println(c2.getId());

            //通过反射API调用普通方法
            Method method = clazz.getDeclaredMethod("setName", String.class);
            method.invoke(c1,"lsm");
            System.out.println(c1.getName());

            //通过反射API可以直接访问属性
            Field f = clazz.getDeclaredField("id");
            f.setAccessible(true);//不需要再进行安全检查,可以直接访问私有属性
            f.set(c1,"000001");
            System.out.println(c1.getId());
            System.out.println(f.get(c1));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

五、反射的效率

使用setAccessible(true)禁用安全检查可以加快反射的效率。

/**
 * @ClassName Demo04
 * @Description 测试反射的效率
 * @Author xwd
 * @Date 2018/10/20 20:52
 */
public class Demo04 {
    /**
     * @MethodName test1
     * @Descrition 测试直接调用情况下的耗时
     * @Param []
     * @return void
     */
    public static void test1(){
        Computer c = new Computer();

        long start = System.currentTimeMillis();
        for(int i=0;i<1000000000L;i++){
            c.getId();
        }
        long end = System.currentTimeMillis();
        System.out.println("执行10亿次getId(),直接调用耗时:"+(end - start));
    }
    /**
     * @MethodName test2
     * @Descrition 使用反射机制调用,不禁用安全检查
     * @Param []
     * @return void
     */
    public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Computer c = new Computer();
        Class clazz = c.getClass();
        Method getName = clazz.getMethod("getName");
        long start = System.currentTimeMillis();
        for(int i=0;i<1000000000L;i++){
            getName.invoke(c,null);
        }
        long end = System.currentTimeMillis();
        System.out.println("执行10亿次getId(),使用反射机制调用,不禁用安全检查耗时:"+(end - start));
    }
    /**
     * @MethodName test3
     * @Descrition 使用反射机制调用,禁用安全检查
     * @Param []
     * @return void
     */
    public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Computer c = new Computer();
        Class clazz = c.getClass();
        Method getName = clazz.getMethod("getName");
        getName.setAccessible(true);
        long start = System.currentTimeMillis();
        for(int i=0;i<1000000000L;i++){
            getName.invoke(c,null);
        }
        long end = System.currentTimeMillis();
        System.out.println("执行10亿次getId(),使用反射机制调用,禁用安全检查耗时:"+(end - start));
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test1();
        test2();
        test3();
    }
}

控制台的输出信息为:

测试反射的效率

六、反射操作泛型

Java采用 泛型擦除 的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
因为反射是操作加载好的类的,因此通过反射是无法直接操作泛型的。

/**
 * @ClassName Demo05
 * @Description 反射操作泛型
 * @Author xwd
 * @Date 2018/10/21 21:07
 */
public class Demo05 {

    public void test01(Map<String,Computer> map, List<Computer> list){
        System.out.println("Demo05.test01()");
    }

    public Map<Integer,Computer> test02(){
        System.out.println("Demo05.test02()");
        return null;
    }

    public static void main(String[] args) {
        try {
            //获得指定方法的参数泛型信息
            Method m1 = Demo05.class.getMethod("test01",Map.class,List.class);
            Type[] types = m1.getGenericParameterTypes();//获得带泛型的参数类型
            for(Type type:types){
                System.out.println(type);  //遍历参数类型
                if(type instanceof ParameterizedType){
                    //获得真正的泛型信息
                    Type[] genericTypes = ((ParameterizedType)type).getActualTypeArguments();
                    for(Type genericType:genericTypes){
                        System.out.println("参数泛型类型:"+genericType);
                    }
                }
            }

            System.out.println("*******************************");

            //获得指定方法的返回值泛型信息
            Method m2 = Demo05.class.getMethod("test02",null);
            Type returnTypes = m2.getGenericReturnType();//获得返回值的类型
            System.out.println(returnTypes);  //遍历参数类型
            if(returnTypes instanceof ParameterizedType){
                //获得真正的泛型信息
                Type[] genericTypes = ((ParameterizedType)returnTypes).getActualTypeArguments();
                for(Type genericType:genericTypes){
                    System.out.println("返回值泛型类型:"+genericType);
                }
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

控制台输出信息:

反射操作泛型

七、反射操作注解

详见【基础总结】注解Annotation

上一篇下一篇

猜你喜欢

热点阅读