Java反射机制

一. 反射概述

  • 在Java里面,专门有一个java.lang.reflect包用来实现反射相关的类库,包括Constructor、Field、Method等来,分别用来获取类的构造方法、成员变量、成员方法
  • 反射的使用场景还是挺多的,比如在动态代理的场景中,使用动态生成的代理类来提升代码的复用性。
  • Spring框架中,有大量用到反射,比如用反射来实例化Bean对象。

Java反射的优点:

  • 增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作
  • 提高代码的复用率,比如动态代理,就是用到了反射来实现
  • 可以在运行时动态获取类信息以及动态调用类中成分

Java反射的缺点:

  • 反射会涉及到动态类型的解析,所以JVM无法对这些代码进行优化,导致性能要比非反射调用更低。
  • 使用反射以后,代码的可读性会下降
  • 反射可以绕过一些限制访问的属性或者方法,可能会导致破坏了代码本身的封装性和抽象性,而且会造成一些安全性问题, 

什么是反射?

  • 反射允许对封装类的字段,方法和构造函数的信息进行编程访问
  • 这里的字段指的就是成员变量,方法指的就是成员方法,构造函数指的就是构造方法
  • 反射:反射是在运行时获取类的字节码文件对象,然后可以解析类中的成员变量,构造方法以及成员方法,反射允许对成员变量、成员方法和构造方法的信息进行编程访问!
  • 反射的核心思想和关键就是:得到编译后的字节码文件对象,也就是class文件对象。
  •  反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制!

  • 反射允许对成员变量、成员方法和构造方法的信息进行编程访问
  • 反射就可以把它理解成是一个人,这个人它可以把成员变量、成员方法以及构造方法,  挨个的都获取出来,并对它们进行操作!

获取出来有什么用呢?

  • 当然有用了,比如IDEA里面自动提示的功能,就是利用反射来实现的。
  • 我们在程序中创建了一个对象,利用这个对象去调用方法,或者是调用成员变量,那么IDEA就会利用反射把这个类里面能调用的所有的方法,所有的成员变量获取出来并进行展示!
  • 还有我们在创建对象或者是调用方法的时候,方法的形参给忘了,可以按Ctrl + P加以提示,此时IDEA就是利用反射去获取这个方法上所有的形参并展示出来!
  • 还有当我们在一个类里面定义了私有的构造方法,那么在测试类当中,IDEA给出的自动提示就不让去调用那个私有的构造方法,这是怎么做到的呢?
    就是利用反射,利用反射获取到该类的构造方法的权限修饰符,然后一看是private的,IDEA给出的自动提示就不会提示该私有的构造方法参数!

=======================================================================

问题1:说白了反射就是可以从类里面拿东西,拿什么?

  • 拿成员变量、构造方法、成员方法,把它们挨个的拿出来,这就是反射!

问题2:既然都是拿东西,为什么不用IO流而要用反射呢?

  • IO流是从上往下一行一行的去读,当我们读到构造方法和普通成员方法的时候,怎么区分?很难了把,如果想着用返回值区分,那么成员变量和局部变量你怎么区分呢?还得通过上下文去区分,非常的麻烦!

  • 反射甚至可以利用获取出来的构造方法去创建对象!
  • 反射还能够运行获取出来的方法!
  • 在反射面前,一切都是赤裸裸的。
  • 利用反射,我们就可以获取到类里面所有的信息!

  • 反射的第一步永远都是先获取Class类对象,如此才可以解析类中的全部成分!
  • 在获取的时候,不是从Java文件当中去获取的,而是从Class字节码文件当中去获取的!
  • Class类就是用来描述字节码文件的!
  • 同一个类编译后只产生一份class文件!

Class是一种类:

  1. 获取class类对象的三种方式:

  • java文件:就是我们自己编写的java代码。
  • 字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
  • 字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。
  • ​ 这个对象里面至少包含了:构造方法,成员变量,成员方法。
  • 而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是唯一的。

package com.gch.d2_reflect_class;

public class Student {
   
}
package com.gch.d2_reflect_class;

/**
   目标:反射的第一步,获取class对象,也就是类对象
   一个类编译后只产生一份class文件
   获取class对象的三种方式:
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 1.调class类中的一个静态方法:forName(String className===>全限名/全类名:包名 + 类名)
        // 最为常用的
        Class c1 = Class.forName("com.gch.d2_reflect_class.Student");
        System.out.println(c1); // class com.gch.d2_reflect_class.Student,本质就是Student.class

        // 2.类名.class
        // 一般更多的是当作参数进行传递(比如多线程的synchronized的同步锁对象用的就是class对象)
        Class c2 = Student.class;
        System.out.println(c1);// class com.gch.d2_reflect_class.Student

        // 3. java.lang.Object下的一个方法,对象.getClass(),获取对象对应类的Class对象
        // 当我们已经有了这个类的对象时,才可以使用
        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c3); // class com.gch.d2_reflect_class.Student
    }
}
  • 在Java当中,有一个思想叫做万物皆对象
  • 比如字节码文件可以看作是Class这个类的对象
  • 同理,构造方法也可以把它看作是一个对象,在Java当中定义了一个类叫做Constructor,那么这个类就是用来描述构造方法的!同理这个类的对象,就表示构造方法的对象
  • 在Java当中,有一个类叫Field,它是用来描述成员变量的,所以这个类的对象就表示成员变量的对象。
  • 第三个成员,方法,在Java当中有一个类叫Method,用来描述成员方法,同理,这个类的对象就是成员方法的对象!

 2. 反射获取构造方法

  • Declared就把它理解成权限的意思,有了Declared我就可以获取到所有的构造方法,包括私有的构造方法我都能获取到!

package com.gch.d3_reflect_constructor;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("无参构造器执行!");
    }

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

    protected Student(int age){
        this.age = age;
    }

    private Student(String name, int age) {
        System.out.println("有参构造器执行!");
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
package com.gch.d3_reflect_constructor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;

/**
   目标:反射获取Constructor构造方法
 */
public class MyReflectDemo {
    public static void main(String[] args) throws Exception {
        /*
           Class类中用于获取构造方法的方法
                Constructor<?>[] getConstructors()
                Constructor<?>[] getDeclaredConstructors()
                Constructor<T> getConstructor(Class<?>... parameterTypes)
                Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

            Constructor类中用于创建对象的方法
                 T newInstance(Object... initargs)
                 public void  setAccessible(boolean flag)
         */

        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("com.gch.d3_reflect_constructor.Student");

        // 2.获取构造方法
        // 获取所有的公共的构造方法
        Constructor[] cons1 = clazz.getConstructors();
        for (Constructor con : cons1) {
            System.out.println(con);
        }

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

        // 获取所有的构造方法
        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for (Constructor con : cons2) {
            System.out.println(con);
        }

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

        // 获取到空参的构造方法
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);
        System.out.println();

        // 获取到指定参数的构造方法,这个参数要跟构造方法里面的参数要保持一致才是可以的
        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
        System.out.println();
        // 在Java当中,int也是有对应字节码文件的
        Constructor con5 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con5);

        System.out.println("====================================");
        // getDeclared只是让你看到这个构造,但是无法利用它直接创建对象
        Constructor con6 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con6);
        // 获取该构造方法的权限修饰符,返回值类型是int
        int modifiers = con6.getModifiers();
        System.out.println(modifiers); // 2
        System.out.println("================================");
        // con6.getParameterCount(); 获取到参数的个数
//        con6.getParameterTypes(); 获取到参数的类型
        // 获取到这个构造方法所有的参数
        Parameter[] parameters = con6.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter); // 表示构造方法的每一个参数
        }
        // 表示临时取消权限校验,如果遇到私有构造器,可以暴力反射,表示权限被打开
        con6.setAccessible(true); // 暴力反射
        // 利用获取到的构造方法去创建对象,要传递参数,参数的属性要跟构造方法的参数保持一致
        Student stu = (Student) con6.newInstance("张三",24);
        System.out.println(stu);
    }
}

 3. 反射获取成员变量

package com.gch.d4_reflect_field;

public class Student {
   private String name; // 姓名
   private int age; // 年龄
   public String gender; // 性别


    public Student() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";
    }
}

package com.gch.d4_reflect_field;

import java.lang.reflect.Field;

public class MyReflectDemo {
    public static void main(String[] args) throws Exception {
        /*
            Class类中用于获取成员变量的方法:
                Field[] getFields()	返回所有公共成员变量对象的数组(只能拿public的)
                Field[] getDeclaredFields()	返回所有成员变量对象的数组,存在就能拿到
                Field getField(String name)	返回单个公共成员变量对象(只能拿public的)
                Field getDeclaredField(String name)	返回单个成员变量对象,存在就能拿到

            Filed类中用于创建对象的方法:
                void set(Object obj,Object value):赋值
                Object get(Object obj):  获取值
         */

        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("com.gch.d4_reflect_field.Student");

        // 2.获取成员变量
        // 获取所有的公共的成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field); // public java.lang.String com.gch.d4_reflect_field.Student.gender
        }
        System.out.println();
        // 获取所有的成员变量
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field); // private int com.gch.d4_reflect_field.Student.age
        }
        System.out.println();
        // 获取单个的公共成员变量
        Field gender = clazz.getField("gender");
        System.out.println(gender); // public java.lang.String com.gch.d4_reflect_field.Student.gender
        System.out.println();
        // 获取单个的全部成员变量
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);

        // 获取权限修饰符
        int modifiers = name.getModifiers();
        System.out.println(modifiers); // 2
        // 获取成员变量的名字
        String nameStr = name.getName();
        System.out.println(nameStr); // name
        // 获取到成员变量的数据类型
        Class nameType = name.getType();
        System.out.println(nameType); // class java.lang.String
        System.out.println();

        // 获取成员变量记录的值
        // 要获取值,首先要创建一个对象
        Student s = new Student("张三",24,"男");
        // 表示临时取消访问权限,暴力反射,表示权限被打开了
        name.setAccessible(true);
        String nameValue = (String) name.get(s);
//        Object nameValue = name.get(s);
        System.out.println(nameValue); // 张三

        // 修改对象里面记录的值
        // 参数1:修改哪个对象里面的值  参数2:修改成什么
        name.set(s,"李四");
        System.out.println(s); // Student{name = 李四, age = 24, gender = 男}
    }
}

 4. 反射获取成员方法

package com.gch.d5_reflect_method;

import java.io.IOException;

public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public void sleep(){
        System.out.println("睡觉");
    }

    private String eat(String something) throws IOException,NullPointerException,ClassCastException {
        System.out.println("在吃" + something);
        return "奥里给";
    }

    private void eat(String something,int a){
        System.out.println("在吃" + something);
    }


    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
package com.gch.d5_reflect_method;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
   目标:利用反射获取成员方法
 */
public class MyReflectDemo {
    public static void main(String[] args) throws Exception {
        /*
         反射获取类的Method方法对象:
         1、Method getMethod(String name,Class...args);
             根据方法名和参数类型获得对应的方法对象,只能获得public的

         2、Method getDeclaredMethod(String name,Class...args);
             根据方法名和参数类型获得对应的方法对象,包括private的

         3、Method[] getMethods();
             获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的

         4、Method[] getDeclaredMethods();
            获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。

    Method的方法执行:
        Object invoke(Object obj, Object... args)
          参数一:触发的是哪个对象的方法执行。
          参数二: args:调用方法时传递的实际参数

      获取方法的修饰符
      获取方法的名字
      获取方法的形参
      获取方法的返回值
      获取方法的抛出的异常
         */

        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("com.gch.d5_reflect_method.Student");

        // 2.获取里面所有的方法对象
        // 获取所有的公共成员方法对象(包含父类中所有的公共方法)
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }
        System.out.println();

        // 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }

        // 获取单个的公共成员方法对象
        // 参数1:方法名  参数2:参数的形参

        // 获取指定的单个的所有成员方法对象
        Method m = clazz.getDeclaredMethod("eat", String.class);
        System.out.println(m); // private void com.gch.d5_reflect_method.Student.eat(java.lang.String)

        // 获取方法的修饰符
        int modifiers = m.getModifiers();
        System.out.println(modifiers); // 2

        // 获取方法的名字
        String name = m.getName();
        System.out.println(name); // eat

        // 获取方法的形参
        // 获取到方法参数的个数
        int count = m.getParameterCount();
        System.out.println(count); // 1
        // 获取到参数的类型
       Class[] types = m.getParameterTypes();
        for (Class type : types) {
            System.out.println(type); // class java.lang.String
        }
        // 获取到参数的对象
        Parameter[] parameters = m.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter); // java.lang.String arg0
        }

        // 获取到方法抛出的异常
        Class[] exceptionTypes = m.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType); // class java.io.IOException
//                                                class java.lang.NullPointerException
//                                                class java.lang.ClassCastException
        }

        // 方法运行
     /*   Method的方法执行:invoke方法就可以运行你获取出来的方法
        Object invoke(Object obj, Object... args):运行方法
        参数一:表示方法的调用者,用obj对象调用该方法
        参数二: args:方法的形参,表示在调用方法时传递的实际参数(如果没有就不写)
        返回值:方法的返回值(如果没有就不写)*/
        Student s = new Student();
        // 表示临时取消访问权限,暴力反射,表示权限被打开
        m.setAccessible(true);
        // 参数一s:表示方法的调用者
        // 参数二"稀饭":表示在调用方法的时候传递的实际参数
        String result = (String) m.invoke(s,"稀饭"); // 在吃稀饭
       // 获取到方法的返回值(要方法运行之后才能获取到方法的返回值)
        System.out.println(result); // 奥里给
    }
}

面试题:

你觉得反射好不好?好,有两个方向

  1. 第一个方向:反射可以无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。
  2. 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象并动态的调用方法。

 练习1:利用反射保存任意对象数据

package com.gch.d6_reflect_test;

public class Student {
    private String name;
    private int age;
    private char gender; // 性别
    private double height;
    private String hobby;


    public Student() {
    }

    public Student(String name, int age, char gender, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.hobby = hobby;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public char getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(char gender) {
        this.gender = gender;
    }

    /**
     * 获取
     * @return height
     */
    public double getHeight() {
        return height;
    }

    /**
     * 设置
     * @param height
     */
    public void setHeight(double height) {
        this.height = height;
    }

    /**
     * 获取
     * @return hobby
     */
    public String getHobby() {
        return hobby;
    }

    /**
     * 设置
     * @param hobby
     */
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
    }
}
package com.gch.d6_reflect_test;

public class Teacher {
    private String name;
    private double salary; // 工资,薪水
    
    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}
package com.gch.d6_reflect_test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;
import java.lang.reflect.Field;

/**
   目标:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去
 */
public class MyReflectDemo {
    public static void main(String[] args) throws Exception {
        Student s = new Student("小A",23,'女',167.5,"睡觉");
        Teacher t = new Teacher("播妞",10000);

        saveObject(s);
        System.out.println();
        saveObject(t);
    }

    /**
     * 把对象里面所有的成员变量名和值保存到本地文件当中
     * @param obj
     */
    public static void saveObject(Object obj) throws Exception {
        // 1.获取类的字节码文件的对象:对象都有了,直接对象.getClass()即可,我真笨!
        Class clazz = obj.getClass();

        // 2.创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("day13-reflect-demo\\src\\a.txt",true)); // 追加数据管道

        // 3.获取到所有的成员变量对象
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
           // 一律暴力破解,临时取消访问取消,表示访问权限被打开
            field.setAccessible(true);
            // 获取成员变量的名字
            String name = field.getName();
            // 获取成员变量的值
            Object value = field.get(obj);
            // 写出数据
            bw.write(name + " = " + value);
            // 换行
            bw.newLine();
        }
        // 关闭IO流,释放资源
        bw.close();
    }
}

 练习2:利用反射动态的创建对象和运行方法

package com.gch.d7_reflect_test;

public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

    public void study(){
        System.out.println("学生在学习!");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
package com.gch.d7_reflect_test;

public class Teacher {
    private String name;
    private double salary; // 薪水


    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public void teach(){
        System.out.println("老师在教书!");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}
package com.gch.d7_reflect_test;

import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

/**
   目标:反射可以跟配置文件结合的方式,动态的创建对象,并调用方法
 */
public class MyReflectDemo {
    public static void main(String[] args) throws Exception {
        // 1.读取配置文件中的信息
        Properties prop = new Properties();
        // 创建字节输入流用于读取文件
        FileReader fr = new FileReader("day13-reflect-demo\\prop.properties");
        // 加载属性文件的键值对信息到属性集合prop中去
        prop.load(fr);
        // 释放资源
        fr.close();
        System.out.println(prop); // {classname="com.gch.d7_reflect_test.Teacher", method=teach}

        // 2.获取全类名和方法名
        String className = (String) prop.get("classname");
        String methodName = (String) prop.get("method");

        System.out.println(className); // "com.gch.d7_reflect_test.Teacher"
        System.out.println(methodName); // teach

        // 3.利用反射去创建对象并运行方法
        // 获取字节码文件的对象
        System.out.println(className);
        Class clazz = Class.forName(className);
        System.out.println(className);

        // 获取构造方法
        Constructor con = clazz.getDeclaredConstructor();
        con.setAccessible(true);
        Object o = con.newInstance();
        System.out.println(o);

        // 获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true); // 暴力反射,表示权限被打开
        // 运行方法
        method.invoke(o);
    }
}

 泛型擦除,反射可以绕过编译阶段为集合添加数据:

package com.gch.d8_reflect_genericity;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
   目标:反射的作用-绕过编译阶段为集合添加数据
   反射实现泛型擦除后,加入其他类型的元素
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception{
        ArrayList<String> lists1 = new ArrayList<>();
        ArrayList<Integer> lists2 = new ArrayList<>();

        System.out.println(lists1.getClass()); // class java.util.ArrayList
        System.out.println(lists2.getClass()); // class java.util.ArrayList

        // 结论:同一个类编译后只产生一份class文件
        System.out.println(lists1.getClass() == lists2.getClass()); // true , 本质ArrayList.class
        System.out.println("==================================================================");

        ArrayList<Integer> lists3 = new ArrayList<>();
        lists3.add(23);
        lists3.add(22);
//        lists3.add("黑马"); 直接报错

        // 获取字节码文件对象
        Class clazz = lists3.getClass(); // ArrayList.class ===> public void add(E  e)
        // 获取add方法
        Method method = clazz.getDeclaredMethod("add",Object.class);
        // 运行获取到的add方法
        boolean rs = (boolean) method.invoke(lists3,"黑马");
        System.out.println(rs); // true

        System.out.println(lists3); // [23, 22, 黑马]
        System.out.println();
        System.out.println("==================突破泛型的限制还可以这样做============================");
        // 把lists3赋值给同类型的变量lists4
        ArrayList lists4 = lists3;
        // 给lists4集合添加元素
        lists4.add("白马");
        lists4.add(true);
        // 输出lists3集合
        System.out.println(lists3); // [23, 22, 黑马, 白马, true]
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Surpass余sheng军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值