Java基础-反射-反射的概述

Java工程师知识树 / Java基础


反射(Reflection)的概述

反射的由来:

加载完类之后,在堆内存的方法区中就会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。我们可以通过这个对象看到这个类的结构,这个对象就像一面镜子,透过这个镜子看到了类的结构,所以,通过这样获取类信息的方式形象的称之为,反射

Java反射机制就是允许Java程序在运行期间借助Reflection API取得任何类的内部信息,并能直接操作对象的内部属性和方法。

使用反射获取对象以及之后操作的前提条件:必须先得到代表字节码的Class类,Class类用于表示.class文件(字节码)

反射在日常工作中用的倒不是很多,而被广泛应用于框架设计之中,反射是框架设计的灵魂。

反射的优缺点:

优点:反射可以实现动态创建对象和编译,体现出很大的灵活性
缺点:使用反射对性能有一定影响。慢与直接创建对象调用方法。

反射使用实例:

public class Test {
    public static void main(String[] args) throws Exception {
        long start1 = System.currentTimeMillis();
        User user = new User();
        for (int i = 0; i < 1000000; i++) {
            user.getName();
        }
        long end1 = System.currentTimeMillis();
        System.out.println("普通方式执行:" + (end1 - start1) + "ms");

        long start2 = System.currentTimeMillis();
        Class userClass = user.getClass();
        Method getName = userClass.getDeclaredMethod("getName", null);
        for (int i = 0; i < 10000000; i++) {
            getName.invoke(user, null);
        }
        long end2 = System.currentTimeMillis();
        System.out.println("反射方式执行:" + (end2 - start2) + "ms");

        long start3 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            getName.invoke(user, null);
        }
        long end3 = System.currentTimeMillis();
        System.out.println("反射方式关闭检测执行:" + (end3 - start3) + "ms");
    }
}

class User {
    private String name;

    public void getName() {

    }
}

获得Class类的方式

  • 如果已有具体的类,通过类的class属性获取,最为安全可靠且性能最高的方法。(Class xxx=XXX.class;)
  • 已知某个类的实例,调用此实例的getClass()方法获取Class对象。(Class xxx=xxx1.getClass();)
  • 已知一个类的全名且在类路径下,可以通过Class类的静态方法forName()获取,需要处理异常ClassNotFoundException(Class xxx=ClassforName(“yyy.XXX”);)
  • 内置基本数据类型可以直接使用类名.Type
  • 还可以用ClassLoader
public class TestReflection {

    public static void main(String[] args) throws ClassNotFoundException {
        //获取Class对象
        Student student = new Student();
        //Class xxx=XXX.class
        Class aClass = Student.class;
        //Class xxx=xxx1.getClass();
        Class bClass = student.getClass();
        //Class xxx=ClassforName(“yyy.XXX”);
        Class cClass = Class.forName("com.wechat.management.reflection.Student");
        //.Type
        Class integerClass = Integer.TYPE;
        Class intClass = int.class;
        //ClassLoader
        ClassLoader loader = ClassLoader.getSystemClassLoader();//获取加载器对象
        Class classString = loader.loadClass("java.lang.String");
        Class dClass = loader.loadClass("com.wechat.management.reflection.Student");

        System.out.println(aClass.hashCode());
        System.out.println(bClass.hashCode());
        System.out.println(cClass.hashCode());
        System.out.println(dClass.hashCode());
    }

}

class Student{
    private String name;

    public String getName() {
        return name;
    }

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

在运行期间,一个类,只有一个Class对象产生。

反射最常用的方式为Class xxx=ClassforName(“yyy.XXX”)
Class xxx=XXX.class对象都有了还要反射干什么。Class xxx=xxx1.getClass()需要导入类的包,依赖太强,不导包就抛编译错误。一般使用Class xxx=ClassforName(“yyy.XXX”),参数可以传入也可写在配置文件中。

Class类的常用方法

  • 类的构造方法:Class类提供了用于获取某个类的构造方法。
Constructor getConstructor(Class... parameterTypes)     根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors()     返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class... parameterTypes)     根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors()    返回该类中所有的构造函数数组(不分public和非public属性)
  • 类的属性:Class类提供了获取成员属性的方法
Field getField(String name)    根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields()    返回具有public属性的成员变量的数组
Field getDeclaredField(String name)    根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredFields()    返回所有成员变量组成的数组(不分public和非public属性)
  • 类的方法:Class类提供了获取成员方法的方法
Method getMethod(String name, Class... parameterTypes)    根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods()    返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class... parameterTypes)   根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods()    返回该类中的所有的方法数组(不分public和非public属性)

哪些类型可以有Class对象

  • class:外部类、成员(成员内部类、静态内部类),局部内部类,匿名内部类。
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解
  • primitive type:基本数据类型
  • void
  • .....
public class Test5 {
    public static void main(String[] args) {
        Class c1= Object.class;//类
        Class c2= Runnable.class;//接口
        Class c3= String[].class;//一维二维数组
        Class c4= int[][].class;//基本数据类型数组
        Class c5= Override.class;//注解
        Class c6= ElementType.class;//枚举
        Class c7= Integer.class;//基本数据类型
        Class c8= void.class;//void
        Class c9= Class.class;//Class
    }
}

setAccessible

setAccessible启动和禁用安全检查的开关,为了安全默认为false (启动)

setAccessible启动与关闭测试:

package com.wechat.management.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestSetAccessible {

    /**
     * 测试方法
     */
    public String getName() {
        return "test";
    }

    //普通方式调用
    public static void test01() {
        TestSetAccessible user = new TestSetAccessible();
        //开始时间
        long starTime = System.currentTimeMillis();

        //模拟10亿次不断获取名字所需要的时间
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }

        //结束时间
        long endTime = System.currentTimeMillis();

        System.out.println("普通方式执行10亿次:" + (endTime - starTime) + "ms");
    }

    //反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        TestSetAccessible user = new TestSetAccessible();
        Class c1 = user.getClass();

        //获取指定方法
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(false);//false为开启检测 默认为也是false,为了安全
        //开始时间
        long starTime = System.currentTimeMillis();

        //模拟10亿次不断获取名字所需要的时间
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }

        //结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:" + (endTime - starTime) + "ms");
    }

    //反射方式调用  关闭检测
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        TestSetAccessible user = new TestSetAccessible();
        Class c1 = user.getClass();

        //获取指定方法
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);//true为关闭检测
        //开始时间
        long starTime = System.currentTimeMillis();

        //模拟10亿次不断获取名字所需要的时间
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }

        //结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("关闭检测执行10亿次:" + (endTime - starTime) + "ms");
    }


    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();//普通方式执行10亿次:3ms
        test02();//反射方式执行10亿次:3226ms
        test03();//关闭检测执行10亿次:2011ms
        //如果反射调用次数多的话,可以关闭这个检测 提高程序的一个效率
    }
}

你可能感兴趣的:(Java基础-反射-反射的概述)