5

Как получить значение закрытого поля из другого класса в Java?

12

У меня есть плохо спроектированный класс в стороннем JAR, и мне нужно получить доступ к одному из его приватных полей. Например, возникает вопрос, почему я должен выбирать приватное поле и нужно ли это делать?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

Как я могу использовать рефлексию, чтобы получить значение stuffIWant?

5 ответ(ов)

7

Чтобы получить доступ к приватным полям, вам необходимо извлекать их из объявленных полей класса и сделать их доступными:

Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException

ИСПРАВЛЕНИЕ: как отметил aperkins, доступ к полю, установка его в доступный вид и извлечение значения могут вызывать Exception, хотя единственные проверяемые исключения, о которых следует помнить, приведены выше.

NoSuchFieldException будет выброшено, если вы попытаетесь получить поле по имени, которое не соответствует ни одному из объявленных полей.

obj.getClass().getDeclaredField("misspelled"); //выдаст NoSuchFieldException

IllegalAccessException будет выброшено, если поле недоступно (например, если оно является приватным и вы не установили доступность с помощью строки f.setAccessible(true)).

Среди RuntimeException, которые могут быть выброшены, есть либо SecurityException (если SecurityManager JVM не позволит вам изменить доступность поля), либо IllegalArgumentException, если вы попробуете получить доступ к полю объекта, который не соответствует типу класса этого поля:

f.get("BOB"); //выдаст IllegalArgumentException, так как String имеет неправильный тип
0

Одним из других вариантов, который еще не был упомянут: используйте Groovy. Groovy позволяет вам получать доступ к приватным переменным экземпляра благодаря особенностям дизайна языка. Независимо от того, есть ли у вас метод-геттер для поля, вы можете просто воспользоваться следующим кодом:

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()

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

0

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

С учетом сказанного, если этот класс так плохо спроектирован, что вам приходится прибегать к таким уловкам, возможно, стоит поискать альтернативу. Конечно, этот небольшой хак может сэкономить вам пару часов сейчас, но сколько это будет стоить вам в будущем?

0

Чтобы использовать фреймворк оптимизации Soot для прямого изменения байт-кода, вам нужно выполнить несколько шагов.

  1. Подключение Soot к вашему проекту: Сначала добавьте Soot в зависимости вашего проекта, например, через Maven:

    <dependency>
        <groupId>org.picocontainer</groupId>
        <artifactId>soot</artifactId>
        <version>4.3.0</version> <!-- Убедитесь, что используете актуальную версию -->
    </dependency>
    
  2. Конфигурация Soot: Перед началом важно настроить Soot. Это включает в себя указание классов, которые предстоит анализировать и изменять. Вы можете начать с основной настройки, например:

    String[] sootArgs = {
        "-process-dir", "путь/к/вашему/коду",
        "-d", "путь/для/выходных/файлов",
        "-src-prec", "java",
        "-target", "1.8", // или другая версия Java
    };
    soot.Main.main(sootArgs);
    
  3. Изменение байт-кода: Для внесения изменений в байт-код вам нужно будет создать анализатор и визиторы (visitors). Например, чтобы изменить определённый метод, вы можете использовать Transformer в Soot:

    public class MyBodyTransformer extends BodyTransformer {
        @Override
        protected void internalTransform(Body body, String phase, Map<String, String> options) {
            // Логика для изменения байт-кода
        }
    }
    
  4. Запуск и генерация новых классов: После внесения всех необходимых изменений запустите процесс обработки, и Soot сгенерирует изменённые классы в указанной вами директории.

Обратите внимание, что Soot полностью написан на Java и поддерживает новые версии Java, что делает его гибким инструментом для работы с байт-кодом. Для более глубокого понимания стоит ознакомиться с документацией Soot, которая содержит множество примеров и подробности по различным аспектам работы с фреймворком.

0

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

private static Field getField(Class<?> cls, String fieldName) {
    for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
        try {
            final Field field = c.getDeclaredField(fieldName);
            field.setAccessible(true); // Делаем поле доступным, если оно приватное
            return field;
        } catch (final NoSuchFieldException e) {
            // Если поле не найдено, пробуем у родителя
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Не удалось получить доступ к полю " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Поле " + cls.getName() + "." + fieldName + " не найдено");
}

Объяснение:

  • Метод getField принимает два параметра: класс cls и строку fieldName, которая представляет собой имя поля, которое нужно найти.
  • Мы начинаем с текущего класса и перебираем все его суперклассы с помощью цикла.
  • В блоке try мы пытаемся получить доступ к полю с указанным именем. Если поле найдено, устанавливаем его доступность через setAccessible(true), чтобы иметь возможность работать с приватными полями, и возвращаем его.
  • Если поле не найдено (NoSuchFieldException), то продолжаем поиск в суперклассе.
  • Если произошла какая-то другая ошибка, выбрасываем IllegalArgumentException с детальным сообщением об ошибке.
  • Если после перебора всех суперклассов поле так и не было найдено, также выбрасываем IllegalArgumentException с соответствующим сообщением.

Этот подход позволяет гибко работать с полями, даже если они объявлены в родительских классах.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь