Рубрики
Без рубрики

Чтение значений полей “private” из другого класса в Java

Узнайте, как получить доступ к закрытым полям другого класса и прочитать их значения с помощью API отражения Java.

Автор оригинала: baeldung.

1. Обзор

В этом кратком руководстве мы обсудим, как мы можем получить доступ к значению поля private из другого класса в Java.

Прежде чем приступить к учебнику, мы должны понять, что модификатор private access предотвращает случайное неправильное использование полей. Однако, если мы хотим получить к ним доступ, мы можем сделать это с помощью API Reflection .

2. Пример

Давайте определим пример класса Person с некоторыми частными полями:

public class Person {

    private String name = "John";
    private byte age = 30;
    private short uidNumber = 5555;
    private int pinCode = 452002;
    private long contactNumber = 123456789L;
    private float height = 6.1242f;
    private double weight = 75.2564;
    private char gender = 'M';
    private boolean active = true;

    // getters and setters
}

3. Обеспечение Доступа к частным Полям

Чтобы сделать любое частное поле доступным, мы должны вызвать метод Field#setAccessible :

Person person = new Person(); 
Field nameField = person.getClass().getDeclaredField("name"); 
nameField.setAccessible(true);

В приведенном выше примере мы сначала указываем поле, которое мы хотим получить – name – с помощью метода Class#getDeclaredField . Затем мы делаем поле доступным с помощью name Field.setAccessible(true) .

4. Доступ к закрытым примитивным полям

Мы можем получить доступ к полям private , которые являются примитивами, используя методы Field#getXXX .

4.1. Доступ к целочисленным полям

Мы можем использовать методы getByte, getShort , getInt и get Long для доступа к полям byte , | short , int и long соответственно:

@Test
public void whenGetIntegerFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field ageField = person.getClass().getDeclaredField("age");
    ageField.setAccessible(true);

    byte age = ageField.getByte(person);
    Assertions.assertEquals(30, age);

    Field uidNumberField = person.getClass().getDeclaredField("uidNumber");
    uidNumberField.setAccessible(true);

    short uidNumber = uidNumberField.getShort(person);
    Assertions.assertEquals(5555, uidNumber);

    Field pinCodeField = person.getClass().getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    int pinCode = pinCodeField.getInt(person);
    Assertions.assertEquals(452002, pinCode);

    Field contactNumberField = person.getClass().getDeclaredField("contactNumber");
    contactNumberField.setAccessible(true);

    long contactNumber = contactNumberField.getLong(person);
    Assertions.assertEquals(123456789L, contactNumber);
}

Также можно выполнить автобокс с примитивными типами:

@Test
public void whenDoAutoboxing_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field pinCodeField = person.getClass().getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    Integer pinCode = pinCodeField.getInt(person);
    Assertions.assertEquals(452002, pinCode);
}

То getXXX методы для примитивных типов данных также поддерживают расширение :

@Test
public void whenDoWidening_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field pinCodeField = person.getClass().getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    Long pinCode = pinCodeField.getLong(person);
    Assertions.assertEquals(452002L, pinCode);
}

4.2. Доступ к Полям Плавающего Типа

Чтобы получить доступ к полям float и double , нам нужно использовать методы getFloat и getDouble соответственно:

@Test
public void whenGetFloatingTypeFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field heightField = person.getClass().getDeclaredField("height");
    heightField.setAccessible(true);

    float height = heightField.getFloat(person);
    Assertions.assertEquals(6.1242f, height);
    
    Field weightField = person.getClass().getDeclaredField("weight");
    weightField.setAccessible(true);

    double weight = weightField.getDouble(person);
    Assertions.assertEquals(75.2564, weight);
}

4.3. Доступ к символьным Полям

Чтобы получить доступ к полям char , мы можем использовать метод getChar :

@Test
public void whenGetCharacterFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field genderField = person.getClass().getDeclaredField("gender");
    genderField.setAccessible(true);

    char gender = genderField.getChar(person);
    Assertions.assertEquals('M', gender);
}

4.4. Доступ к логическим полям

Аналогично, мы можем использовать метод getBoolean для доступа к полю boolean :

@Test
public void whenGetBooleanFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field activeField = person.getClass().getDeclaredField("active");
    activeField.setAccessible(true);

    boolean active = activeField.getBoolean(person);
    Assertions.assertTrue(active);
}

5. Доступ К закрытым Полям, Которые Являются Объектами

Мы можем получить доступ к полям private , которые являются объектами, с помощью метода Field#get |. Следует отметить , что универсальный get метод возвращает Объект , поэтому нам нужно будет привести его к целевому типу, чтобы использовать значение :

@Test
public void whenGetObjectFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field nameField = person.getClass().getDeclaredField("name");
    nameField.setAccessible(true);

    String name = (String) nameField.get(person);
    Assertions.assertEquals("John", name);
}

6. Исключения

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

6.1. Исключение незаконных аргументов

JVM бросит IllegalArgumentException если мы используем метод доступа getXXX , несовместимый с типом целевого поля . В нашем примере, если мы напишем name Field.getInt(person) , JVM выдаст это исключение, поскольку поле имеет тип String , а не int или Integer :

@Test
public void givenInt_whenSetStringField_thenIllegalArgumentException() 
  throws Exception {
    Person person = new Person();
    Field nameField = person.getClass().getDeclaredField("name");
    nameField.setAccessible(true);

    Assertions.assertThrows(IllegalArgumentException.class, () -> nameField.getInt(person));
}

Как мы уже видели, методы getXXX поддерживают расширение для примитивных типов. Важно отметить, что мы должны обеспечить правильную цель для расширения, чтобы быть успешными . В противном случае JVM создает исключение IllegalArgumentException :

@Test
public void givenInt_whenGetLongField_thenIllegalArgumentException() 
  throws Exception {
    Person person = new Person();
    Field contactNumberField = person.getClass().getDeclaredField("contactNumber");
    contactNumberField.setAccessible(true);

    Assertions.assertThrows(IllegalArgumentException.class, () -> contactNumberField.getInt(person));
}

6.2. Исключение IllegalAccessException

JVM выдаст исключение IllegalAccessException , если мы пытаемся получить доступ к полю, у которого нет прав доступа . В приведенном выше примере, если мы не напишем оператор name Field.setAccessible(true) , то JVM выдаст исключение:

@Test
public void whenFieldNotSetAccessible_thenIllegalAccessException() 
  throws Exception {
    Person person = new Person();
    Field nameField = person.getClass().getDeclaredField("name");

    Assertions.assertThrows(IllegalAccessException.class, () -> nameField.get(person));
}

6.3. Исключение NoSuchFieldException

Если мы попытаемся получить доступ к полю, которое не существует в классе Person , то JVM может выдать NoSuchFieldException :

Assertions.assertThrows(NoSuchFieldException.class,
  () -> person.getClass().getDeclaredField("firstName"));

6.4. Исключение NullPointerException

Наконец, как и следовало ожидать, JVM выдает исключение NullPointerException , если мы передаем имя поля как null :

Assertions.assertThrows(NullPointerException.class,
  () -> person.getClass().getDeclaredField(null));

7. Заключение

В этом уроке мы рассмотрели, как мы можем получить доступ к полям private класса в другом классе. Мы также видели исключения, которые может создавать JVM, и то, что их вызывает.

Как всегда, полный код для этого примера доступен на GitHub .