【发布时间】:2021-06-24 16:57:30
【问题描述】:
在 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 版本(还不能使用它)中,Google 提供了一个 HandlerCompat 方法,该方法使用反射来调用私有方法(如果它不是公共的)。所以我现在将谷歌的方法复制到我自己的 HandlerCompatP 类中,但我注意到如果我调用它 1000 次,那么反射查找将发生 1000 次(我看不到任何缓存)。所以这让我开始思考是否有一种好方法可以只执行一次反射,并且只在需要时执行。
“不要使用反射”在这里不是一个答案,因为在这种情况下它是必需的,谷歌自己打算在他们的兼容性库中发生它。我的问题也不是使用反射是否安全和/或良好的做法,我很清楚这通常不好,而是考虑到我是否 am 使用反射,哪种方法是安全的/更好。
【问题讨论】:
-
为什么?你想达到什么目的?听起来像是一个 XY 问题...
-
您是否尝试仅分配方法
foo()的参考?或者您希望其他线程存储对 SomeThng 类中其他方法的引用? -
第二种方法更好 - 但不要吃异常 - 将异常包装在一些
Error/RuntimeException中,然后重新抛出它。不确定 android 是如何工作的,但是 OpenJDK 的 JIT 可以不断折叠static final字段。 -
第一个被破坏了,因为没有线程安全构造,
sFooMethod == null可以看到由不同线程进行的写入,评估为false,然后是随后的sFooMethod.invoke(thing);not看到写入并导致NullPointerException。这并没有说明Method的内部状态。您应该遵循 Johannes Kuhn 的建议并使用第二个,但不要吞下异常。如果您不确保始终且最多分配一次最终字段,则无论如何都不会编译。请记住,不能使用可变的setAccessible。 -
@pallgeuer 这是两个没有线程安全的独立读取。他们每个人都可能感知到并发写入。所以是的,第一次读取可能会感知到写入,而第二次则不会。这不是关于“忘记”,因为阅读不记得以前的阅读。请参阅shipilev.net/blog/2014/safe-public-construction/…
UnsafeDCLFactory示例及其下方的第 2 点。
标签: java methods reflection static singleton