Java 反射简介

本文部分内容参考博客。点击链接可以查看原文。

1. 反射的观点

反射是指在运行时将类的属性、组织函数和方式等元素动态地映射成一个个工具。通过这些工具我们可以动态地天生工具实例,挪用类的方式和更改类的属性值。

2. 使用场景

什么情况下运用JAVA反射呢?若是编译时基本无法预知工具和类可能属于哪些类,程序只依赖运行时信息来发现该工具和类的真实信息,此时就必须使用反射。

使用反射可以实现下面的功效:

  • 在运行时判断随便一个工具所属的类
  • 在运行时组织随便一个类的工具
  • 在运行时判断随便一个类所具有的方式和属性
  • 在运行时挪用随便一个工具的方式
  • 天生动态署理

3. 获得Class工具的几种方式

前面已经先容过了,每个类被加载之后,系统就会为该类天生一个对应的Class工具,通过该Class工具就可以接见到JVM中的这个类。在Java程序中获得Class工具通常有如下3种方式。

深入解读Dictionary

  • 使用Class类的forName(String clazzName)静态方式。该方式需要传入字符串参数,该字符串参数的值是某个类的全限制类名(必须添加完整包名)。
  • 挪用某个类的class属性来获取该类对应的Class工具。例如,Person.class将会返回Person类对应的Class工具。
  • 挪用某个工具的getClass()方式。该方式是java.lang.Object类中的一个方式,以是所有的Java工具都可以挪用该方式,该方式将会返回该工具所属类对应的Class工具。

对于第一种方式和第二种方式都是直接凭据类来取得该类的Class工具,相比之下,第二种方式有如下两种优势。

  • 代码更平安。程序在编译阶段就可以检查需要接见的Class工具是否存在。
  • 程序性能更好。由于这种方式无须挪用方式,以是性能更好。

也就是说,大部分时刻我们都应该使用第二种方式来获取指定类的Class工具。但若是我们只有一个字符串,例如“java.lang.String”,若需要获取该字符串对应的Class工具,则只能使用第一种方式,使用Class的forName(String clazzName)方式获取Class工具时,该方式可能抛出一个ClassNotFoundException异常。一旦获得了某个类所对应的Class工具之后,程序就可以挪用Class工具的方式来获得该工具和该类的真实信息了。

4. Class类 API先容

通过class类我们能够获取大量的信息:

  1. 获取组织函数
  • Connstructor getConstructor(Class<?>… parameterTypes):返回此Class工具对应类的指定public组织器。
  • Constructor<?>[] getConstructors():返回此Class工具对应类的所有public组织器。
  • Constructor getDeclaredConstructor(Class<?>… parameterTypes):返回此Class工具对应类的指定组织器,与组织器的接见权限无关。
  • Constructor<?>[] getDeclaredConstructors():返回此Class工具对应类的所有组织器,与组织器的接见权限无关。
  1. 获取方式
  • Method getDeclaredMethod(String name, Class<?>… parameterTypes):返回此Class工具对应类的指定方式,与方式的接见权限无关。
  • Method[] getDeclaredMethods():返回此Class工具对应类的所有方式,与方式的接见权限无关。
  1. 获取属性
  • Field getField(String name):返回此Class工具对应类的指定public Field。
  • Field[] getFields():返回此Class工具对应类的所有public Field。
  • Field getDeclaredField(String name):返回此Class工具对应类的指定Field,与Field的接见权限无关。
  • Field[] getDeclaredFields():返回此Class工具对应类的所有Field,与Field的接见权限无关。
  1. 获取Class对应类上所包罗的Annotation。

  1. 获取Class工具对应类包罗的内部类。
  • Class<?>[] getDeclaredClasses():返回该Class工具对应类里包罗的所有内部类。
    如下方式用于接见该Class工具对应类所在的外部类。
  • Class<?> getDeclaringClass():返回该Class工具对应类所在的外部类。
    如下方式用于接见该Class工具对应类所继续的父类、所实现的接口等。
  • Class<?>[] getInterfaces():返回该Class工具对应类所实现的所有接口。
  1. 获取Class工具对应类所继续的父类
  • Class<? super T> getSuperclass():返回该Class工具对应类的超类的Class工具。
  1. 获取Class工具对应类的修饰符、所在包、类名等基本信息。
  • int getModifiers():返回此类或接口的所有修饰符。修饰符由public、protected、private、final、static、abstract等对应的常量组成,返回的整数应使用Modifier工具类的方式来解码,才可以获取真实的修饰符。
  • Package getPackage():获取此类的包。
  • String getName():以字符串形式返回此Class工具所示意的类的名称。
  • String getSimpleName():以字符串形式返回此Class工具所示意的类的简称。
  1. 判断该类是否为接口、枚举、注释类型等
  • boolean isAnnotation():返回此Class工具是否示意一个注释类型(由@interface界说)。
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断此Class工具是否使用了Annotation注释修饰。
  • boolean isAnonymousClass():返回此Class工具是否是一个匿名类。
  • boolean isArray():返回此Class工具是否示意一个数组类。
  • boolean isEnum():返回此Class工具是否示意一个枚举(由enum关键字界说)。
  • boolean isInterface():返回此Class工具是否示意一个接口(使用interface界说)。
  • boolean isInstance(Object obj):判断obj是否是此Class工具的实例,该方式可以完全取代instanceof操作符。

上面的多个getMethod()方式和getConstructor()方式中,都需要传入多个类型为Class<?>的参数,用于获取指定的方式或指定的组织器。关于这个参数的作用,假设某个类内包罗如下3个info方式署名:

  • public void info()
  • public void info(String str)
  • public void info(String str , Integer num)

这3个同名方式属于重载,它们的方式名相同,但参数列表差别。在Java语言中要确定一个方式光有方式名是不行的,例如,我们指定info方式——实际上可以是上面3个方式中的随便一个!若是需要确定一个方式,则应该由方式名和形参列表来确定,但形参名没有任何实际意义,以是只能由形参类型来确定。例如,我们想要确定第二个info方式,则必须指定方式名为info,形参列表为String.class——因此在程序中获取该方式使用如下代码:

clazz.getMethod("info",String.class);

使用反射天生工具

  1. 行使组织函数天生工具
  • 使用Class工具的newInstance()方式来建立该Class工具对应类的实例,这种方式要求该Class工具的对应类有默认组织器,而执行newInstance()方式时实际上是行使默认组织器来建立该类的实例。
  • 先使用Class工具获取指定的Constructor工具,再挪用Constructor工具的newInstance()方式来建立该Class工具对应类的实例。通过这种方式可以选择使用指定的组织器来建立实例。
Constructor c = clazz.getConstructor(String.class);
c.newInstance("xx");

挪用方式

Method setProName = aClass.getDeclaredMethod("setProName",String.class);
setProName.setAccessible(true);
etProName.invoke(product,"我是一个产物");

操作属性

Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("fieldName:"+declaredField.getName()+" filedType:"+declaredField.getType());
        }
        Field proName = aClass.getDeclaredField("proName");
        proName.setAccessible(true);
        proName.set(product,"我是一个产物");
        System.out.println("修稿属性:"+product);

操作数组

//使用反射动态地建立数组
//建立一个元素类型为String,长度为3的数组
Object arr = Array.newInstance(String.class, 3);
//依次为arr数组中index为0,1,2的元素赋值
Array.set(arr, 0, "荣耀盒子");
Array.set(arr, 1, "荣耀8手机");
Array.set(arr, 2, "华为mate9保时捷版");
Object o1= Array.get(arr, 0);
Object o2= Array.get(arr, 1);
Object o3= Array.get(arr, 2);
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);

5. 使用Demo

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class<Product> aClass = Product.class;
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor.getName());
        }
        Constructor<Product> constructor = aClass.getConstructor(int.class, String.class);
        Product product = constructor.newInstance(10, "ds");
        System.out.println("建立工具:"+product);

        //获取方式并挪用
        Method setProName = aClass.getDeclaredMethod("setProName", String.class);
        setProName.setAccessible(true);
        setProName.invoke(product,"我是一个产物");
        System.out.println("挪用方式:"+product);

        //获取属性,并设置属性的值
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("fieldName:"+declaredField.getName()+" filedType:"+declaredField.getType());
        }
        Field proName = aClass.getDeclaredField("proName");
        proName.setAccessible(true);
        proName.set(product,"我是一个产物");
        System.out.println("修稿属性:"+product);

        //使用反射动态地建立数组
        //建立一个元素类型为String,长度为3的数组
        Object arr = Array.newInstance(String.class, 3);
        //依次为arr数组中index为0,1,2的元素赋值
        Array.set(arr, 0, "荣耀盒子");
        Array.set(arr, 1, "荣耀8手机");
        Array.set(arr, 2, "华为mate9保时捷版");
        Object o1= Array.get(arr, 0);
        Object o2= Array.get(arr, 1);
        Object o3= Array.get(arr, 2);
        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o3);
        
        System.out.println("end...");
    }
}

原创文章,作者:28x29新闻网,如若转载,请注明出处:https://www.28x29.com/archives/18636.html