Dosen’t Reflection API打破了数据封装的目的吗?

最近我遇到了Reflection API,令我惊讶的是我们可以访问甚至更改私有变量。我尝试了以下代码

import java.lang.reflect.Field; public class SomeClass{ private String name = "John"; } public class Test{ public static void main(String args[]) throws Exception { SomeClass myClass = new SomeClass(); Field fs = myClass.getClass().getDeclaredField("name"); fs.setAccessible(true); System.out.println("Variable is " + fs.getName() + " and value is " + fs.get(myClass)); fs.set(myClass, "Sam"); System.out.println("Variable is " + fs.getName() + " and value is " + fs.get(myClass)); } } 

我得到了以下输出。

 Variable is name and value is John Variable is name and value is Sam 

我们说Java是面向对象的语言,它的主要特性是数据封装,inheritance,多态……等不是reflectionAPI改变了数据封装的目的吗? 为什么我们必须使用Reflection API? 我在一些网站上读到它可以用于测试目的,但据我说,模块已经过测试,可以使用JUnit测试用例轻松完成。 所以任何人都可以解释为什么我们有这样的黑客攻击?

reflectionAPI是不是改变了数据封装的目的?

是的,不是。

  • 是的,reflectionAPI的某些用法可能会破坏数据封装。
  • 不,并非所有reflectionAPI的使用都会破坏数据封装。 实际上,一个明智的程序员只有在有充分理由这样做的时候才能通过reflectionAPI打破封装。
  • 不,reflectionAPI不会改变数据封装的目的 。 数据封装的目的保持不变……即使有人故意破坏它。

为什么我们必须使用Reflection API?

reflection的许多用途都不会破坏封装; 例如,使用reflection来找出类的超类型,它有什么注释,它有什么成员,调用可访问的方法和构造函数,读取和更新可访问的字段等等。

并且有些情况可以接受(在不同程度上)使用封装破坏各种reflection:

  • 您可能需要查看封装类型(例如,访问/修改私有字段)作为实现某些unit testing的最简单方法(或唯一方式)。

  • 某些forms的dependency injection(也称为IoC),序列化和持久性需要访问和/或更新私有字段。

  • 偶尔,您需要打破封装以解决某些无法修复的类中的错误。

我在一些网站上读到它可以用于测试目的,但据我说,模块已经过测试,可以使用JUnit测试用例轻松完成。 所以任何人都可以解释为什么我们有这样的黑客攻击?

这取决于你class级的设计。 设计为可测试的类将是可测试的,而不需要访问“私有”状态,或者将暴露该状态(例如protected getter)以允许测试。 如果类没有这样做,那么JUnit测试可能需要使用reflection来查看抽象内部。

这是不可取的(IMO),但如果您正在为某人编写的类编写unit testing,并且您无法“调整”API以提高可测试性,那么您可能必须在使用reflection或根本不进行测试之间进行选择。


最重要的是,数据封装是我们努力实现的理想(在Java中),但有些情况下,实际上正确的做法是打破它或忽略它。

请注意,并非所有OO语言都支持像Java那样强大的数据封装。 例如,Python和Javascript都是无可争议的OO语言,但它们都使一个类可以轻松访问和修改另一个类的对象状态……甚至可以更改其他类的行为。 强大的数据抽象并不是每个人对面向对象意味着什么的看法的核心。

是的,它确实违反了OO概念。 但是它不会破坏任何Java安全模型。 如有必要,它可以由java安全管理器控制。 Javareflection本身是一件有用的事情。 它用在Annotations和IOC中,它们是非常有用的概念,能够在运行时处理类。

你不能将EncapsulationPolymorphism与reflection混合在一起。

reflection具有完全不同的目的。使用reflection,您可以动态创建类型并执行它。您可以在运行时动态地访问类的成员。


例如,最近我在我的应用程序中使用了reflection来运行Plugins 。我从特定目录加载了dll,然后通过reflection将其转换为共享接口来创建类的对象。

reflectionAPI是不是改变了数据封装的目的?

不要太依赖规则。 有时候打破它们是有用的。

Reflextion非常有用。 以我的代码为例来记录查找更改,以便我可以将它们记录到数据库中:

 public static void log(String userID, Object bOld, Object bNew) throws IntrospectionException, IllegalAccessException, InvocationTargetException { String res = "";//String to hold the change record boolean changed = false; try { if(bOld != null){ //if this is an update BeanInfo beanInfo = Introspector.getBeanInfo(bOld.getClass()); res = bOld.getClass().getSimpleName() + " - "; //loop and compare old values with new values and add them to our string if they are changed for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) { Method getter = prop.getReadMethod(); Object vOld = getter.invoke(bOld); //old value Object vNew = getter.invoke(bNew); //new value if (vOld == vNew || (vOld != null && vOld.equals(vNew))) { continue; } changed = true; res = res + "(" + prop.getName() + ", " + vOld + ", " + vNew + ")"; } } 

这样做要容易得多。 如果我使用getter,我将不得不为每个类编写一个单独的方法,并在每次添加新字段时进行修改。 通过reflection,我可以编写一个方法来处理我的所有类。 我在这里写了关于记录更改的方法