Класс с приватным полем

Каждый из вас отлично знает про модификаторы доступа полей. И если у поля стоит модификатор 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); нет доступа к полю
  }
}

Доступа к полю age у нас нет, но есть рефлексия. И с ее помощью мы можем получить доступ к приватным полям и работать с ними.

Получение приватного поля у объекта через рефлексию

Давай получим массив всех полей нашего класса с помощью метода getDeclaredFileds(). Он возвращает класс Fileds, с которым мы будем дальше работать и преобразовывать:

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;
    }
private age
public nickname

В методе getFieldNames мы получаем два поля из нашего класса. Метод getModifiers возвращает нам модификатор нашего поля, а getName — название. Теперь давай попробуем изменить и получить доступ к этому полю. Сначала попробуем достать данные из публичного поля:

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

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

    System.out.println(person.nickname);
}
javaRush
javaRush

Доступ к полю и с помощью рефлексии, и с помощью обращения к объекту у нас есть. Все отлично! Движемся к приватному полю.

Меняем название поля, которое мы хотим получить, на наше приватное поле age:

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

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

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

У нас нет доступа через созданный экземпляр класса, поэтому пробуем через рефлексию и сталкиваемся с ошибкой:

IllegalAccessException

У нас возникает IllegalAccessException. Давай перейдем внутрь и посмотрим, что внутри:

Пробуем разобраться.

Исключение IllegalAccessException возникает, когда приложение пытается рефлективно создать экземпляр (кроме массива), задать или получить поле или вызвать метод, но выполняемый в данный момент метод не имеет доступа к определению указанного класса, поля, метода или конструктора.

Также тут есть методы, которые выбрасывают это исключение. Чтобы избежать выбрасывания такого исключения, мы будем использовать специальный метод для получения доступа к приватному полю.

setAccessible(boolean flag)

Этот метод позволяет избежать проверки на доступ к полю или методу класса из-за его модификатора доступа. В метод мы можем передать параметр true или false, который определит, необходимы ли нам проверки на доступ к полю. Пробуем исправить наш код:

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

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

    int age = (int) field.get(person);
    System.out.println("Текущее значение - " + age);
}

Смотрим на результат:

Текущее значение - 10

Отлично, мы получили данные нашего класса. Давай заодно попробуем изменить наше поле, установив в него новое значение:

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

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

    field.set(person, 19);

    int age =(int)field.get(person);
    System.out.println("Текущее значение - " + age);
}

Пробуем изменить наше поле и получаем результат:

Текущее значение - 19

Пробуем выставить setAccessible(false).

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

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

    field.setAccessible(true);
    field.set(person, 19);
    field.setAccessible(false);

    System.out.println("Текущие значение - " + field.get(person));
}

После возвращения доступа на false мы снова сталкиваемся с нашим исключением, когда пытаемся вызвать метод get:

Поэтому будь осторожен, когда работаешь с private-полями и не забывай, что рефлексия — очень мощный инструмент для работы!