За работу с полями классов в пакете java.lang.reflect отвечает класс Field. Для получения информации о полях и управления ими этот класс определеяет ряд методов, из которых отмечу основные:
String getName(): возвращает имя поля
Class getType(): возвращает тип поля в виде объекта Class
int getModifiers(): возвращает целое число с различными включенными и выключенными битами, которые описывают используемые модификаторы.
Set<AccessFlag> accessFlags(): возвращает набор флагов доступа для этого поля
Object get(Object obj): возвращает имя поля
void set(Object obj, Object value): возвращает имя поля
Для манипулирования полями нам прежде всего надо получить их. Для получения полей типа применяется ряд методов класса Class:
Field[] getFields(): возвращает массив объектов Field для открытых полей (с модификатором public) данного класса или его суперклассов
Field[] getDeclaredFields(): возвращает массив объектов Field для всех полей данного класса. Метод возвращают массив нулевой длины, если таких полей нет или если объект
представляет собой примитивный тип или массив.
Field getField(String name): возвращает объект Field для публичного поля, имя которого передается в качестве параметра. Поиск также идет среди полей суперклассов. Если подобное публичное поле отсутствует, то метод генерирует исключение NoSuchFieldException
Field getDeclaredField(String name): возвращает объект Field для поля, имя которого передается в качестве параметра. Если подобное поле отсутствует, то метод генерирует исключение NoSuchFieldException
Например, получим поля класса:
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// получаем класс для типа Person
Class cl = Person.class;
// получаем поля
Field[] fields = cl.getDeclaredFields();
for(Field f : fields){
// получаем тип и имя поля
System.out.printf("%s %s;\n", f.getType().getName(), f.getName());
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
void print(){
System.out.printf("Person. Name: %s; Age: %d\n", name, age);
}
}
Консольный вывод:
java.lang.String name; int age;
Теперь получим по отдельности поля, используя их имя:
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// получаем класс для типа Person
Class cl = Person.class;
try{
// получаем поля name и age
Field nameField = cl.getDeclaredField("name");
Field ageField = cl.getDeclaredField("age");
System.out.println(nameField);
System.out.println(ageField);
}
catch(NoSuchFieldException ex){
System.out.println(ex);
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
void print(){
System.out.printf("Person. Name: %s; Age: %d\n", name, age);
}
}
В данном случае с помощью метода getDeclaredField() получаем поля name и age класса Person. Обратите внимание, что при передаче имени поля учитывается регистр имени. В итоге на консоль будет выведена
информация о полученных полях:
private java.lang.String Person.name private int Person.age
Манипуляции обычно включают получение значения поля или его установку. для этого класс Field предоставляет методы get()/set().
По умолчанию механизм рефлексии учитывает контроль доступа Java. Так, при обращении к закрытым полям методы get() и set() по умолчанию вызываюют исключение IllegalAccessException - исключение недопустимого доступа.
И по умолчанию методы get и set можно использовать только с доступными полями. Механизм безопасности Java позволяет узнать, какие поля есть у объекта, но не позволяет читать и записывать
значения этих полей без соответствующего разрешения.
Но мы можем переопределить контроль доступа, вызвав метод setAccessible() для объекта Field и передав в метод true:
field.setAccessible(true);
Вызов setAccessible вызывает исключение, если доступ не предоставлен. Доступ может быть запрещён модульной системой.
Для получения значения поля применяется метод get(), который имеет следующее определение:
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
В качестве параметра в метод передается объект, значение поля которого надо получить. Например, получим значения полей объекта Person:
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// Исследуемый объект
Person tom = new Person("Tom", 41);
// получаем класс для типа Person
Class cl = tom.getClass();
try{
// получаем поля name и age
Field nameField = cl.getDeclaredField("name");
Field ageField = cl.getDeclaredField("age");
// делаем поля доступными для доступа
nameField.setAccessible(true);
ageField.setAccessible(true);
// получаем значения полей
Object name = nameField.get(tom); // Tom
Object age = ageField.get(tom); // 41
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
catch(Exception ex){
System.out.println(ex);
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
void print(){
System.out.printf("Person. Name: %s; Age: %d\n", name, age);
}
}
Для изменения значения поля применяется метод set():
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
ПЕрвый параметр (obj) представляет объект, поле которого надо изменить, а второй параметр (value) - присваемое полю новое значение. Посмотрим на примере:
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// Исследуемый объект
Person tom = new Person("Tom", 41);
// получаем класс для типа Person
Class cl = tom.getClass();
try{
// получаем поля name и age
Field nameField = cl.getDeclaredField("name");
Field ageField = cl.getDeclaredField("age");
// делаем поля доступными для доступа
nameField.setAccessible(true);
ageField.setAccessible(true);
// изменяем значения полей
nameField.set(tom, "Bob");
ageField.set(tom, 22);
tom.print(); // Person. Name: Bob; Age: 22
}
catch(Exception ex){
System.out.println(ex);
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
void print(){
System.out.printf("Person. Name: %s; Age: %d\n", name, age);
}
}
Таким образом, благодаря рефлексии приватность полей является преградой для их изменения.