我完全困惑了——我用Java8SE写了一个类(不到10行代码),它完全折衷了使用反射的受限(私有)方法。我发现这是非常危险的,并质疑允许这样做的反思意识。
package reflection;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Reflection {
// Throw 'Throwable' because this method must be totally transparent, doing exactly what the actually called method does:
static public Object call(Object target, String methodName, Object... args) throws Throwable {
// Get the arguments' classes:
Class<?>[] argumentClasses =
Arrays.asList(args)
.stream()
.map(object -> object.getClass())
.toArray(Class[]::new);
Method method = target.getClass().getDeclaredMethod(methodName, argumentClasses);
method.setAccessible(true);
return method.invoke(target, args);
}
}
您可以运行这个测试,它调用另一个类Action:的私有方法,包括静态和非静态方法
package reflection;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class ReflectionUnitTest {
@Test
public void testCall() throws Throwable {
Action target = new Action();
assertEquals("Something static done!", Reflection.call(target, "doSomethingStatic"));
assertEquals("Something static done with something else!", Reflection.call(target, "doSomethingStatic", "something else"));
assertEquals("Something static done 3 times with something else!", Reflection.call(target, "doSomethingStatic", 3, "something else"));
assertEquals("Something static done 5 times with something else!", Reflection.call(target, "doSomethingStatic", "something else", 5));
assertEquals("Something done!", Reflection.call(target, "doSomething"));
assertEquals("Something done with something else!", Reflection.call(target, "doSomething", "something else"));
assertEquals("Something done 3 times with something else!", Reflection.call(target, "doSomething", 3, "something else"));
assertEquals("Something done 5 times with something else!", Reflection.call(target, "doSomething", "something else", 5));
}
}
package reflection;
public class Action {
static private String doSomethingStatic(){
return "Something static done!";
}
private String doSomethingStatic(String argument) {
return "Something static done with " + argument + "!";
}
private String doSomethingStatic(Integer count, String argument) {
return "Something static done " + count + " times with " + argument + "!";
}
private String doSomethingStatic(String argument, Integer count) {
return "Something static done " + count + " times with " + argument + "!";
}
private String doSomething() {
return "Something done!";
}
private String doSomething(String argument) {
return "Something done with " + argument + "!";
}
private String doSomething(Integer count, String argument) {
return "Something done " + count + " times with " + argument + "!";
}
private String doSomething(String argument, Integer count) {
return "Something done " + count + " times with " + argument + "!";
}
}
我的问题:
- 与您分享这些知识
- 询问是否有人能解释为什么这是可能的
问题不是反射。
问题在于你的假设/期望。
让我解释一下:除非你的应用程序在你自己的物理硬件上运行,否则其他人也无法访问;除非您控制运行应用程序的系统上代码加载的所有方面。。。无论如何,控制是一种幻觉。
如果你的类运行在一个不属于你的JVM上。。。人们可以(几乎)做任何他们想做的事。这只是一个努力的问题。
含义:Java语言之父在某种程度上实现了反思;不管好坏,他们决定允许你覆盖这种保护(如果没有SecurityManager的话)。但这并不是什么新鲜事,从一开始就是这样。
你说你觉得它很危险。。。为什么?如果你调用了一个私有代码,它就变成了BOOM,那么你的程序就会崩溃。调用方代码是必须处理异常的代码。。。那么,如果不是为了安全,为什么private
存在呢?
它们的存在是为了表达意图。它们的存在是为了告诉代码的用户他们应该使用哪些方法,哪些方法只有在子类化时才应该使用,哪些方法根本不应该使用,因为这样做会导致问题。
原始代码的开发人员不希望另一个开发人员直接调用私有方法,因为这样做可能会跳过一些验证,并给出错误的结果。
或者你不能直接改变一个变量,因为它也必须改变另一个。。。但如果另一个变量正是给你带来麻烦的变量,那么直接通过反射设置它将跳过问题。。。
因此,通过反射调用private并不是为了破坏安全措施,而是为了解决糟糕的设计决策(可能在private
部分,设计应该允许直接调用,也可能在反射部分,你不应该调用private方法,因为这会使你的代码行为不正确)。