Java-反射详解


1️⃣ 反射的含义

  1. 反射是将Java类中的各种成分映射成相应的Java对象。
  2. Class对象的产生是基于将class文件读入内存,并为之创建一个对应的Class对象。需要注意的是,一个类在运行期间只会产生一个Class对象。
  3. 反射是许多框架设计的核心。使用反射的前提是:必须先获取代表的字节码的Class对象。Class类代表着.class文件(也就是字节码)。

2️⃣ 获取Class对象的三种方式

  1. 通过对象获取:Object ——> getClass();
  2. 通过数据类型的“静态”class属性:任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  3. 通过Class类的静态方法获取:Class.forName(String className)。这种方式是常用的。

🔹 完整示例

package com.study.notes.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.List;

/**
 * @program: study-notes
 * @description: 作家
 * @author: lzq
 * @create: 2023-06-27 15:38
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    public String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //薪水
    private BigDecimal salary;
    //作品
    private List<Book> books;

    /**************构造方法***************/
    public Author(String name) {
        this.name = name;
    }

    /**************成员方法***************/
    public void show1(String s){
        System.out.println("调用了:公有的,String参数的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("调用了:受保护的,无参的show2()");
    }
    void show3(){
        System.out.println("调用了:默认的,无参的show3()");
    }
    private String show4(int age){
        System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
        return String.valueOf(age);
    }
}

package com.study.notes.reflect;


import com.study.notes.bean.Author;

/**
 * 获取Class对象的三种方式
 * 1 Object ——> getClass();
 * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
 * 3 通过Class类的静态方法:forName(String  className)(常用)
 * 注意:在运行期间,一个类,只有一个Class对象产生。
 * 三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
 *
 */
public class ReflectClass {
    public static void main(String[] args) {
        System.out.println("**********************第一种方式获取Class对象*********************************");
        Author author1 = new Author();//这一new 产生一个Author对象,一个Class对象。
        Class authorClass = author1.getClass();//获取Class对象
        System.out.println(authorClass.getName());

        System.out.println("**********************第二种方式获取Class对象*********************************");
        Class authorClass2 = Author.class;
        System.out.println(authorClass == authorClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个

        System.out.println("**********************第三种方式获取Class对象*********************************");
        try {
            Class authorClass3 = Class.forName("com.study.notes.bean.Author");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(authorClass3 == authorClass2);//判断三种方式是否获取的是同一个Class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}


📌注意事项

  • 在运行期间,一个类只会产生一个Class对象。
  • 虽然有三种方式获取Class对象,但通常最常用的是第三种。原因是:
    • 使用第一种方式时,对象已经存在,那么进行反射似乎显得多余。
    • 使用第二种方式时,需要导入类的包,这增加了依赖性。如果不导入相应的包,则会抛出编译错误。
    • 第三种方式是最灵活的。通过传入一个字符串来获取Class对象,这个字符串可以是硬编码的,也可以从配置文件、数据库等多种来源获得。

3️⃣ 通过Class对象访问类的成分

通过Class对象,我们可以访问某个类的构造方法、成员变量、以及成员方法。这为我们提供了动态创建对象、访问和修改成员等功能。


🔹 批量获取构造方法

  • 获取所有公有构造方法
    public Constructor[] getConstructors()
    
  • 获取所有构造方法(无论是公有、私有、受保护、默认):
    public Constructor[] getDeclaredConstructors()
    

🔹 获取单个构造方法并调用

  • 获取单个公有构造方法

    public Constructor getConstructor(Class... parameterTypes)
    
  • 获取某个构造方法(可以是私有的、受保护的、默认的或公有的):

    public Constructor getDeclaredConstructor(Class... parameterTypes)
    
  • 调用构造方法创建新实例

    Constructor.newInstance(Object... initargs)
    

🔹 完整示例

package com.study.notes.reflect;

import java.lang.reflect.Constructor;


/**
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 *
 * 1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
 *           public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
 *
 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 *
 * 			调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */
public class ReflectConstructor {

    public static void main(String[] args) throws Exception {
        System.out.println("************获取Class对象********************");
        Class clazz = Class.forName("com.study.notes.bean.Author");

        System.out.println("**********************所有公有构造方法*********************************");
        Constructor[] conArray = clazz.getConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("*****************获取公有、无参的构造方法*******************************");
        Constructor con = clazz.getConstructor(null);
        //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
        //2>、返回的是描述这个无参构造函数的类对象。
        System.out.println("con = " + con);
        //调用构造方法
        Object obj = con.newInstance();
        System.out.println("obj = " + obj);

        System.out.println("******************获取私有构造方法,并调用*******************************");
        con = clazz.getDeclaredConstructor(String.class);
        System.out.println(con);
        //调用构造方法
        con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
        obj = con.newInstance("男");
        System.out.println("obj = " + obj);
    }

}


4️⃣ 获取成员方法并执行

通过Class对象,你不仅可以访问类的构造方法,还可以访问它的成员方法,并在运行时动态地执行这些方法。这是Java反射机制的另一个强大功能。


🔹 批量获取

  • 获取所有公有方法(这包括它的所有父类的公有方法,也包括Object类的方法):

    public Method[] getMethods()
    
  • 获取该类所有的方法(不论是私有、受保护、默认、公有,但不包括继承的方法):

    public Method[] getDeclaredMethods()
    

🔹 获取单个方法

  • 获取某个公有的方法

    public Method getMethod(String name, Class<?>... parameterTypes)
    

    参数:

    • name: 要获取的方法名
    • Class<?>... parameterTypes: 方法的参数类型,按声明顺序
  • 获取某个方法(可以是私有的、受保护的、默认的或公有的):

    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    

🔹 执行方法

  • 调用某个对象的方法
    public Object invoke(Object obj, Object... args)
    
    参数:
    • obj: 要执行方法的对象实例
    • args: 调用方法时的参数

这为我们提供了在运行时动态地获取和执行对象的任何方法的能力,无论这些方法是公有的还是私有的。


🔹 完整示例

package com.study.notes.reflect;

import java.lang.reflect.Method;

/**
 * 获取成员方法并调用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 3.调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
 */
public class ReflectMethod {

    public static void main(String[] args) throws Exception {
        System.out.println("************获取Class对象********************");
        Class clazz = Class.forName("com.study.notes.bean.Author");
        
        System.out.println("***************获取所有的”公有“方法*******************");
        clazz.getMethods();
        Method[] methodArray = clazz.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        
        System.out.println("***************获取所有的方法,包括私有的*******************");
        methodArray = clazz.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        
        System.out.println("***************获取公有的show1()方法*******************");
        Method m = clazz.getMethod("show1", String.class);
        System.out.println(m);
        //实例化一个Author对象
        Object obj = clazz.getConstructor().newInstance();
        m.invoke(obj, "刘德华");

        System.out.println("***************获取私有的show4()方法******************");
        m = clazz.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
        System.out.println("返回值:" + result);
        
    }
}


5️⃣ 获取成员变量并操作

在Java中,通过反射机制,你不仅可以访问类的构造方法和成员方法,还可以访问类的成员变量,并在运行时动态地修改这些变量的值。这为动态操作对象提供了很大的灵活性。


🔹 批量获取

  • 获取所有公有变量

    Field[] getFields()
    

    这个方法返回一个包含所有公有成员变量的数组,但不包括继承的变量。

  • 获取该类的所有变量

    Field[] getDeclaredFields()
    

    这个方法返回一个Field数组,包含该类的所有成员变量(不论是私有、受保护、默认、公有)

🔹 获取单个变量

  • 获取某个公有变量

    public Field getField(String fieldName)
    

    参数 fieldName 是你想要获取的成员变量的名字。

  • 获取某个变量(不论是私有、受保护、默认、公有):

    public Field getDeclaredField(String fieldName)
    

🔹 操作成员变量

  • 设置成员变量的值
    public void set(Object obj, Object value)
    
    参数:
    • obj: 要修改的字段所在的对象
    • value: 要为字段设置的新值

此功能使我们能够在运行时动态地访问和修改对象的任何成员变量,不论它是公有的还是私有的。


🔹 完整示例

package com.study.notes.reflect;

import com.study.notes.bean.Author;
import java.lang.reflect.Field;

/**
 * 获取成员变量并调用:
 *
 * 1.批量的
 * 		1).Field[] getFields():获取所有的"公有字段"
 * 		2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
 * 2.获取单个的:
 * 		1).public Field getField(String fieldName):获取某个"公有的"字段;
 * 		2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
 *
 * 	 设置字段的值:
 * 		Field --> public void set(Object obj,Object value):
 * 					参数说明:
 * 					1.obj:要设置的字段所在的对象;
 * 					2.value:要为字段设置的值;
 *
 */
public class ReflectField {

    public static void main(String[] args) throws Exception {
        System.out.println("************获取Class对象********************");
        Class clazz = Class.forName("com.study.notes.bean.Author");

        System.out.println("************获取所有公有的字段********************");
        Field[] fieldArray = clazz.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
        fieldArray = clazz.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        System.out.println("*************获取公有字段并调用***********************************");
        Field f = clazz.getField("name");
        System.out.println(f);
        //获取一个对象
        Object obj = clazz.getConstructor().newInstance();//产生Author对象--》Author author = new Author();
        //为字段设置值
        f.set(obj, "刘德华");//为Author对象中的name属性赋值--》author.name = "刘德华"
        //验证
        Author author = (Author)obj;
        System.out.println("验证姓名:" + author.name);

        System.out.println("**************获取私有字段并调用********************************");
        f = clazz.getDeclaredField("intro");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(obj, "你好");
        System.out.println("简介:" + author);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

yueerba126

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

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

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

打赏作者

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

抵扣说明:

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

余额充值