Je sais que nous pouvons accéder à un constructeur privé via une réflexion sous la forme @Sanjay T. Sharma mentionné dans sa réponse à ma question: “instanceof Void” renvoie-t-il toujours la valeur false?
vous pouvez accéder à tout ce qui est privé avec réflexion: méthodes, constructeurs, membres de données, tout.
1)Comment puis-je accéder aux méthodes privées et aux membres de données privées?
Vous pouvez le faire avec un peu d'aide de la méthode setAccessible(true)
:
class Dummy{
private void foo(){
System.out.println("hello foo()");
}
private int i = 10;
}
class Test{
public static void main(String[] args) throws Exception {
Dummy d = new Dummy();
/*--- [INVOKING PRIVATE METHOD] ---*/
Method m = Dummy.class.getDeclaredMethod("foo");
//m.invoke(d); // Exception Java.lang.IllegalAccessException
m.setAccessible(true);//Abracadabra
m.invoke(d); // Now it's OK
/*--- [GETING VALUE FROM PRIVATE FIELD] ---*/
Field f = Dummy.class.getDeclaredField("i");
//System.out.println(f.get(d)); // Not accessible now
f.setAccessible(true); // Abracadabra
System.out.println(f.get(d)); // Now it's OK
/*--- [SETTING VALUE OF PRIVATE FIELD] ---*/
Field f2 = Dummy.class.getDeclaredField("i");
//f2.set(d,20); // Not accessible now
f2.setAccessible(true); // Abracadabra
f2.set(d, 20); // Now it's OK
System.out.println(f2.get(d));
}
}
2)Est-il possible d'accéder à une variable locale via réflexion?
Les variables locales ne sont pas accessibles en dehors du bloc dans lequel elles ont été créées (quelqu'un pourrait dire que vous pouvez affecter une telle variable à un champ tel que field = localVariable;
et accéder ultérieurement à un tel champ par réflexion, mais nous accéderons ainsi à lavaleur, pas le variable).
3)Y a-t-il un moyen d'empêcher qui que ce soit d'accéder aux constructeurs, méthodes et membres de données privés?
Je pense que pour constructors
ou methods
, vous pouvez utiliser stacktrace pour vérifier si elle a été invoquée par Reflection
.
Pour les champs, je ne trouve pas de solution pour empêcher leur accès via la réflexion.
class Dummy {
private void safeMethod() {
StackTraceElement[] st = new Exception().getStackTrace();
// If a method was invoked by reflection, the stack trace would be similar
// to something like this:
/*
Java.lang.Exception
at package1.b.Dummy.safeMethod(SomeClass.Java:38)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
-> at Java.lang.reflect.Method.invoke(Method.Java:601)
at package1.b.Test.main(SomeClass.Java:65)
*/
//5th line marked by "->" is interesting one so I will try to use that info
if (st.length > 5 &&
st[4].getClassName().equals("Java.lang.reflect.Method"))
throw new RuntimeException("safeMethod() is accessible only by Dummy object");
// Now normal code of method
System.out.println("code of safe method");
}
// I will check if it is possible to normally use that method inside this class
public void trySafeMethod(){
safeMethod();
}
Dummy() {
safeMethod();
}
}
class Dummy1 extends Dummy {}
class Test {
public static void main(String[] args) throws Exception {
Dummy1 d1 = new Dummy1(); // safeMethod can be invoked inside a superclass constructor
d1.trySafeMethod(); // safeMethod can be invoked inside other Dummy class methods
System.out.println("-------------------");
// Let's check if it is possible to invoke it via reflection
Method m2 = Dummy.class.getDeclaredMethod("safeMethod");
// m.invoke(d);//exception Java.lang.IllegalAccessException
m2.setAccessible(true);
m2.invoke(d1);
}
}
Sortie de la méthode principale Test
:
code of safe method
code of safe method
-------------------
Exception in thread "main" Java.lang.reflect.InvocationTargetException
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.lang.reflect.Method.invoke(Method.Java:601)
at package1.b.Test.main(MyClass2.Java:87)
Caused by: Java.lang.RuntimeException: method safeMethod() is accessible only by Dummy object
at package1.b.Dummy.safeMethod(MyClass2.Java:54)
... 5 more
Pour accéderun champ privé vous devrez appeler la méthode Class.getDeclaredField(String name)
ou enter code here
. Cochez ce code simple:
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Field privateStringField = PrivateObject.class.
getDeclaredField("privateString");
privateStringField.setAccessible(true);
String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue
Pour accéder à une méthode privée, vous devrez appeler la méthode Class.getDeclaredMethod (nom de chaîne, Class [] paramètreTypes) ou Class.getDeclaredMethods ().
Cochez ce code simple: _
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
private String getPrivateString(){
return this.privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Method privateStringMethod = PrivateObject.class.
getDeclaredMethod("getPrivateString", null);
privateStringMethod.setAccessible(true);
String returnValue = (String)
privateStringMethod.invoke(privateObject, null);
System.out.println("returnValue = " + returnValue);
Lisez les détails sur http://tutorials.jenkov.com/Java-reflection/private-fields-and-methods.html
Exemple comme ci-dessous:
import Java.lang.reflect.Constructor;
import Java.lang.reflect.Field;
import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;
class Test
{
private int a = 5; // Private data member
private void call(int n) // Private method
{
System.out.println("in call() n: " + n);
}
}
public class Sample
{
public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
{
Class c = Class.forName("Test");
Object obj = c.newInstance();
//---- Accessing a private method
Method m=c.getDeclaredMethod("call",new Class[]{int.class});
m.setAccessible(true);
m.invoke(obj,7);
//---- Accessing a private data member
Field d = c.getDeclaredField("a");
d.setAccessible(true);
System.out.println(d.getInt(obj));
}
}
Area s=(Area)c.newInstance();
s.setRadius(10);
System.out.println("Area: "+s.calculateArea(4));
Method m[]=c.getDeclaredMethods();
Constructor c1[]=c.getConstructors();
for(int i=0;i<m.length;i++)
System.out.println(""+m[i]);
for(int i=0;i<c1.length;i++)
System.out.println(""+c1[i]);
Pour répondre à votre troisième question:
Réponse:
Oui, vous pouvez restreindre l'accès (vous pouvez générer une exception lorsque quelqu'un tente d'accéder à votre constructeur/méthode/données privé)
Reportez-vous à l'exemple ci-dessous:
******JavaSingleton Class******
package server;
public class JavaSingleton {
private static final JavaSingleton INSTANCE = new JavaSingleton();
private JavaSingleton() {
if (INSTANCE != null) {
throw new IllegalStateException("Inside JavaSingleton(): JavaSingleton " +
"instance already created.");
}
System.out.println("Inside JavaSingleton(): Singleton instance is being created.");
}
public static final JavaSingleton getInstance() {
return INSTANCE;
}
}
***Listing 2: JavaSingleton client***
import server.JavaSingleton;
import Java.lang.reflect.*;
public class TestSingleton {
public static void main(String[] args) throws ReflectiveOperationException {
System.out.println("Inside main(): Getting the singleton instance using getInstance()...");
JavaSingleton s = JavaSingleton.getInstance();
System.out.println("Inside main(): Trying to use reflection to get another instance...");
Class<JavaSingleton> clazz = JavaSingleton.class;
Constructor<JavaSingleton> cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
JavaSingleton s2 = cons.newInstance();
}
}
Output:
C:\singleton>Java TestSingleton
Inside main(): Getting the singleton instance using getInstance()...
Inside JavaSingleton(): Singleton instance is being created.
Inside main(): Trying to use reflection to get another instance...
Exception in thread "main" Java.lang.reflect.InvocationTargetException
at Sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at Sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at Sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at Java.lang.reflect.Constructor.newInstance(Unknown Source)
at TestSingleton.main(TestSingleton.Java:13)
Caused by: Java.lang.IllegalStateException: Inside JavaSingleton(): JavaSingleton instance already created.
at server.JavaSingleton.<init>(JavaSingleton.Java:7)
... 5 more
Cet exemple concernait une classe singleton (enregistrement du constructeur), mais vous pouvez toujours implémenter cette logique pour les méthodes privées que vous souhaitez empêcher l'accès à partir d'autres classes.
Dans ce cas, vous déclarerez également une instance statique, en vérifier la valeur dans la méthode privée et émettez une erreur en cas de valeur non souhaitée.