我完全感到困惑 - 我在Java8 SE中编写了一个类(少于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);
}
}
您可以运行此测试,它调用另一个类的私有方法(静态和非静态),操作:
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 + "!";
}
}
我的问题:
答案 0 :(得分:0)
问题不在于反思。
问题在于您的假设/期望。
让我解释一下:除非您的应用程序在您自己的物理硬件上运行,否则其他任何人都无法访问;除非你控制运行你的应用程序的系统上的代码加载的所有方面...无论如何,控制是幻觉。
如果你的课程在一个不属于你的JVM上运行......人们可以(几乎)做任何他们想做的事情。这只是努力的问题。
含义:Java语言的父亲在某些时候能够反思;无论好坏,他们决定允许你覆盖这种保护(如果没有安全管理器)。但这并不是什么新鲜事,从一开始就是这样。
答案 1 :(得分:0)
你说你发现它很危险......为什么?如果你调用一个私有代码并且它是BOOM,那么你的程序将崩溃。调用者代码是必须处理异常的代码...因此,如果不是为了安全性,为什么private
存在?
它们的存在是为了表达意图。它们的存在是为了告诉代码的用户他们应该使用哪些方法,他们应该在子类化时使用哪些方法以及根本不应该使用的方法,因为这样做会导致问题。
原始代码的开发人员不希望其他开发人员直接调用私有方法,因为这样做可能会跳过一些验证并产生错误的结果。
或者也许你不能直接改变一个变量,因为它也必须改变另一个...但如果正是那个给你带来麻烦的变量,直接通过反射设置它将跳过问题......
因此,通过反思来呼叫私有不是为了取消安全措施,而是为了制定糟糕的设计决策(可能在private
部分,设计本应允许直接呼叫,或者可能在反射部分,你不应该调用私有方法,因为这会使你的代码行为不正确)。