it-swarm.com.de

Wie liest man den Wert eines privaten Feldes aus einer anderen Klasse in Java?

Ich habe eine schlecht gestaltete Klasse in einer JAR eines Drittanbieters und ich muss auf eines ihrer private -Felder zugreifen. Zum Beispiel Warum sollte ich ein privates Feld auswählen müssen?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

Wie kann ich Reflection verwenden, um den Wert von stuffIWant zu erhalten?

424
Frank Krueger

Um auf private Felder zugreifen zu können, müssen Sie sie aus den deklarierten -Feldern der Klasse abrufen und dann zugänglich machen:

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

EDIT: Wie von aperkins kommentiert, werden beide, die auf das Feld zugreifen, es als zugänglich definieren und den Wert abrufen, alle Exceptions werfen, obwohl die einzigen checked -Ausnahmen, die Sie sein müssen achtsam sind oben kommentiert.

Die Variable NoSuchFieldException würde ausgelöst, wenn Sie nach einem Feld mit einem Namen fragen, der nicht mit einem deklarierten Feld übereinstimmt. 

obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException

Die Variable IllegalAccessException würde ausgelöst, wenn das Feld nicht verfügbar war (z. B. wenn es privat ist und nicht über die Zeile f.setAccessible(true) erreichbar gemacht wurde).

Die RuntimeExceptions, die ausgelöst werden können, sind entweder SecurityExceptions (wenn die JVM-Variable SecurityManager Sie nicht zulässt, die Eingabehilfen eines Felds zu ändern) oder IllegalArgumentExceptions, wenn Sie versuchen, auf das Feld eines Objekts zuzugreifen, das nicht dem Typ des Felds entspricht:

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
592
oxbow_lakes

Versuchen Sie FieldUtils aus Apache commons-lang3:

FieldUtils.readField(object, fieldName, true);
124
yegor256

Reflexion ist nicht die einzige Möglichkeit, Ihr Problem zu lösen (dh, auf die privaten Funktionen/das Verhalten einer Klasse/Komponente zuzugreifen).

Eine alternative Lösung besteht darin, die Klasse aus der .jar-Datei zu extrahieren, mit (say) Jode oder Jad zu dekompilieren, das Feld zu ändern (oder einen Accessor hinzuzufügen) und mit der ursprünglichen .jar-Datei erneut zu kompilieren. Fügen Sie dann die neue Klasse vor .jar in den Klassenpfad ein oder fügen Sie sie erneut in .jar ein. (Das jar-Dienstprogramm ermöglicht das Extrahieren und Wiedereinsetzen in eine vorhandene .jar-Datei.)

Wie weiter unten erwähnt, löst dies das umfassendere Problem des Zugriffs auf/Ändern des privaten Zustands und nicht nur des Zugriffs auf ein Feld.

Dazu muss natürlich der .jar nicht signiert sein.

24
Brian Agnew

Eine andere Option, die noch nicht erwähnt wurde: use Groovy . Groovy ermöglicht den Zugriff auf private Instanzvariablen als Nebeneffekt des Sprachdesigns. Ob Sie einen Getter für das Feld haben oder nicht, können Sie einfach verwenden 

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

Mit Hilfe von Reflection in Java können Sie auf alle private/public-Felder und -Methoden einer Klasse auf eine andere zugreifen. Wie auch immer in der OracleDokumentation im Abschnitt backbacks wird empfohlen: 

"Da Reflexion Code für das Durchführen von Operationen zulässt, die in nicht reflektierendem Code unzulässig sind, wie z. B. beim Zugriff auf private Felder und Methoden, kann die Verwendung von Reflektionen zu unerwarteten Nebeneffekten führen, die dazu führen, dass Code funktionsunfähig wird und die Portabilität zerstört Reflektierender Code unterbricht Abstraktionen und kann daher das Verhalten bei Upgrades der Plattform ändern "

hier sind die folgenden Code-Ausschnitte, um grundlegende Konzepte von Reflection zu demonstrieren. 

Reflection1.Java

public class Reflection1{

    private int i = 10;

    public void methoda()
    {

        System.out.println("method1");
    }
    public void methodb()
    {

        System.out.println("method2");
    }
    public void methodc()
    {

        System.out.println("method3");
    }

}

Reflection2.Java

import Java.lang.reflect.Field;
import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;


public class Reflection2{

    public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Method[] mthd = Reflection1.class.getMethods(); // for axis the methods 

        Field[] fld = Reflection1.class.getDeclaredFields();  // for axis the fields  

        // Loop for get all the methods in class
        for(Method mthd1:mthd)
        {

            System.out.println("method :"+mthd1.getName());
            System.out.println("parametes :"+mthd1.getReturnType());
        }

        // Loop for get all the Field in class
        for(Field fld1:fld)
        {
            fld1.setAccessible(true);
            System.out.println("field :"+fld1.getName());
            System.out.println("type :"+fld1.getType());
            System.out.println("value :"+fld1.getInt(new Reflaction1()));
        }
    }

}

Ich hoffe es wird helfen.

7
Simmant

Wie von oxbow_lakes erwähnt, können Sie Reflection verwenden, um die Zugriffsbeschränkungen zu umgehen (vorausgesetzt, Ihr SecurityManager wird Sie zulassen).

Wenn diese Klasse jedoch so schlecht gestaltet ist, dass Sie dazu gezwungen werden, zu so viel Hacker zu greifen, sollten Sie vielleicht nach einer Alternative suchen. Sicher, dieser kleine Hacker spart Ihnen jetzt ein paar Stunden, aber wie viel kostet es Sie die Straße hinunter?

5

Verwenden Sie das Soot Java Optimization-Framework, um den Bytecode direkt zu ändern http://www.sable.mcgill.ca/soot/

Ruß ist vollständig in Java geschrieben und arbeitet mit neuen Java-Versionen.

4
pcpratts

Sie müssen Folgendes tun:

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) {
            // Try parent
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Cannot access field " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Cannot find field " + cls.getName() + "." + fieldName);
}
2
Luke Hutchison

Wenn Sie Spring verwenden, bietet ReflectionTestUtils einige nützliche Tools, die hier mit minimalem Aufwand helfen. Es wird als "zur Verwendung in Unit- und Integrationstestszenarien" beschrieben. Es gibt auch eine ähnliche Klasse mit dem Namen ReflectionUtils , die jedoch als "Nur für den internen Gebrauch" beschrieben wird - siehe diese Antwort für eine Interpretation dessen, was dies bedeutet.

So adressieren Sie das veröffentlichte Beispiel:

Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
1
Steve Chambers

Noch ein Hinweis zur Reflektion: Ich habe in einigen speziellen Fällen festgestellt, dass mehrere Reflektoren mit demselben Namen in verschiedenen Paketen vorhanden sind. Diese Reflektion, wie sie in der obersten Antwort verwendet wird, kann möglicherweise nicht die richtige Klasse aus dem Objekt auswählen. Wenn Sie also wissen, was die package.class des Objekts ist, ist es besser, auf die privaten Feldwerte wie folgt zuzugreifen:

org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);

(Dies ist die Beispielklasse, die für mich nicht funktionierte.)

1
xtof54

Sie können Manifolds@JailBreak für eine direkte, typsichere Java-Reflektion verwenden:

@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;

public class Foo {
    private String stuffIWant;
}

@JailBreak gibt die lokale Variable foo im Compiler für den direkten Zugriff auf alle Member in der Hierarchie von Foo frei.

Ebenso können Sie die Erweiterungsmethode jailbreak () zur einmaligen Verwendung verwenden:

foo.jailbreak().stuffIWant = "123";

Über die jailbreak()-Methode können Sie auf jedes Mitglied in der Hierarchie von Foo zugreifen.

In beiden Fällen löst der Compiler den Feldzugriff für Sie typsicher auf, als ob ein öffentliches Feld, während Manifold unter der Haube einen effizienten Reflexionscode für Sie generiert.

Erfahren Sie mehr über Manifold .

0
Scott McKinney