带有私有字段的类

你们都非常了解字段访问修饰符。如果一个字段有private修饰符,那么我们就不能从外部访问它。


public class Person {
  private int age;
  public String nickname;
  public Person(int age, String nickname) {
   this.age = age;
   this.nickname = nickname;
  }
}

让我们检查Main类中的可访问性:


public class Main {
   public static void main(String[] args) {
     Person person = new Person();  
     System.out.println(person.nickname);
    // System.out.println(person.age); No access to the field
  }
}

我们无权访问年龄领域,但我们确实有反思。:) 在它的帮助下,我们可以访问和使用私有字段。

使用反射从对象中获取私有字段

让我们使用getDeclaredFields()方法获取我们类中所有字段的数组。它返回一个我们可以使用和修改的Field对象:


public static void main(String[] args) {
        Field[] fields = Person.class.getDeclaredFields();
        List<String> actualFieldNames = getFieldNames(fields);
        actualFieldNames.forEach(System.out::println);
    }

    static List<String> getFieldNames(Field[] fields) {
        List<String> fieldNames = new ArrayList<>();
        for (Field field : fields)
            fieldNames.add(Modifier.toString(field.getModifiers()) + " " + field.getName());
        return fieldNames;
    }
私人年龄
公开昵称

getFieldNames方法中,我们从类中获取两个字段。getModifiers 方法返回我们字段的修饰符,getName返回它的名称。现在让我们尝试修改和访问这个字段。首先,让我们尝试从公共字段获取数据:


public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("nickname");
    String nickname = (String) field.get(person);
    System.out.println(nickname);

    System.out.println(person.nickname);
}
CodeGym
代码健身房

我们可以借助反射和使用我们对对象的引用来访问该字段。一切都很好!让我们进入一个私人领域。

我们会将请求字段的名称更改为我们的私有字段年龄场地:


public static void main(String[]args)throws NoSuchFieldException, IllegalAccessException {
		Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    int age =(int)field.get(person);
    System.out.println(age);

    // System.out.println(person.age);
}

我们无法通过创建的对象进行访问,因此请尝试使用反射。我们得到一个错误:

非法访问异常

我们得到一个IllegalAccessException。让我们来看看里面有什么:

让我们试着弄明白。

IllegalAccessException当应用程序试图反射性地创建实例数组除外)、设置或获取字段或调用方法时抛出,而当前正在执行的方法无权访问指定类、字段的定义,方法或构造函数。

这里的方法也会抛出这个异常。为了避免这种异常,我们将使用一种特殊的方法来访问私有字段。

setAccessible(布尔标志)

此方法可以避免对字段或类进行安全访问检查。我们可以将truefalse传递给该方法,以确定是否对该字段执行安全访问检查。让我们尝试修复我们的代码:


public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    field.setAccessible(true);

    int age = (int) field.get(person);
    System.out.println("The current value is " + age);
}

让我们看看结果:

当前值为 10

伟大的!我们得到了关于我们班级的信息。让我们也尝试通过为它分配一个新值来更改我们的字段:


public static void main(String[]args)throws NoSuchFieldException, IllegalAccessException {
Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    field.setAccessible(true);

    field.set(person, 19);

    int age =(int)field.get(person);
    System.out.println("The current value is " + age);
}

改变我们的领域后,我们得到:

当前值为 19

让我们尝试调用setAccessible(false)


public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    
    field.setAccessible(true);
    field.set(person, 19);
    field.setAccessible(false);

    System.out.println("The current value is " + field.get(person));
}

将可访问性恢复为false后,当我们尝试调用get方法时再次遇到异常:

所以在处理私有字段时要小心,不要忘记反射是一个非常强大的工具!