使用反射完全破坏普通java中对方法的限制访问 - 为什么这可能?

时间:2016-07-21 09:28:59

标签: java reflection

我完全感到困惑 - 我在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 + "!";
    }

}

我的问题:

  1. 与您分享这些知识
  2. 询问是否有人可以解释为什么这是可能的

2 个答案:

答案 0 :(得分:0)

问题不在于反思。

问题在于您的假设/期望。

让我解释一下:除非您的应用程序在您自己的物理硬件上运行,否则其他任何人都无法访问;除非你控制运行你的应用程序的系统上的代码加载的所有方面...无论如何,控制是幻觉

如果你的课程在一个不属于你的JVM上运行......人们可以(几乎)做任何他们想做的事情。这只是努力的问题。

含义:Java语言的父亲在某些时候能够反思;无论好坏,他们决定允许你覆盖这种保护(如果没有安全管理器)。但这并不是什么新鲜事,从一开始就是这样。

答案 1 :(得分:0)

你说你发现它很危险......为什么?如果你调用一个私有代码并且它是BOOM,那么你的程序将崩溃。调用者代码是必须处理异常的代码...因此,如果不是为了安全性,为什么private存在?

它们的存在是为了表达意图。它们的存在是为了告诉代码的用户他们应该使用哪些方法,他们应该在子类化时使用哪些方法以及根本不应该使用的方法,因为这样做会导致问题。

原始代码的开发人员不希望其他开发人员直接调用私有方法,因为这样做可能会跳过一些验证并产生错误的结果。

或者也许你不能直接改变一个变量,因为它也必须改变另一个...但如果正是那个给你带来麻烦的变量,直接通过反射设置它将跳过问题......

因此,通过反思来呼叫私有不是为了取消安全措施,而是为了制定糟糕的设计决策(可能在private部分,设计本应允许直接呼叫,或者可能在反射部分,你不应该调用私有方法,因为这会使你的代码行为不正确)。