java基础之反射

定义

java的反射进制是在运行过程中对于任意类,我们都能知道它的所有属性和方法,对于任意一个对象,我们都能调用它的所有属性和方法,这样动态的修改信息调用信息的操作称为反射

为什么要用到反射

下面我们通过Oracle官方文档的一些话来回答这个问题:

  • 反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能
  • 反射让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法。以帮助开发者写出正确的代码
  • 测试时可以利用反射 API 访问类的私有成员,以保证测试代码覆盖率

反射的使用

总览图

在这里插入图片描述

这是我们要用到的Person类

public class Person {
    public String name;
    public int age;
    private int money;

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

    public Person() {
    }

    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 int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                '}';
    }
}
Class类
什么是Class类
  • java文件被编译后,会生成.class字节码文件,而字节码文件又会被加载到JVM中,之后JVM会将要使用的类解析为一个对象,这个对象就是Class类的对象,它存储在JVM的堆中。在反射机制中,我们就可以通过这个Class对象创建类的实例,获取类的属性、修改类的属性、调用类的方法等等。

  • 对于每一种类,JVM都会创建一个Class类的实例,每当我们编写并且创建一个新类时,都会创建一个Class对象,并且这个Class对象会保存在.class文件里

  • 无论创建多少个类(比如下文里的Person类)的实例,在JVM里都只对应于一个Class对象

  • Class是反射的基础,要想获得类(比如下文的Person类)的实例,都必须创建一个Class对象获取才可以

Class类相关方法
方法说明
forName(String className)根据类名返回类的对象
newInstance()创建类的实例
getName()获得类的完整路径名字

获取Class类的对象的三种方式

  1. 通过调用Class的静态方法forName(“全路径名”)使用(这种方法需要抛出异常,因为路径名可能写错)

    只需要知道全路径名就可以知道,这种方法如果类还没有加载就会进行加载,如果类已经加载就会返回Class对象

    Class cls1 = Class.forName("Java高级特性.反射.Person");
    
  2. 通过调用Person类class属性创建Class对象

    这种方法比较适合于此类已经知道的情况

    Class cls2 = Person.class;
    
  3. 通过调用Person类对象的getClass()方法创建

    需要创建Person类对象才可以使用

    Person person = new Person("1", 1, 1);
    Class cls3 = person.getClass();
    
public static void main(String[] args) throws Exception {
    //1. 调用Class类的forName("全路径名")方法创建
    Class cls1 = Class.forName("Java高级特性.反射.Person");
    //2. 调用Person类的class属性创建Class对象
    Class cls2 = Person.class;
    //3. 调用Person类的对象的getClass()方法进行创建(这种方法需要一个Person类的对象)
    Person person = new Person("1", 1, 1);
    Class cls3 = person.getClass();

    //比较三种创建方法是否相等
    System.out.println(cls1 == cls2);		//true
    System.out.println(cls1 == cls3);		//true

    /*
    从上面我们可以看出三种创建方式是等价的,Person类在JVM中只会加载一次
     */
}
Field类

对成员变量进行操作

Field类相关方法
方法说明
Field getFiled(String name)获得某个公有的属性对象
Field[] getFileds()获得所有公有的属性对象
Field getDeclaredFiled(String name)获得某个属性对象
Field[] getDeclaredFileds()获得所有属性对象
void set(Object obj, Object value)设置对象的对应的成员变量的值
Object get(Object obj)获取对象的对应的成员变量的值
void setAccessible(boolean flag)当访问私有成员变量是需要设置为true,暴力访问(该方法需要抛出异常)
package Java高级特性.反射;

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        Class cls = Person.class;
        //访问成员变量们
        /*
            Class类对象.getFields():获取以public修饰的成员变量
            Class类对象.getDeclaredFields():获取全部的成员变量
         */
        Field[] fields = cls.getFields();
        System.out.println("公有成员变量:");
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("全部的成员变量:");
        fields = cls.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        Person person = new Person("张三", 18, 100);
        //获取特定的成员变量
        Field field = cls.getField("name");
        System.out.println(field);
        System.out.println(field.getName());
        System.out.println(field.get(person));
        //修改特定的成员变量
        field.set(person, "李四");
        System.out.println(field.get(person));
        //访问private修饰的成员变量
        field = cls.getDeclaredField("money");
        field.setAccessible(true);
        System.out.println(field.get(person));
        field.set(person, 200);
        System.out.println(field.get(person));

    }
}
Constructor类

对构造方法进行操作

Constructor类相关方法
方法说明
getConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructor()获得该类型所有的构造方法
T newInstance(Object ... initargs)通过Constructor对象创建Person对象,需要传Person对象和方法的参数
package Java高级特性.反射;

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

public class Main {
    public static void main(String[] args) throws Exception {
        Class cls = Person.class;
        //获取以public修饰的构造方法
        Constructor[] constructors = cls.getConstructors();
        System.out.println("以public修饰的构造方法:");
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("以private修饰的构造方法:");
        constructors = cls.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
        //使用构造器创建对象(可以使用所有的构造器)
        Person person = (Person)constructor.newInstance("张三", 18, 100);
        System.out.println(person.getName());
        //使用Class对象创建对象(只能使用无参的构造方法)
        person = (Person)cls.newInstance();
        System.out.println(person.getName());

    }
}
Method类

对成员方法进行操作

Method类相关方法
方法说明
getMethod(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有方法
getMethods()获得该类的所有公有方法
getDeclaredMethod(Class...<?> parameterTypes)获得该类中与参数类型匹配的方法
getDeclaredMethods()获得该类型所有的方法
Object invoke(Object obj, Object... args)调用Person对象的方法,需要传Person对象和方法的参数
package Java高级特性.反射;

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

public class Main {
    public static void main(String[] args) throws Exception {
        Class cls = Person.class;
        //获取以public修饰的成员方法(包含父类的)
        System.out.println("以public修饰的成员方法:");
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //获取所有的成员方法(不包含父类的)
        methods = cls.getDeclaredMethods();
        System.out.println("所有的成员方法:");
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("调用方法:");
        Person person = new Person("张三", 18, 100);
        Method method = cls.getMethod("getName");
        System.out.println(method);
        System.out.println(method.invoke(person));

    }
}

创建Person类的对象
通过Class对象创建
Class cls = Class.forName("Java高级特性.反射.Person");
Person person = (Person) cls.newInstance();	//必须强转,因为Class类.newInstance()方法返回的类型是Object类型
通过Constructor对象创建
Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
person = (Person) constructor.newInstance("1", 1, 1);//强转,因为Constructor类.newInstance()方法返回的类型是Object类型

反射的优点和缺点

优点:

  • 对于任意一个类,能够知道该类的所有属性和方法;对于任意一个对象,能够修改对象的所有属性和调用对象的所有方法和属性
  • 可以在程序运行过程中,操作这些对象。
  • 可以解耦,提高程序的可扩展性。

缺点:

  • 反射技术绕过源代码,会带来维护的问题
  • 反射代码相比于直接的代码更为复杂
文章学习与:
https://blog.csdn.net/weixin_51367845/article/details/122014096
https://www.zhihu.com/question/377483107
https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
https://www.jianshu.com/p/10c29883eac1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值