Java反射

本文深入解析Java反射机制,涵盖反射的原理、应用场景及如何通过反射获取类、构造方法、成员变量和成员方法的信息。通过实例演示反射在动态创建对象、调用方法和设置变量值的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 为什么使用反射?

       Java程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型,例如:People p = new Student();将会生成一个p变量,该变量编译时类型为People,运行时类型为Student;除此外,还有一种情况,程序在运行时接收到外部传入的一个对象,该对象的编译时类型为Object,但程序又需要调用该对象运行时类型的方法。故程序需要在运行时发现对象和类的真实信息,解决方法有两种:

  1. 假设在编译时和运行时知道对象和类的具体信息,此时可以先用instanceof进行判断,然后再使用强转将其转换为运行时类型的变量;
  2. 假设编译时不知道对象和类的具体信息,程序只能依靠运行时的信息来获取对象和类的信息,这就必须使用反射。 
  • 什么是反射?

  •  能够分析类能力的程序即为反射,反射就是将类别的各个组成部分进行剖析,可以得到每个组成部分,就可以对每一部分进行操作。使用反射,可以实现:在运行中分析类的能力,在运行中查看对象,实现数组的操作代码,利用Method对象等。 
  • 反射的使用(反射使用的前提,就是要获取Class对象)

  1. 获取Class对象(每个类被加载后,系统就会为该类生成一个对应的Class对象,通过Class对象就可以访问JVM中的这个类)

            (1)通过调用对象的getClass ()方法;

            (2)通过调用类的class属性获取该类对应的Class对象;

            (3)通过Class类的静态方法:forName(String className);(最为常用) 

代码如下:

public static void main(String[] args) {
        //获取Class对象的第一种方式
        People p1 = new People();//new产生了一个People对象,一个Class对象
        Class classPeo1 = p1.getClass();//获取Class对象   getClass()返回一个对象的运行时类
        System.out.println(classPeo1.getName());

        //获取Class对象的第二种方式
        Class classPeo2 = People.class;
        System.out.println(classPeo1 == classPeo2);//判断第一种方式与第二种方式获取的Class对象是否是同一个;

        //获取Class对象的第三种方式
        try {
            Class classPeo3 = Class.forName("com.tulun.myjava.People");//	注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(classPeo3 == classPeo2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //第三种最常用。第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,
        // 不导包就抛编译错误,一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
    }

结果如下:

com.tulun.myjava.People
true
true

表明三种获取Class对象的方式获得的对象是同一个,但一般来说,第三种方法(Class类的静态方法forName())最为常用。

  1. 利用反射获取构造方法并调用
 通过Class对象可以获得某个类中的构造方法,属性,方法,并访问成员
* 1、获取构造方法
* (1)批量获取
*          public Constructor[] getConstructors();所有“公有”的构造方法
*          public Constructor[] getDeclaredConstructors();所有的构造方法(公有的,受保护的,私有的)
* (2)单个获取
*          public Constructor getConstructor(Class...parameterTypes);获取单个的“公有”构造方法
*          public Constructor getDeclaredConstructor(Class...parameterTypes);所有的构造方法(公有的,受保护的,私有的)
*      调用构造方法:
*          Constructor--->newInstance(Object...initargs)

代码如下:在People类中

public class People {
    //默认构造方法
    public People(String name){
        System.out.println("默认构造方法"+name);
    }
    //无参构造方法
    public People(){
        System.out.println("调用了公有,无参方法执行了");
    }
    //有一个参数的构造方法
    public People(char ch){
        System.out.println("属性:"+ch);
    }
    //有多个参数的构造方法
    public People(String name,int age){
        System.out.println("姓名:"+name+"年龄:"+age);
    }
    //受保护的构造方法
    protected People(boolean n){
        System.out.println("受保护的方法  n=  "+n);
    }
    //私有构造方法
    private People(int age){
        System.out.println("私有的构造方法:  "+age);
    }
}

测试函数:

 public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, ClassNotFoundException {
        //加载类对象
        Class c = Class.forName("com.tulun.myjava.People");

        //获取所有“公有”方法
        System.out.println("***********所有公有构造方法************");
        Constructor[] con = c.getConstructors();
        for(Constructor n:con){
            System.out.println(n);
        }
        //获取所有构造方法
        System.out.println("***********所有构造方法************");
        Constructor[] con1 = c.getDeclaredConstructors();
        for(Constructor m:con1){
            System.out.println(m);
        }
        //获取公有,无参的构造方法
        System.out.println("***********所有公有,无参构造方法************");
        Constructor con2 = c.getConstructor();
        System.out.println("con2 = "+con2);

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

 输出结果:

***********所有公有构造方法************
public com.tulun.myjava.People(java.lang.String,int)
public com.tulun.myjava.People(char)
public com.tulun.myjava.People()
public com.tulun.myjava.People(java.lang.String)
***********所有构造方法************
private com.tulun.myjava.People(int)
protected com.tulun.myjava.People(boolean)
public com.tulun.myjava.People(java.lang.String,int)
public com.tulun.myjava.People(char)
public com.tulun.myjava.People()
public com.tulun.myjava.People(java.lang.String)
***********所有公有,无参构造方法************
con2 = public com.tulun.myjava.People()
调用了公有,无参方法执行了
***********获取私有方法,并调用************
public com.tulun.myjava.People(char)
属性:男

分析:newInstance()方法可以实现快速创建一个类的实例,该方法调用默认构造器(无参)来初始化新创建的对象,若没有无参构造器,就会抛出异常。而将forName()与newInstance()结合起来使用,可以根据对应类名创建一个对象。

         Class[] getParameterTypes()(Method和Constructor类中)可以返回一个用于描述参数类型的Class对象数组

   2. 利用反射获取成员变量并调用

2、获取成员变量
 *     (1)批量获取
 *           public Field[] getFields();//获取所有公有成员变量
 *           public Field[] getDeclaredFields();//获取所有成员变量(包括公有,私有,受保护)
 *     (2)单个获取
 *           public Field getField(String fieldname);//获取某个公有成员变量
 *           public Field getDeclaredField(String fieldname);//获取某个成员变量(包括公有,私有,受保护)
 *        设置成员变量的值
 *        Field--->public void set(Object obj,Object value);

在Student类中:

public class Student {
    public String name;
    int age;
    protected char sex;
    private String id;
    public Student(){

    }

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

    public void show1(String str){
        System.out.println("调用了:公有的,String参数的show1():str = "+str);
    }
    protected void show2(){
        System.out.println("调用了:受保护的,无参的show2()");
    }
    void show3(){
        System.out.println("调用了:,默认的,无参的show3()");
    }
    private String show4(int age){
        System.out.println("调用了:私有的,有age参数的show4():age = "+age);
        return "aaa";
    }
}

测试函数:

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        //获取Class对象
        Class cc = Class.forName("com.tulun.myjava.Student");
        System.out.println("******获取所有公有成员变量******");
        Field[] f = cc.getFields();
        for(Field ff:f){
            System.out.println(ff);
        }

        System.out.println("********获取所有成员变量(公有,私有,受保护)**********");
        Field[] f1 = cc.getDeclaredFields();
        for(Field ff1:f1){
            System.out.println(ff1);
        }
        System.out.println("**********获取某个公有成员变量并调用******");
        Field ff2 = cc.getField("name");
        System.out.println(ff2);
        //获取一个对象
        Object obj = cc.getConstructor().newInstance();//产生Student对象--》Student stu = new Student()
        //为字段设置值
        ff2.set(obj,"张三");//为Student中的name赋值
        //验证
        Student stu = (Student)obj;
        System.out.println("验证姓名:"+stu.name);

        System.out.println("********获取私有成员变量并调用*********");
        Field ff3 = cc.getDeclaredField("id");
        System.out.println(ff3);
        ff3.setAccessible(true);//暴力访问,解除私有限定
        ff3.set(obj,"201112020202");
        System.out.println("验证电话:"+stu);
    }

输出结果:

******获取所有公有成员变量******
public java.lang.String com.tulun.myjava.Student.name
********获取所有成员变量(公有,私有,受保护)**********
public java.lang.String com.tulun.myjava.Student.name
int com.tulun.myjava.Student.age
protected char com.tulun.myjava.Student.sex
private java.lang.String com.tulun.myjava.Student.id
**********获取某个公有成员变量并调用******
public java.lang.String com.tulun.myjava.Student.name
验证姓名:张三
********获取私有成员变量并调用*********
private java.lang.String com.tulun.myjava.Student.id
验证电话:Student[name =张三,age = 0,sex =  ,id = 201112020202]

分析:反射机制的默认行为受限于访问控制,一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制,setAccessible()方法可以实现这一目的,setAccessible()是AccessibleObject类中的一个方法,它是Constructor,Field和Method类的公共超类。

3. 利用反射获取成员方法并调用

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

在Student类中,测试函数如下:

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException {
        Class nn = Class.forName("com.tulun.myjava.Student");
        System.out.println("*********获取所有公有成员方法*********");
        Method[] m = nn.getMethods();
        for (Method mm : m) {
            System.out.println(mm);
        }

        System.out.println("**********获取所有成员方法(包括私有,默认)***********");
        Method[] m1 = nn.getDeclaredMethods();
        for (Method mm1 : m1) {
            System.out.println(mm1);
        }

        System.out.println("***********获取公有的show1()方法***********");
        Method m2 = nn.getMethod("show1", String.class);
        System.out.println(m2);

        //实例化一个Student对象
        Object obj = nn.getConstructor().newInstance();
        m2.invoke(obj, "张三");

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

    }

输出结果:

*********获取所有公有成员方法*********
public void com.tulun.myjava.Student.show1(java.lang.String)
public java.lang.String com.tulun.myjava.Student.toString()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
**********获取所有成员方法(包括私有,默认)***********
public void com.tulun.myjava.Student.show1(java.lang.String)
void com.tulun.myjava.Student.show3()
protected void com.tulun.myjava.Student.show2()
private java.lang.String com.tulun.myjava.Student.show4(int)
public java.lang.String com.tulun.myjava.Student.toString()
***********获取公有的show1()方法***********
public void com.tulun.myjava.Student.show1(java.lang.String)
调用了:公有的,String参数的show1():str = 张三
***********获取私有的show4()方法**********
private java.lang.String com.tulun.myjava.Student.show4(int)
调用了:私有的,有age参数的show4():age = 15
返回值:aaa

分析:m2 = nn.getDeclaredMethod("show4", int.class);//调用制定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。

         m2.setAccessible(true);//解除私有限定
        Object result = m2.invoke(obj, 15);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参

关于invoke()方法,invoke(Object obj,Object...args);//对带有指定参数的指定对象调用由此Method对象表示的底层方法。

参考文章:https://blog.youkuaiyun.com/sinat_38259539/article/details/71799078

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值