Java#反射

介绍

在这里插入图片描述

反射是一种技术,使用这种技术理论上可以解刨任意类。Java的反射机制主要涉及到两个类Class和Member。

Class对象是整个类信息的访问入口,每个类被加载之后,系统就会为该类生成一个对应的Class对象,当获取到Class对象后就可以使用Class类提供的方法获取类的对象以及类的信息了。

Member就代表类的成员,包括构造函数Constructor、方法Method、字段Field。因为这三个类都实现了Member接口,所以统称Member吧。通过Class类提供的方法就可以获取到Constructor、Method、Field的实例了,有了这些实例我们就可创建对象,执行方法,修改字段了~

Class类

1、Class对象的常见获取方式
// 方式1
Class clazz = Class.forName("类的全限定名") //包名+类名

// 方式2
Class clazz = 类名.class

// 方式3
Class clazz = 类对象 .getClass()
2、Class对象常用的方法
  /**
   1、获取构造函数对象。
   parameterTypes:构造函数中参数所属的Class类型。由于构造函数中的参数可能有多个,这里的parameterTypes是一个可变参数。
   */
  public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException 
  //暴力反射时使用
  public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException 
  //获取所有的构造函数对象
  public Constructor<?>[] getConstructors() throws SecurityException 
   /**
   2、获取方法对象
   name:方法名
   parameterTypes:方法中参数所属的Class类型。由于方法中的参数可能有多个,这里的parameterTypes是一个可变参数。
   */
   public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException 
   //暴力反射使用
   public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
   //获取所有的方法对象
   public Method[] getMethods() throws SecurityException 
    /**
     3、获取字段对象
     name:字段名
    */
    public Field getField(String name) throws NoSuchFieldException, SecurityException
    //暴力反射使用
    public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
    // 获取所有的字段
    public Field[] getFields() throws SecurityException 
  /**
   4、其他
  */
 public String getName() //获取类名如java.lang.String
 public String getCanonicalName()//获取类名java.lang.String。不过数组、匿名类无CanonicalName,会返回null。
 public String getSimpleName()//获取类名,如String.

 public native int getModifiers()// 获取类的修饰符
 
 public TypeVariable<Class<T>>[] getTypeParameters()//获取类上的泛型信息

 public Type[] getGenericInterfaces()//获取类实现的接口信息

 public native Class<? super T> getSuperclass()//获取所有父类信息
 
 public Annotation[] getAnnotations()//获取类上的注解信息
3、Class类上信息的获取
@Deprecated
@CustomAnnotation
public final class Student<T, E> extends superClass1 implements IInterface1, IInterface2 {

    public String name = "SunnyDay";
    // test
    public static void main(String[] args) {
        Student<String, Integer> student = new Student<>();
        Class clazz = student.getClass();
        System.out.println("类名字:" + clazz.getSimpleName());
        System.out.println("类所有的修饰符名字:" + getModifiersByCode(clazz.getModifiers()));
        System.out.println("泛型参数名字:" + getTypeParametersName(clazz));
        System.out.println("所有实现接口的名字:" + getInterfaceName(clazz));
        System.out.println("所有父类的名字:" + getSuperClassName(clazz));
        System.out.println("所有注解名字:" + getAnnotationName(clazz));

        /**
         *log:
         *         类名字:Student
         *
         *         类所有的修饰符名字:public final
         *
         *         泛型参数名字:T E
         *
         *         所有实现接口的名字:IInterface1 IInterface2
         *
         *         所有父类的名字:superClass1 superClass2 Object
         *
         *         所有注解名字:@java.lang.Deprecated() @CustomAnnotation()
         *
         */

    }

    /**
     * 获取类的修饰符
     */
    private static String getModifiersByCode(int code) {
        return Modifier.toString(code);
    }

    /**
     * 获取类的泛型参数名
     */
    private static String getTypeParametersName(Class clazz) {
        if (clazz == null) return "";
        TypeVariable[] arr = clazz.getTypeParameters();
        if (arr.length <= 0) return "";
        StringBuilder sb = new StringBuilder();
        for (TypeVariable typeVariable : arr) {
            sb.append(typeVariable.getName()).append(" ");
        }
        return sb.toString();
    }

    /**
     * 获取类所有实现接口的名字
     */
    private static String getInterfaceName(Class clazz) {
        if (clazz == null) return "";
        Type[] arr = clazz.getGenericInterfaces();
        if (arr.length <= 0) return "";
        StringBuilder sb = new StringBuilder();
        for (Type type : arr) {
            sb.append(type.getTypeName()).append(" ");
        }
        return sb.toString();
    }

    /**
     * 获取类所有父类的名字
     */
    private static String getSuperClassName(Class clazz) {
        if (clazz == null) return "";
        Class superClass = clazz.getSuperclass();
        StringBuilder sb = new StringBuilder();
        while (superClass != null) {
            sb.append(superClass.getSimpleName()).append(" ");
            superClass = superClass.getSuperclass();
        }
        return sb.toString();
    }

    /**
     * 获取类所有注解的名字
     */
    private static String getAnnotationName(Class clazz) {
        if (clazz == null) return "";
        Annotation[] arr = clazz.getAnnotations();
        if (arr.length <= 0) return "";
        StringBuilder sb = new StringBuilder();
        for (Annotation annotation : arr) {
            sb.append(annotation.toString()).append(" ");
        }
        return sb.toString();
    }
}

interface IInterface1 {
}

interface IInterface2 {
}

class superClass1 extends superClass2 {
}

class superClass2 {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
}

Member

构造方法、方法、字段都是类的Member,如下首先准备一个类供测试用,然后分别解剖下类的Member。

/**
 * Create by SunnyDay on 2018/11/19
 */
public class Person {
     //字段
     public String name;
     public  int age;
     private String pwd;
     static int num;
     //构造方法
    public Person() {}

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

    public Person(String name, int age) {
        this.name=name;
        this.age=age;
        }
    private Person(List list) {}
    // 普通方法
     public  void test(){
        System.out.println("无参数此方法!!!");
    }
    public void test(String s,int a){
        System.out.println("两个参数的方法参数:"+s+" "+ a);
    }
    
    private String[] test(String s){
        System.out.println("有返回值的私有方法");
        return new String[]{"123","456"};
    }
    
    public static void hello(){
        System.out.println("静态的方法");
    }
}
1、 反射构造方法
    /**
     * 1、反射类的无参数构造函数
     */
    public static void reflectConstructorNone() throws Exception {
        Class cl = Class.forName("reflect.Person");
        Constructor constructor = cl.getConstructor(null);
        // 通过无参数的构造函数对象创建一个Person类的对象
        Person person1 = (Person) constructor .newInstance(null);
        System.out.println(person1.name);// 打印null,由于我们未进行赋值 字符串默认为null
    }

    /**
     * 2、 反射类的1个参数构造函数
     */
    private static void reflectConstructorOne() throws Exception {
        Class cl = Class.forName("reflect.Person");
        Constructor constructor = cl.getConstructor(String.class);
        //newInstance参数:构造函数需要的参数值
        Person person = (Person) constructor .newInstance("Kate");
        System.out.println(person.name);// Kate
    }

    /**
     * 3、反射类的2个参数构造函数
     */
    private static void reflectConstructorTwo() throws Exception {
        Class cl = Class.forName("reflect.Person");// ps:
        Constructor constructor = cl.getConstructor(String.class, int.class);
        Person person = (Person) constructor .newInstance("Kate", 20);
        System.out.println(person.name + "  " + person.age);// Kate 20
    }

    /** 4、反射私有构造
     *     反射私有的Member要采用暴力反射的方式,也即getDeclaredXXX+setAccessible。否则得不到反射信息。
     * */
    private static void violenceReflect() throws Exception {
        Class cl = Class.forName("reflect.Person");
        Constructor constructor =cl.getDeclaredConstructor(List.class);
        constructor.setAccessible(true);// 设置暴力反射权利  否则java.lang.IllegalAccessException
        Person p = (Person) constructor.newInstance(new ArrayList());
        System.out.println(p.name);
    }

创建对象有两种方法,优先使用第一种方法,第二种方法已经被废弃了。
1、Constructor.newInstance()
2、Class.newInstance()
上述二者还是有点区别的:

  • 方法一可以调用任意参数的构造方法,而方法二只能调用无参的构造方法
  • 方法一会将原方法抛出的异常都包装成 InvocationTaregtException 抛出,而方法二会将原方法中的异常不做处理原样抛出
  • 方法一不需要方法权限,方法二只能调用 public 的构造方法
2、反射方法
    /**
     * 1、反射无参数的方法
     */
    private static void ReflectMethodNone() throws Exception {
        Class cl = Class.forName("reflect.Person");
        /* 反射方法
         *  参数一 : String类型方法名
         *  参数二: 方法的参数所属的类型
         * */
        Method method = cl.getMethod("test", null);
        /*执行方法
         * 参数1:类的对象,说明是由那个类的对象执行的。
         * 参数2:方法的参数值。
         * */
        method.invoke(new Person(), null);
    }

    /**
     * 2、反射两个参数的方法
     */
    private static void ReflectMethodTwo() throws Exception {
        Class cl = Class.forName("reflect.Person");
        Method method = cl.getMethod("test", String.class, int.class);
        method.invoke(new Person(), "xxxxx", 20);
    }

    /**
     * 3、反射有返回值私有方法(必须暴力解决啦)
     */
    private static void ReflectMethodPrivate() throws Exception {
        Class cl = Class.forName("reflect.Person");
        Method method = cl.getDeclaredMethod("test", String.class);
        method.setAccessible(true);
        String[] strings = (String[]) method.invoke(new Person(), "xxxxx");
        System.out.println(strings);
    }

    /**
     * 4、反射静态方法方法
     */
    private static void ReflectStaticiMethod() throws Exception {
        Class cl = Class.forName("reflect.Person");
        Method method = cl.getMethod("hello", null);
        // 注意 静态的方法执行不需要对象 故此处设为null
        method.invoke(null, null);
    }
    
   /**
     * 5、获取方法的返回值类型.
     * <p>
     * getSimpleName:获取方法的返回值类型。
     * getGenericReturnType:获取方法的返回值类型。
     * <p>
     * 方法为普通方法时二者返回值相同。但是针对泛型有区别如:
     * <p>
     * 1、public T test() getReturnType返回Object,getGenericReturnType返回 T
     * <p>
     * 2、public Class<String> test() getReturnType返回Class,getGenericReturnType返回Class<String>
     */
    public static void getMethodReturnType() {
        try {
            Class clazz = Person.class;
            Method method = clazz.getMethod("test", null);
            Class returnClass = method.getReturnType();
            System.out.println("方法返回值类型:" + returnClass.getSimpleName());
            System.out.println("方法返回值类型:" + method.getGenericReturnType().getTypeName());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取方法参数信息。
     * */
    public static void getMethodParameterInfos() {
        try {
            Class clazz = Person.class;
            Method method = clazz.getMethod("test", String.class, int.class);
            Parameter[] arr = method.getParameters();

            StringBuilder sb = new StringBuilder();
            for (Parameter parameter : arr) {
                sb.append("参数名:")
                        .append(parameter.getName())
                        .append(" ")
                        .append("参数类型:")
                        .append(parameter.getType().getName())
                        .append("\n");
            }
            System.out.println(sb.toString());
            /**
             * log:
             *
             * 参数名:arg0 参数类型:java.lang.String
             * 参数名:arg1 参数类型:int
             *
             * 注意: .class 文件中默认不存储方法参数名称,如果想要获取方法参数名称,需要在编译的时候加上 -parameters 参数。
             *      如果编译未加上 -parameters 参数,返回的参数名则形如 ”argX“,X 代表参数的位置。
             * */

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

    /**
     *
     * 其他:
     *
     * 1、获取方法参数类型
     * getParameterTypes()	获取目标方法各参数类型对应的 Class 对象
     * getGenericParameterTypes() 获取目标方法各参数类型对应的 Type 对象
     *
     * 2、获取方法抛出的异常的类型
     * getExceptionTypes() 获取目标方法抛出的异常类型对应的 Class 对象
     * getGenericExceptionTypes() 获取目标方法抛出的异常类型对应的 Type 对象
     * 
     * */
3、反射类的字段
    /**
     * 1、反射公有的字段
     */
    private static void ReflectFieldPublic() throws Exception {
        Class cl = Class.forName("reflect.Person");
        // 获得字段对象  参数:字段名
        Field field = cl.getField("name");
        Person person = new Person();
        // 给指定对象赋值
        field.set(person,"xxxxx");
        String name = (String) field.get(person);
        System.out.println("获取到字段的值:"+name);

        /**
         * 注意我们可能不知道字段的类型
         * 这时我们使用api获得(安全起见)
         * */
        Object value = field.get(new Person());// 返回obj类型
        Class type = field.getType();// 获得字段类型

        if (type.equals(String.class)){
           String s =(String) value;
           //TODO 数据处理
        }
    }

    /**
     * 2、反射私有的字段
     */
    private static void ReflectFieldPrivate() throws Exception {
        Class cl = Class.forName("reflect.Person");
        // 私有的要暴力反射
        Field field = cl.getDeclaredField("pwd");
        field.setAccessible(true);
        Person person = new Person();
        field.set(person,"tom");
        System.out.println(field.get(person));
    }
    /**
     * 3、反射静态的字段
     */
    private static void ReflectStaticField()throws Exception {
        Class cl = Class.forName("reflect.Person");
        Field field = cl.getField("num");
        field.set(null,20);
        System.out.println(field.get(null));
    }

    /**
     *
     * 其他:
     *
     * 1、getName 获取字段名
     * 2、getType 获取字段类型
     * 3、getModifiers 获取字段修饰符
     * 4、getDeclaredAnnotations 获取字段上的注解
     *
     * */

数组枚举的反射

数组和枚举是特殊的对象,Java 反射为数组和枚举提供了一些特定的 API 接口。

1、数组的反射

数组本质上也是一个对象,它也有自己的Class类型。如 int[] array,class类型为 class [I。

数组类型中的 [ 个数表示数组的维度,其后面的字母代表数组元素类型,如int的为I,一般为类型的首字母大小。 long 类型例外使用J,因为 L 被引用对象类型占用了。

数组相关api:

 /**
  获取数组对象,返回值是Object。
  componentType:数组元素对应的class类型。如int.class。
  length:数组的长度。
  */
 public static Object newInstance(Class<?> componentType, int length)

 public static Object newInstance(Class<?> componentType, int... dimensions)

 /**
   给数组对象赋值
   array:数组对象
   index:数组index
   value:数组index对应的value值
 */
 public static native void set(Object array, int index, Object value)
  /**
   获取数组对象的值
   array:数组对象
   index:数组index
 */
 public static native Object get(Object array, int index)

举个栗子:

    /**
     * 反射一维数组栗子
     */
    private static void ref1DimensionArray() {
        int[] arr = new int[]{1, 1, 1};
        //由于数组元素为int类型,数组长度为3 所以传递int.class, 3
        Object object = Array.newInstance(int.class, 3);

        for (int i = 0; i < arr.length; i++) {
            Array.set(object, i, 2);
        }

        for (int i = 0; i < arr.length; i++) {
            System.out.println("index:" + i + "index-value:" + Array.get(object, i));
        }
        /**
         *
         * log:
         * index:0index-value:2
         * index:1index-value:2
         * index:2index-value:2
         *
         * */
    }

    /**
     * 反射多维数组(这里以二维数组举个栗子)
     * ps:Java 反射没有提供能够直接访问多维数组元素的 API,可以把多维数组当成数组的数组处理。
     */
    private static void ref2DimensionArray() {
        int[][] arr = new int[][]{{1, 1}, {1, 1}};

        Object object = Array.newInstance(int.class, 2, 2);
        //每一行都当做一维数组的对象。
        Object row1 = Array.get(object, 0);
        Object row2 = Array.get(object, 1);
        // 设置元素
        Array.set(row1, 0, 2);
        Array.set(row1, 1, 2);
        Array.set(row2, 0, 2);
        Array.set(row2, 1, 2);

        System.out.println("[0,0]->" + Array.get(row1, 0));
        System.out.println("[0,1]->" + Array.get(row1, 1));
        System.out.println("[1,0]->" + Array.get(row2, 0));
        System.out.println("[1,1]->" + Array.get(row2, 1));
        /**
         * [0,0]->2
         * [0,1]->2
         * [1,0]->2
         * [1,1]->2
         *
         * */
    }
2、枚举的反射

枚举隐式继承自 java.lang.Enum,Enum 继承自 Object。普通类所能使用的反射方法,枚举都能使用,另外,Java 反射额外提供了几个方法。

Class#isEnum()
Class#getEnumConstants()
Field#isEnumConstant()

反射应用

  • 处理配置文件提供的信息
  • 注解的运行期处理
  • jdk动态代理的实现

反射优缺点

1、优点
  • 灵活,可以动态改变类行为。
2、缺点
  • 性能开销:反射涉及类型动态解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率低下,尽量避免在高性能要求下使用反射。

  • 安全限制:使用反射要求程序必须在一个没有安全限制的环境中运行。

  • 内部曝光:由于反射允许代码执行一些在正常情况下不被允许的操作,所以使用反射可能会导致意料之外的副作用。

The end

参考:Java反射完全解析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值