Java存储在类中静态地反映了方法:安全



在Java中类似以下"安全"的东西吗?为什么?

public final class Utility {
private Utility() {}
private static Method sFooMethod = null;
public static void callFoo(SomeThing thing) {
try {
if(sFooMethod == null)
sFooMethod = SomeThing.class.getMethod("foo");
sFooMethod.invoke(thing);
} catch(Exception e) {}  // Just for simplicity here
}
}

我的理由是,即使另一个线程在后台写入sFooMethod,而当前线程在执行callFoo()的过程中突然在某个地方看到了它,它仍然会导致thing.foo()的旧反射调用?

额外问题:以下方法与上述方法在哪些方面不同(积极/消极(?会更可取吗?

public final class Utility {
private Utility() {}
private static final Method sFooMethod;
static {
try {
sFooMethod = SomeThing.class.getMethod("foo");
} catch(Exception e) {}
}
public static void callFoo(SomeThing thing) {
try {
if(sFooMethod != null)
sFooMethod.invoke(thing);
} catch(Exception e) {}
}
}

来自评论的后台更新:我正在编写一个Android应用程序,我需要调用一个在API 29之前是私有的方法,当它被公开而没有被更改时。在AndroidX核心库的alpha版本(目前还不能使用(中,谷歌提供了一个HandlerCompat方法,如果它不是公共的,它会使用反射来调用私有方法。因此,我暂时将谷歌的方法复制到我自己的HandlerCompatP类中,但我注意到,如果我调用它1000次,那么反射查找将发生1000次(我看不到任何缓存(。这让我思考是否有一种好的方法只进行一次反思,而且只有在需要的时候。

"不要使用反射"在这里并不是一个答案,因为在这种情况下,这是必需的,谷歌自己也打算在他们的兼容性库中实现这一点。我的问题也不是使用反射是否安全和/或良好的做法,我很清楚这通常不好,而是考虑到我正在使用反射,哪种方法会安全/更好。

避免内存一致性错误的关键是理解先发生后发生的关系。这种关系只是保证一个特定语句所写的内存对另一个特定的语句可见。
Java语言规范规定如下:
17.4.5。订单前发生

两个操作可以按先发生后发生的关系排序。如果有动作发生在另一个动作之前,然后第一个动作对和可见在第二次之前订购。

如果我们有两个动作x和y,我们写hb(x,y(来表示x发生在y.之前

如果x和y是同一线程的操作,并且x在中位于y之前程序顺序,然后是hb(x,y(。

在您的情况下,对静态字段的写入和从静态字段的读取是在同一步骤中进行的。因此,"先发生后发生"的关系成立了。因此,读取操作将始终看到写入操作的效果。
此外,所有线程都将写入相同的数据。更糟糕的是,所有符合条件的线程都将同时写入变量。变量将引用最后一个被分配的对象,其余被取消引用的对象将被垃圾回收。

你的应用程序中不会有太多线程同时进入相同的方法,这将由于大量的对象创建而导致显著的性能打击。但如果您只想设置一次变量,那么第二种方法会更好。因为静态块是线程安全的。

在Java中,以下内容是安全的吗?为什么?

  1. 不,除非迫不得已,否则我不建议使用反射
  2. 大多数情况下,开发人员都会以某种方式设计类,这样就不需要访问隐藏的字段或方法。很可能会有更好的方法来访问隐藏的内容
  3. 特别是隐藏的字段和方法,当它们所包含的库更新时,可能会更改它们的名称。所以你的代码可能会突然停止工作,你不知道为什么,因为编译器不会输出任何错误
  4. 直接访问方法或字段也比通过反射更快,因为反射首先需要搜索它,而直接访问不需要

所以如果你不需要,就不要使用反射

我不确定你的目标是什么——可能有更好的方法来做你想做的事情。

第二种方法是使用静态初始值设定项,这是更可取的,因为您的第一个实现具有竞争条件。

最新更新