Java反射机制

反射的概念

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它任意一个方法和属性。这种动态获取类信息以及动态调用对象方法的功能称为Java语言的反射机制。


Class类

Class类的介绍

  • Class类是Java中十分重要的一个类,我们想要使用反射,是离不开这个类的。
  • 当JVM加载一个class文件后,就会在内存中生成一个与之对应的Class对象,这个对象保存着该class文件的全部信息
  • 同一类型的多个实例对象,它们的Class对象都是同一个,即在内存中每个Class对象都是独一无二的。

Class对象的获取

获取Class对象的方式有三种。

方式一:Class类的静态方法获取

使用该方式,意味着你的Java代码只有字节码文件,并没有进内存,我们需要手动的将字节码文件加载到内存,然后生成对应的Class对象、并获取Class对象

Class.forName("全类名") :将字节码文件加载进内存,返回Class对象

*多用于配置文件,将类名定义在配置文件中。读取文件加载类

方式二:类名获取

如果说内存中已经存在这个Class对象,那么我们就不需要再重新把字节码文件加载内存获取Class对象了。我们可以直接通过类名获取对应的Class对象

类名.class:通过类名的属性获取

*多用于参数的传递,例如方法中需要传递类

方式三:对象获取

若存在类实例对象,那么我们也可以通过该对象来获取该实例对象的Class对象。

对象.getClass(): getClass()方法在Object类中定义着

*多用于对象获取字节码的方式

代码测试:
Person类:

package domain;

public class Person {
    public String name;
    public String sex;
    private int age;
    private String address;
    // 不同访问权限的成员变量 a b c d
    public String a;
    protected String b;
    String c; // default
    private String d;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    public String getC() {
        return c;
    }

    public void setC(String c) {
        this.c = c;
    }

    public String getD() {
        return d;
    }

    public void setD(String d) {
        this.d = d;
    }

    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;
    }

    public Person() {
    }

    // 公有构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 私有构造方法
    private Person(String name) {
        this.name = name;
    }

    // 公共成员方法
    public void eat() {
        System.out.println("吃饭吃饭-------");
    }
    // 公共成员方法
    public void eat(String name) {
        System.out.println("吃"+name);
    }

    // 私有成员方法
    private void sleep() {
        System.out.println("睡觉睡觉-------");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}



测试类:

import domain.Person;

public class ReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {

        // 1、 Class类的静态方法获取 通过全类名
        Class<?> clazz1 = Class.forName("domain.Person");
        System.out.println(clazz1);

        // 2、 类名获取
        Class<Person> clazz2 = Person.class;
        System.out.println(clazz2);

        // 3、 对象获取
        Person p = new Person();
        Class clazz3 = p.getClass();
        System.out.println(clazz3);
        System.out.println("---------------------");
        // 看看这三个对象是否是同一个
        System.out.println(clazz1 == clazz2);   // true
        System.out.println(clazz2 == clazz3);   // true
    }
}

输出演示:
在这里插入图片描述
结论:
同一字节码文件(*.class)在一次程序运行过程中,只会被加载一次不论通过哪一种方式获取的Class对象都是同一个。


通过Class对象获取 并 使用成员变量

获取成员变量

  • Field[] getFields():获取所有public成员变量
  • Field getField(String name):根据名称获取某个public成员变量
  • Field[] getDeclaredFields():获取所有成员变量(任意权限修饰的,包括private)
  • Field getDeclaredField(String name):根据名称获取某个成员变量(任意权限修饰的,包括private)

代码测试:

import domain.Person;

import java.lang.reflect.Field;

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class clazz1 = Person.class;

        System.out.println("------------ public成员变量们 ---------------");
        Field[] fields = clazz1.getFields();
        for (Field field: fields) {
            System.out.println(field);
        }

        System.out.println("------------ 通过名称获取某个public成员变量 ---------------");
        Field name = clazz1.getField("name");
        System.out.println(name);

        System.out.println("------------ 所有成员变量们 ---------------");
        Field[] declaredFields = clazz1.getDeclaredFields();
        for (Field field: declaredFields) {
            System.out.println(field);
        }

        System.out.println("------------ 通过名称获取某个成员变量 ---------------");
        Field d = clazz1.getDeclaredField("d");
        System.out.println(d);
    }
}

输出显示:
在这里插入图片描述

使用成员变量

上面我们获取了某个类的成员变量Field,那么我们能拿成员变量干什么呢?
其实无非就是两个操作:设置值获取值

  • Field getField(String name):获取值
  • void set(Object obj, Object value):设置值

演示代码:

import domain.Person;

import java.lang.reflect.Field;

public class ReflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Person p1 = new Person("coderzpw",22);
        Class clazz1 = p1.getClass();
        Field name = clazz1.getField("name");
        // 通过反射 获取 p1对象的name
        Object name1 = name.get(p1);
        System.out.println(name1);
        // 通过反射 设置 p1对象的name
        name.set(p1, "小虎牙");
        System.out.println(p1.name);
    }
}

输出显示:
在这里插入图片描述
其实上面我们使用的成员变量name它是一个public的。我相信大家肯定会有疑问,私有的成员变量我们可以获取并设置吗?
接下来我们来试一下:

import domain.Person;

import java.lang.reflect.Field;

public class ReflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Person p1 = new Person();
        p1.setD("小D");
        Class clazz1 = p1.getClass();
        Field d = clazz1.getDeclaredField("d");
        // 通过反射 获取 p1对象的d
        Object d1 = d.get(p1);
//        System.out.println(d1);
//        // 通过反射 设置 p1对象的d
//        d.set(p1, "大D");
//        System.out.println(p1.getD());
    }
}

结果:
在这里插入图片描述
直接报错了,告诉我们是非法操作!!!
难道我们真的无法通过反射使用p1的d属性吗? 当然可以使用,在反射面前一切访问权限都形同虚设。
看我操作:

import domain.Person;

import java.lang.reflect.Field;

public class ReflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Person p1 = new Person();
        p1.setD("小D");
        Class clazz1 = p1.getClass();
        // 获取private成员变量 d
        Field d = clazz1.getDeclaredField("d");
        // 忽略访问权限修饰符的安全检查
        d.setAccessible(true);  // 暴力反射
        // 通过反射 获取 p1对象的d
        Object d1 = d.get(p1);
        System.out.println(d1);
        // 通过反射 设置 p1对象的d
        d.set(p1, "大D");
        System.out.println(p1.getD());
    }
}

输出演示:
在这里插入图片描述
获取、设置成功!!!其实跟上面代码不同点,就是加了d.setAccessible(true); ,这一点是关键

// 忽略访问权限修饰符的安全检查
d.setAccessible(true);  // 暴力反射

通过Class对象获取 并 使用构造方法

获取构造方法对象

  • Constructor<T> getConstructor(Class<?>... parameterTypes):根据你的参数类型,获取public修饰的构造方法
  • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):根据你的参数类型,获取任意权限修饰的构造方法,包括private
  • T newInstance():直接通过Class对象调用无参构造方法,创建实例

使用构造方法对象创建实例

T newInstance(Object ... initargs): 传入对应的参数即可

代码案例:

import domain.Person;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        Class clazz1 = Person.class;
        // 通过public的构造方法 初始化一个对象
        Constructor constructor1 = clazz1.getConstructor(String.class, int.class);
        Object person1 = constructor1.newInstance("coderzpw", 22);
        System.out.println(person1);

        // 通过private的构造方法 初始化一个对象(需要加暴力反射代码,不加的话也会报错)
        Constructor constructor2 = clazz1.getDeclaredConstructor(String.class);
        // 忽略访问权限修饰符的安全检查
        constructor2.setAccessible(true);  // 暴力反射
        Object person2 = constructor2.newInstance("小虎牙");
        System.out.println(person2);

        // 直接通过Class对象创建实例(必须是无参的)
        Object o = clazz1.newInstance();
        System.out.println(o);
    }
}

输出演示:
在这里插入图片描述


通过Class对象获取 并 使用成员方法

获取成员方法对象

  • Method[] getMethods():获取所有public成员方法,包括父类的方法
  • Method[] getDeclaredMethods():获取获取任意权限修饰的成员方法,但不包括父类的方法
  • Method getMethod(String name, Class<?>... parameterTypes):根据方法名以及参数类型获取指定方法
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes):根据方法名以及参数类型获取指定方法(任意访问修饰符修饰的,包括private)

getMethods代码演示:

public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Class clazz1 = p1.getClass();
        Method[] methods = clazz1.getMethods();
        for (Method method: methods) {
            System.out.println(method);
        }
    }
}

输出演示:
在这里插入图片描述getDeclaredMethods代码演示:

import domain.Person;

import java.lang.reflect.Method;

public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Class clazz1 = p1.getClass();
        // 获取所有的方法(任意访问修饰符,包括private) 但是不包括父类的方法
        Method[] declaredMethods = clazz1.getDeclaredMethods();
        for (Method m: declaredMethods) {
            System.out.println(m);
        }
    }
}

输出演示:

在这里插入图片描述
getMethod 和 getDeclaredMethod 代码演示:

import domain.Person;

import java.lang.reflect.Method;

public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Class clazz1 = p1.getClass();
        Method eatMethod1 = clazz1.getMethod("eat");
        Method eatMethod2 = clazz1.getMethod("eat", String.class);
        Method sleepMethod = clazz1.getDeclaredMethod("sleep");
        System.out.println(eatMethod1);
        System.out.println(eatMethod2);
        System.out.println(sleepMethod);
    }
}

输出演示:
在这里插入图片描述

通过方法对象调用某个对象的方法

Object invoke(Object obj, Object... args):调用某个对象的方法,第一个参数是指定对象,其他参数都是方法的形参

invoke代码演示:

import java.lang.reflect.Method;

public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Class clazz1 = p1.getClass();
        Method eatMethod2 = clazz1.getMethod("eat", String.class);
        Method sleepMethod = clazz1.getDeclaredMethod("sleep");
        eatMethod2.invoke(p1, "火锅");
        // 忽略访问权限修饰符的安全检查
        sleepMethod.setAccessible(true);    // 暴力反射
        sleepMethod.invoke(p1);
    }
}

输出演示:

在这里插入图片描述


总结

  • 其实Class对象、Field对象、Constructor对象和Method对象都有对应的getName()方法,获取对应的名字,这里就不演示了
  • 无论是获取Field对象、Constructor对象和Method对象,如果是加s的都是获取复数,不加s的是获取指定的一个(一般都需要指定条件)
  • 一般带Declared的,都可以获取任意访问修饰符修饰的,包括private。但是如果使用private的对象的话,需要提前调用对应的setAccessible(true)方法
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

coderzpw

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

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

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

打赏作者

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

抵扣说明:

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

余额充值