反射机制

反射机制

什么是反射

宏观上理解一下:我们对数据操作的时候,都要先知道这个数据是什么类型,是八种基本的数据类型还是引用类型,然后根据数据的类型创建变量或者对象进行操作,编译器就在编译阶段对代码进行检查的;但是对于反射,就不是在编译的时候获取数据的类型,而是在运行时获取不同数据的类型信息,然后根据这些信息创建对象,调用方法等。

Class类

所有的类都是Class类的实例——每一个运行时加载到内存中的都是Class类的一个对象——存放着不同的信息

这里的和平时说的数据类型中的类不太一样,这个涵盖了数据类型中的普通类,数组,接口,枚举,注解,基本数据类型,void关键字

这个Class类还有一个特别之处:虽然是类,但是没有public构造器,所有的该类对象都是由JVM装载的时候创建的,并且同一个只在JVM中有一个Class对象实例(对应的是.class文件)

Class对象举例

  • 普通类对应的Class的对象

    //获取了ArrayList类对应的Class对象
    Class<ArrayList> arrayListClass = ArrayList.class;
    
  • 定义的数组对应的Class对象

    int[] array = new int[10];
    Class<? extends int[]> aClass = array.getClass();
    
  • 接口对应的Class对象

    Class<List> listClass = List.class;
    
  • 基本数据类型和关键字void对应的Class对象

    Class<Integer> integerClass = int.class;
    Class<Short> shortClass = short.class;
    Class<Boolean> booleanClass = boolean.class;
    Class<Character> characterClass = char.class;
    Class<Void> voidClass = void.class;
    

获取Class对象的几种方法

  • 通过类获取:调用类的属性class获取,上面代码中Stirng类对应的Class对象就是这样

  • 有了某个类的对象,可以由这个对象的.getClass()方法获取,比如上面个数组的例子

  • 如果知道全类名(包名.类名),并且在这个类的类路径下,可以用Class的静态方法public static Class<?> forName(String className)获取

  • 通过类加载器

    ClassLoader cl = this.getClass().getClassLoader(); 
    Class clazz = cl.loadClass(“类的全类名”);
    

Class对象的使用

通过不同类对应的Class对象,可以得到关于类型的很多信息,获取的方式大同小异,下面一起列举一下:

1.获得关于类的结构和信息

  • 获取类名,包名

    public String getName()//类在Java内部真正的名字
    public String getSimpleName()//平时声明类时使用的类名,不带包名
    public String getCanonicalName()//上面那个的基础上带包名
    public Package getPackage()//包信息
    
  • 返回类中字段(静态变量和实例变量)信息——返回Field对象(它也对应好多方法,可以获取字段的例如名称,访问权限,字段值)

    //返回所有的public字段,包括其父类的,如果没有字段,返回空数组 
    public Field[] getFields() 
    //返回本类声明的所有字段,包括非public的,但不包括父类的 
    public Field[] getDeclaredFields() 
    //返回本类或父类中指定名称的public字段,找不到抛出异常NoSuchFieldException 
    public Field getField(String name) 
    //返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException 
    public Field getDeclaredField(String name)
    
  • 获取方法信息——返回Method对象

    //返回所有的public方法,包括其父类的,如果没有方法,返回空数组 
    public Method[] getMethods() 
    //返回本类声明的所有方法,包括非public的,但不包括父类的 
    public Method[] getDeclaredMethods() 
    //返回本类或父类中指定名称和参数类型的public方法, 
    //找不到抛出异常NoSuchMethodException 
    public Method getMethod(String name, Class<?>... parameterTypes) 
    //返回本类中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    

    除了字段和方法之外,根据反射Class的对象还可以获得运行时类的构造器(Constructor),父类(Superclass),接口(Interface),注解(Annotation)。类对应的修饰符等。其中MethodField Constructor都是java.lang.reflect包下面的类,他们还有对应的方法获取字段,方法,构造器自己本身的属性
    java.lang.reflect结构
    在这里插入图片描述

  • 创建运行时类的对象

    public T newInstance()
            throws InstantiationException, IllegalAccessException
    
    • 调用这个方法的要求
      • 这个运行时类必须有无参构造器
      • 构造器的权限要够
    • 如果没有无参构造器,可以通过上面说的,获取运行时类的Constructor结构,然后调用Constructor的方法创建对象

2.获取例如内部类,基本数据类型,数组等的信息

  • 给定一个Class对象,判断是什么类型的

    public native boolean isArray() //是否是数组
    public native boolean isPrimitive() //是否是基本类型 
    public native boolean isInterface() //是否是接口 
    public boolean isEnum() //是否是枚举 
    public boolean isAnnotation() //是否是注解 
    public boolean isAnonymousClass() //是否是匿名内部类 
    public boolean isMemberClass() //是否是成员类,成员类定义在方法外,不是匿名类 public boolean isLocalClass() //是否是本地类,本地类定义在方法内,不是匿名类
    

3.综合举例

//定义的相关结构
class Person implements Comparable{
    String name;
    int age;

    public Person() {
    }

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

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

    @Override
    public int compareTo(Object o) {
        if(o instanceof Person) {
            Person p = (Person) o;
            return (this.age == p.age) ? 0 : (this.age > p.age ? 1 : -1);
        }
        throw new RuntimeException("类型出错");
    }

    public void show(){
        System.out.println("我叫" + name);
    }
}

enum StudentList{
    STUDENT1("学生1",22),
    STUDENT2("学生2",23),
    STUDENT3("学生3",24),
    STUDENT4("学生4",25);

    private final String name;
    private final int id;
    private StudentList(String name,int id){
        this.name = name;
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public int getId() {
        return id;
    }
}


//测试及结果
public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();//无参构造器实例化的普通类
        Person p2 = new Person("人",12);//带参构造器实例化的普通类
        Person[] array = new Person[]{p1,p2};//数组
        int length = array.length;//基本数据类型
        /**
         * Class类对象的获取
         */
        //普通类获取对应Class类对象的方式
        Class<Person> personClass = Person.class;
        System.out.println(personClass);//控制台输出:class leetcode.Person
        Class<? extends Person> aClass = p1.getClass();
        System.out.println(aClass);//控制台输出:class leetcode.Person
        Class<?> aClass2 = Class.forName("leetcode.Person");
        System.out.println(aClass2);//控制台输出:class leetcode.Person
        ClassLoader classLoader = Person.class.getClassLoader();
        Class<?> aClass3 = classLoader.loadClass("leetcode.Person");
        System.out.println(aClass3);//控制台输出:class leetcode.Person

        //数组对应的Class类对象
        Class<? extends Person[]> aClass1 = array.getClass();
        System.out.println(aClass1);//控制输出:class [Lleetcode.Person;

        //基本数据类型对应的Class类对象
        Class<Integer> integerClass = int.class;
        System.out.println(integerClass);//控制台输出:int

        //枚举类
        Class<StudentList> studentListClass = StudentList.class;
        System.out.println(studentListClass);//class leetcode.StudentList

        /**
         * 获取不同数据类型的Class类对象之后可以惊醒的操作
         */
        //普通类
        Field[] fields = personClass.getDeclaredFields();//获取类的包括非public的字段:静态变量和实例变量
        for(Field f : fields)
            System.out.println(f);
        //上面控制台的输出
        //java.lang.String leetcode.Person.name
        //int leetcode.Person.age

        Method[] declaredMethods = personClass.getDeclaredMethods();//获取所有的方法
        for(Method m : declaredMethods)
            System.out.println(m);
        //上面控制台的输出
        //public java.lang.String leetcode.Person.toString()
        //public int leetcode.Person.compareTo(java.lang.Object)
        //public void leetcode.Person.show()

        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();//获取所所有声明的构造器
        for(Constructor c : declaredConstructors)
            System.out.println(c);
        //public leetcode.Person()
        //public leetcode.Person(java.lang.String,int)

        Class<?>[] interfaces = personClass.getInterfaces();
        for(Class c : interfaces)
            System.out.println(c);
        //interface java.lang.Comparable

        //数组获取存储对象的类型
        Class<?> componentType = aClass1.getComponentType();
        System.out.println(componentType);//class leetcode.Person

        //枚举类
        Field[] declaredFields = studentListClass.getDeclaredFields();
        for(Field f : declaredFields)
            System.out.println(f);
        //public static final leetcode.StudentList leetcode.StudentList.STUDENT1
        //public static final leetcode.StudentList leetcode.StudentList.STUDENT2
        //public static final leetcode.StudentList leetcode.StudentList.STUDENT3
        //public static final leetcode.StudentList leetcode.StudentList.STUDENT4
        //private final java.lang.String leetcode.StudentList.name
        //private final int leetcode.StudentList.id
        //private static final leetcode.StudentList[] leetcode.StudentList.$VALUES


        /**
         * Class对象创建对应类的对象:调用方法或者先获取构造器然后构造
         */
        //直接调用方法的
        Person person = personClass.newInstance();
        System.out.println(person);//调用的是空参构造器,输出:Person{name='null', age=0}
        //调用构造器的
        Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class);//注意顺序不能反
        Person cPerson = declaredConstructor.newInstance("构造器创建的", 12);
        System.out.println(cPerson);//调用的是有参构造器,输出为:Person{name='构造器创建的', age=12}

    }


}

总结

  • Class类是Reflection的根源,针对任何想动态加载、运行的类都要获取到相应数据类型对应的Class对象
  • 获取了某个数据类型对应的Class类对象之后,就获取了关于这个数据类型的所有相关信息
  • 通过Class对象可以创建对应类的对象,获取它的字段,方法,构造器等等
  • 达到的效果就是系统是在运行时而不是在编译时确定类型的信息,实现动态的效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值