Java反射

一、基本概念

Java反射机制是指在运行时检查、获取和操作类、方法、字段等结构的能力。通过反射,可以在运行时动态地创建对象、调用方法、访问字段等,而不需要在编译时知道类的具体信息。这种机制使得Java程序具有更高的灵活性和动态性。

一句话总结:反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

二、核心类与接口

Java反射机制主要依赖于java.lang.reflect包中的类和接口,这些类和接口提供了丰富的API来操作类、接口、字段、方法和构造器等。主要的核心类包括:

  • Class:代表一个类的元数据,可以获取类的名称、字段、方法等信息。
  • Constructor:用于描述类的构造方法。
  • Method:用于描述类的方法。
  • Field:用于描述类的字段。

必须先获得Class才能获取Method、Constructor、Field

三、为什么要用反射?

Java Reflection功能非常强大,并且非常有用,比如:

  • 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
  • 获取任意对象的属性,并且能改变对象的属性
  • 调用任意对象的方法
  • 判断任意一个对象所属的类
  • 实例化任意一个类的对象
  • 通过反射我们可以实现动态装配,降低代码的耦合度,动态代理等。

四、获取Class的三方式

要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?

  1. Class.forName(String className): 通过类的完全限定名获取 Class 对象。(静态方法)

  2. ClassName.class: 通过类的字面常量获取 Class 对象。

  3. object.getClass(): 通过对象实例获取 Class 对象。

注:以上三种方式返回值都是Class类型

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 String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
  /*获取class对象的三种方式:
        1. class.forName("全类名");
        2.类名.class
        3.对象.getclass( );*/

        //第一种方式:
        //全类名 : 包名 + 类名
        //最为常用
        Class<?> clazz1 = Class.forName("com.wedu.myreflect1.Student");
        System.out.println(clazz1);

        //第二种方式
        //一般更多的是当做参数进行传递
        Class<Student> clazz2 = Student.class;
        System.out.println(clazz2);

        //第三种方式
        //当我们已经有了这个类的对象时,才可以使用
        Student s = new Student();
        Class clazz3 = s.getClass();
        

五、通过反射实例化对象

        对象.newInstance()

注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
否则会抛出java.lang.InstantiationException异常。

eg.

 Class<?> clazz1 = Class.forName("com.wedu.myreflect1.Student");

        Object o = clazz1.newInstance();
        System.out.println(o);

如今class.newInstance()在java9时被@Deprecated

六、反射Filed【反射/反编译一个类的属性】

6.1、Class类方法

方法名备注
public T newInstance()创建对象
public String getName()返回完整类名带包名
public String getSimpleName()返回类名
public Field[] getFields() 返回类中public修饰的属性
public Field[] getDeclaredFields()返回类中所有的属性
public Field getDeclaredField(String name)根据属性名name获取指定的属性
public native int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Method[] getDeclaredMethods() 返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) 根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)根据方法形参获取指定的构造方法
public native Class<? super T> getSuperclass() 返回调用类的父类
public Class<?>[] getInterfaces()返回调用类实现的接口集合

eg.这里我举例了几个

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

        //2.获取所有的成员变量
        /*Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }*/

        //获取单个的成员变量
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);

        //获取权限修饰符
        int modifiers = name.getModifiers();
        System.out.println(modifiers);

6.2、Field类方法

方法名备注
public String getName() 返回属性名
public int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getType()以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value)    设置属性值
public Object get(Object obj)    读取属性值

eg.反编译一个类的属性Field

        //1.获取class字节码文件的对象
        Class clazz = Class.forName("com.wedu.myreflect3.Student");
        
        //获取成员变量名字
        String n = name.getName();
        System.out.println(n);

        //获取成员变量数据类型
        Class<?> type = name.getType();
        System.out.println(type);

        //获取成员变量记录值
        Student s = new Student("张三", 12, "男");
        name.setAccessible(true);
        String value = (String) name.get(s);
        System.out.println(value);

        //修改对象里面的值
        name.set(s,"lisi");
        System.out.println(s);

set()可以访问私有属性嘛?

不可以,需要打破封装,才可以。

Fidle方法:

方法备注
public void setAccessible(boolean flag)默认false,设置为true为打破封装

七、反射Method【反射/反编译一个类的方法】

方法名备注
public String getName()返回方法名
public int getModifiers()获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getReturnType()以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes()返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args) 调用方法

注: Method类中invoke()使用注意事项:

方法.invoke(对象, 实参);

八、反射Constructor【反射/反编译一个类的构造方法】

方法名备注
public String getName()返回构造方法名
public int getModifiers() 获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?>[] getParameterTypes()返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs) 创建对象【参数为创建对象的数据】
反射机制创建对象两步骤
  1. 先获取到这个有参数的构造方法【用ClassgetDeclaredConstructor()方法获取】
  2. 调用构造方法new对象【用Constructor类的newInstance()方法new对象】

九、获取一个类的父类以及实现的接口

两个方法【Class类中的】

  1. public native Class<? super T> getSuperclass()
  2. public Class<?>[] getInterfaces()
        // String举例
        Class vipClass = Class.forName("java.lang.String");
        // 获取String的父类
        Class superclass = vipClass.getSuperclass();
        // 获取String类实现的所有接口(一个类可以实现多个接口。)
        Class[] interfaces = vipClass.getInterfaces();
        System.out.println(superclass.getName());
        for (Class i : interfaces) {
            System.out.println(i.getName());
        }


  
    
   

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值