Java 通过反射获取和设置类中属性及方法(包括私有)

先看一下我们主要用到的方法

public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException

API文档解释:

返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。name 参数是一个 String,它指定所需字段的简称。注意,此方法不反映数组类的 length 字段。

这里的Filed是一个类,要获取类属性和方法就要用到Field类中的方法

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

有了这些我们就可以开始了

首先我们创建一个简单的Person类:

public class Person {
    public String name;
    protected String gender;
    int height;
    private int  age;

    public Person() {
    }

    @Override
    public String toString() {
        return "Person "+"["+"name="+this.name+","+"age="+this.age+"]";
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height=height;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender=gender;
    }

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

此处注意到我们给这里的四个属性分别设置了四种不同的访问权限。

下面开始:
先试试这两个方法的输出结果:getDeclaredFields()和 getFields()

public static void main(String[] args) throws ClassNotFoundException {
	      Class c = Class.forName("RunTimeTest.Person");
	
	      Field[] declaredFields=c.getDeclaredFields();
	      for (Field declaredField : declaredFields) {
	          System.out.println(declaredField);
	      }
	
	      System.out.println("=========================");
	
	      Field[] fields=c.getFields();
	      for (Field field : fields) {
	          System.out.println(field);
	      }
}

在这里插入图片描述
我们来看看API怎么说:

getFields() : 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

getDeclaredFields() :返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

看完我们就明白了,加了Declare的方法可以访问所有属性(方法),没有加Declare的方法只能访问公共字段。

下面试试设置public型变量:

public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("RunTimeTest.Person");
        Person p = new Person();
        System.out.println(p);

        try {
            Field name=c.getField("name");
            name.set(p,"王五");
            System.out.println(p);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
可以看到我们通过set()成功把公有属性的name设置成了”王五“

在试试访问保护成员

    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("RunTimeTest.Person");
        Person p = new Person();

        try {
            Field gender=c.getField("gender");
//            Field gender=c.getDeclaredField("gender");
            System.out.println(gender);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
可以看到,这里如果用getField(“gender”)来获取gender属性,在打印此属性的时候则会报错,提示:NoSuchFieldException
因为前面也说了getField();方法只能获取到公有的属性或方法。
在使用了getDeclaredField(“gender”);之后就能成功打印了
在这里插入图片描述

统一设置一波:

    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("RunTimeTest.Person");
        Person p = new Person();
        System.out.println(p);

        try {
            Field name=c.getField("name");
            Field gender=c.getDeclaredField("gender");
            Field height=c.getDeclaredField("height");
            Field age=c.getDeclaredField("age");

            name.set(p,"王五");
            gender.set(p,"男");
            height.set(p,180);
//            age.set(p,66);

            System.out.println(p);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

这里我们又注意到,我age怎么不设置,why?

aee~ 因为age是私有变量嘛,私有变量哪有这么容易被访问,还想修改。。。 在这里对于私有属性或方法,我们需要多进行一个步骤即可访问/设置,那就是setAccessible(boolean flag)
看看API的解释:

public void setAccessible(boolean flag) throws SecurityException
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

如果不设置,可以看看报错信息:
cannot access a member of class RunTimeTest.Person with modifiers "private"
翻译一下就是:无法访问带有修饰符“ private”的RunTimeTest.Person类的成员在这里插入图片描述
所有只要在上面添加一句:age.setAccessible(true); 就可以成功访问啦
在这里插入图片描述
调用方法和属性差不多,只不过在调用的时候是用invoke(),而不是用set()。
如下:

       try {
            Method setName=c.getMethod("setName", String.class);
            setName.invoke(p,"李四");
            System.out.println(p);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

在这里插入图片描述
同样,如果方法是私有的话也要使用setAccessible(boolean flag); flag为 true,并且调用的方法必须是getDeclaredMethod(String name, Class<?>... parameterTypes),不能使用getMethod(String name, Class<?>... parameterTypes)

不然也会报错:NoSuchMethodException

总结:
1、getField()方法只能访问公有 属性/方法,getDeclaredField()方法可以访问所有 属性/方法
2、设置属性用 set(),设置方法用 invoke()
3、访问私有属性/方法时要使用带有Declare的方法,并且将setAccessible(boolean flag)的flag设为true

PS:文章中多次提到API文档,这里附上一份链接供大家下载参考:
链接:https://pan.baidu.com/s/18TspjA37qM9qKMKqWQwB2w
提取码:p3hz

大家还可以看看这篇文章JAVA中的反射机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值