2 反射中get和getDeclared的区别

  • get**():获得某个类的所有的公共(public)的(字段/方法),包括父类中的(字段/方法)。
  • getDeclared**():获得某个类的所有声明的(字段/方法),即包括publicprivateproteced,但是不包括父类的申明(字段/方法)。

3 反射常用api

3.1 Class常用api

(1) Field[] getFields() 返回这个类或其超类的公共字段的Field对象数组。如果Class对象描述的是基本类型或者数组类型,将返回一个长度为0的数组。

(2) Field[] getDeclaredFields() 返回这个类的全部字段对应的Field对象数组。如果Class对象描述的是基本类型或者数组类型,将返回一个长度为0的数组。

(3)Field getField(String name) 返回指定名称的Field。如果不存在对应名称的字段,则抛出异常 NoSuchFieldException 。

(4)Field getDeclaredField(String name) 返回指定名称的Field。如果不存在对应名称的字段,则抛出异常 NoSuchFieldException 。

(5) Method[] getMethods() 返回所有的公共方法,包括从超类继承来的公共方法。

(6) Method[] getDeclaredMethods() 返回这个类或者接口的所有方法,但是不包括从超类继承的方法。

(7) Constructor[] getConstructors() 返回这个类的公共构造方法。

(8) Constructor[] getDeclaredConstructors(Class<?>... parameterTypes) 返回这个类的全部构造方法。参数类型是一个类对象的数组,该数组标识构造函数的正式参数类型,即声明的顺序

(9) String getPackageName 得到包含这个类型的包的包名。如果该类是一个数组类型,则返回元素类型所属的包。如果是基本数据类型,则返回 “java.lang”。

4 反射修改static final修饰的字段

Java反射-修改字段值, 反射修改static final修饰的字段

4.1 修改private修饰StringBuilder类型的字段

public class Pojo {
    private StringBuilder name = new StringBuilder("default");

    public void printName() {
        System.out.println(name);
    }
}

//测试
Pojo p = new Pojo();
// 查看被修改以前的值
p.printName();
// 反射获取字段, name成员变量
Field nameField = p.getClass().getDeclaredField("name");
// 因为name成员变量是private, 因此须要进行访问权限设定
nameField.setAccessible(true);
// 使用反射进行赋值
nameField.set(p, new StringBuilder("111"));
// 打印查看被修改后的值
p.printName();

4.2 修改final修饰StringBuilder类型的字段

Pojo2 p = new Pojo2();
// 查看被修改以前的值
p.printName();
// 反射获取字段, name成员变量
Field nameField = p.getClass().getDeclaredField("name");
// 因为name成员变量是private, 因此须要进行访问权限设定
nameField.setAccessible(true);
// 使用反射进行赋值
nameField.set(p, new StringBuilder("111"));
// 打印查看被修改后的值
p.printName();

4.3 修改final修饰String类型的字段(失败)

Pojo3 p = new Pojo3();
p.printName();      //"default"
Field nameField = p.getClass().getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "111");
p.printName();      //"default"

修改失败

由于JVM在编译时期, 就把final类型的String进行了优化, 在编译时期就会把String处理成常量, 因此 Pojo3里的printName()方法被写死了:

public void printName() {
    System.out.println("default");
}

而看似name修改失败,其实修改成功了

Pojo3 p = new Pojo3();
Field nameField = p.getClass().getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "111");
Object name = nameField.get(p);
System.out.println(name.toString());    //"111"

4.4 修改final修饰String类型的字段(成功)

阻止final修饰的String在JVM编译时就被处理为常量。

可以让final String类型的name的初始值通过一次运行才能获得, 那么就不会在编译时期就被处理为常量。

public class Pojo4 {
    // 防止JVM编译时就把"default4"做为常量处理
    private final String name = (null == null ? "default4" : "");
    //或者
    //private final String name = new StringBuilder("default5").toString();
    ...
}
//测试
Pojo4 p = new Pojo4();
p.printName();
Field nameField = p.getClass().getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "111");
p.printName();      //  "111"

4.5 修改static修饰StringBuilder类型的字段

成功

4.6 修改static final修饰StringBuilder类型的字段(失败)

Pojo7 p = new Pojo7();
p.printName();
Field nameField = p.getClass().getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, new StringBuilder("111"));
p.printName();

上述代码会抛异常,反射没法修改同时被static final修饰的变量。

4.6 修改static final修饰StringBuilder类型的字段(成功)

Field nameField = p.getClass().getDeclaredField("name");
nameField.setAccessible(true);
//去掉final修饰符
Field modifiers = nameField.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL);
//再修改就正常了
nameField.set(p, new StringBuilder("111"));
//最后加回final修饰符
modifiers.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL);
Copyright © qgao 2021-* all right reserved,powered by Gitbook该文件修订时间: 2022-08-15 18:43:44

results matching ""

    No results matching ""