Как получить доступ к частным методам и частным данным через отражение?
Я знаю, что мы можем получить доступ к частному конструктору через отражение как @Sanjay T. Sharma, упомянутый в его ответе на мой вопрос: Всегда ли "instanceof Void" возвращает false?
Однако @duffymo сказал:
вы можете получить доступ к частному все с помощью отражений - методов, конструкторов, элементов данных, всего.
- Как я могу получить доступ к частным методам и частным данным?
- Можно ли получить доступ к локальной переменной через отражение?
- Есть ли способ предотвратить доступ любого пользователя к частным конструкторам, методам и членам данных?
Ответы
Ответ 1
1) Как я могу получить доступ к частным методам и частным элементам данных?
Вы можете сделать это с небольшой помощью метода 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 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 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 OK
System.out.println(f2.get(d));
}
}
2) Можно ли получить доступ к локальной переменной через отражение?
Нет. Локальные переменные не могут быть доступны за пределами блока, в котором они были созданы (кто-то может сказать, что вы можете назначить такую переменную для поля типа field = localVariable;
и позже получить доступ к такому полю через отражение, но таким образом мы будем получать доступ к значению, а не переменная).
3) Есть ли способ предотвратить доступ любого пользователя к частным конструкторам, методам и элементам данных?
Я думаю, что для constructors
или methods
вы можете использовать stacktrace, чтобы проверить, был ли он вызван Reflection
.
Для полей я не могу найти решение для предотвращения доступа к ним через отражение.
[ПРЕДУПРЕЖДЕНИЕ: Это не одобрено никем. Я просто написал его, вдохновленный вашим вопросом.]
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 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);
}
}
Вывод из основного метода 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
Ответ 2
- Используя метод, указанный в ответе, который вы связали с: setAccessible (true), который является методом суперкласса поля, конструктора и метода.
- Нет.
- Нет, если код не запущен в JVM, который вы контролируете, где вы устанавливаете менеджера безопасности. Но если вы дадите кому-нибудь файл jar, и он использует классы из этого файла jar, он сможет получить доступ ко всему.
Ответ 3
Чтобы получить доступ к частному полю, вам нужно будет позвонить
Class.getDeclaredField(String name)
или enter code here
.
Проверьте этот простой код:
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
Чтобы получить доступ к закрытому методу, вам нужно будет вызвать Class.getDeclaredMethod(String name, Class [] parameterTypes) или метод Class.getDeclaredMethods().
Проверьте этот простой код:
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);
Подробнее прочитайте в
http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html
Ответ 4
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]);
Ответ 5
Пример, как показано ниже:
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));
}
}
Ответ 6
Чтобы ответить на третий вопрос:
- Есть ли способ предотвратить доступ любого пользователя к частным конструкторам, методам и членам данных?
Ответ:
Да, вы можете ограничить доступ (вы можете выбросить исключение, когда кто-то пытается получить доступ к вашему частному конструктору/методу/данным)
Обратитесь к приведенному ниже примеру:
******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
Этот пример был для одноэлементного класса (проверка в конструкторе), но вы все равно можете реализовать эту логику для частных методов, которые вы хотите запретить доступ из других классов.
В этом случае вы также объявите статический экземпляр и проверите его значение в приватном методе и выбросите ошибку в случае любого нежелательного значения.