Java中的反射

  反射,也可以称为反向获取,在一个程序的运行时,我们可以获取任意一个类的所有方法和属性,同样也可以调用任意一个对象的所有方法和属性。反射应用在一些通用性较高的代码,后面的框架大多数就是用反射来实现的。

一、反射的原理

  • 首先需要把Java文件保存到本地磁盘
  • 编译Java文件,生成.class文件
  • 使用JVM运行,把class文件通过类加载到内存中,再执行
  • class文件在内存中使用Class对象存储

当使用反射的时候,首先需要获取到Class对象,得到这个对象之后,就可以得到class文件里面的所有内容,包含属性,构造方法,普通方法

  • 属性通过一个类Filed
  • 构造方法通过一个类Constructor
  • 普通方法通过一个类Method


    反射的原理

二、获取Class对象

  上面我们说,使用反射的时候,首先要获取类的对象,这是反射的前提。获取类的对象有三种方法:
  1、通过对象的getClass()方法获取,必须要有对象
  2、通过类名.class创建类的对象
  3、调用Class类中的forName()方法,获取类的对象,这种方法最常用

  • 学生类
public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }

}
  • 测试类
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //方法一:创建类的对象,调用getClass()方法
        Student s = new Student();
        Class clazz1 = s.getClass();
        //输出类的对象,会得到类所在位置的全路径
        System.out.println(clazz1);
        
        //方法二:通过类名.class创建类的对象
        Class clazz2 = Student.class;
        System.out.println(clazz2);
        
        //方法三:调用Class类中的forName()方法获取类的对象,传入的参数必须是类的全路径,需要抛出异常
        Class clazz3 = Class.forName("com.itheima.review01.Student");
        System.out.println(clazz3);
    }

}

三、获取类中的构造方法并使用

  我们获取到类的对象以后,就可以获取类中的所有内容了。首先我们来获取构造方法,有了构造方法后,我们就可以创建对象,然后就可以调用属性和方法。

1、获取构造方法

获取构造方法的方式有两种:
  Constructor[] getConstructors():获取所有public修饰的构造方法
  Constructor getConstructor(Class... parameterTypes):获取带参的构造方法,如果没有参数,则获取的是无参的构造方法

2、使用构造方法

  T newInstance(Object... initargs): 供Constructor创建对象使用,括号内填写构造方法的参数。

3、案例

import java.lang.reflect.Constructor;

public class ReflectDemo2 {
    public static void main(String[] args) throws ReflectiveOperationException {
        Class clazz = Class.forName("com.itheima.review01.Student");
        //获取构造方法
        //方法一
        Constructor[] cs = clazz.getConstructors();
        for (Constructor constructor : cs) {
            //输出构造方法
            System.out.println(constructor);
        }
        
        //方法二
        //获取无参构造:
        Constructor c1 = clazz.getConstructor();
        System.out.println(c1);
        //获取有参构造,括号内是参数的类的对象
        Constructor c2 = clazz.getConstructor(String.class,int.class);
        System.out.println(c2);
        
        //使用构造方法
        Object obj1 = c1.newInstance();
        System.out.println(obj1);//Student类中重写了toString()方法,Student [name=null, age=0]
        
        Object obj2 = c2.newInstance("张三",20);
        System.out.println(obj2);//Student [name=张三, age=20]
    }

}

四、获取属性并使用

1、获取属性

  获取属性的方法有四种:
  Field[] getFields():拿到类中所有的public修饰的字段对象
  Field getField(String name):根据字段名称获取指定的public修饰的字段对象
  Field[] getDeclaredFields():拿到类中所有的字段对象(私有也能拿到)
  Field getDeclaredField(String name):根据字段名称获取指定的字段对象(私有也能拿到)

2、获取和修改属性

  Object get(Object obj):获取指定对象中字段的值(需要传一个对象)
  void set(Object obj, Object value):修改指定对象中的值(需要传一个对象)

3、案例

import java.lang.reflect.Field;

public class ReflectDemo3 {
    public static void main(String[] args) throws ReflectiveOperationException {
        // 创建学生类的字节码对象
        Class clazz = Class.forName("com.itheima.review01.Student");
        // 创建学生类的对象
        Object obj = clazz.newInstance();
        // 获取学生类的属性
        Field f1 = clazz.getDeclaredField("name");
        Field f2 = clazz.getDeclaredField("age");

        // 设置反射时取消Java的访问检查(去除私有权限)
        f1.setAccessible(true);
        f2.setAccessible(true);
        
        //设置属性
        f1.set(obj, "张三");
        f2.set(obj, 20);
        
        //获取属性
        Object name = f1.get(obj);
        Object age = f2.get(obj);
        System.out.println(name);
        System.out.println(age);
    }

}

五、获取普通方法并使用

1、获取普通方法

  Method getMethod(String name, Class... parameterTypes):拿到类里面的成员方法

2、使用普通方法

  Object invoke(Object obj, Object... args)

3、案例

public class ReflectDemo4 {
    public static void main(String[] args) throws ReflectiveOperationException {
        Class clazz = Class.forName("com.itheima.review01.Student");
        Object obj = clazz.newInstance();

        // 获取普通方法
        // 获取无参无返回值的方法并调用
        Method m1 = clazz.getMethod("method");
        m1.invoke(obj);

        // 获取有参无返回值的方法并调用
        Method m3 = clazz.getMethod("setName", String.class);
        m3.invoke(obj, "张三");

        // 获取无参有返回值的方法并调用
        Method m2 = clazz.getMethod("getName");
        Object name = m2.invoke(obj);
        System.out.println(name);

    }

}

六、补充

  创建一个ArrayList的一个对象,如何添加一个字符串类型的数据?
  首先来简单介绍一下泛型:

  • Java泛型中的标记符含义:
      E - Element(在集合中使用,因为集合中存放的是元素)
      T - Type(Java类)
      K - Key(键)
      V - Value(值)
      N - Number(数值类型)

  • 泛型的三种
    [1]ArrayList al = new ArraList();
      指定集合元素只能是T类型
    [2]ArrayList al = new ArraList();
      指定集合元素可以使任何类型,这个声明没有任何意义
    [3]ArrayList al = new ArraList();
      ? extends E:接收E类型或者E的子类型
      ? super E:接收E类型或者E的父类型

你可能感兴趣的:(Java中的反射)