Java 将反映的方法静态存储在 class 中:安全吗?
Java store reflected Method statically in class: Safe?
Java中是否有类似下面的'safe',为什么?
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()
?
的反射调用
附加问题:下面的方法与上面的方法有什么不同(positive/negative)?会是首选吗?
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,当它被制作 public 时没有被改变。在 AndroidX 核心库 Google 的 alpha 版本(还不能使用它)中提供了一个 HandlerCompat 方法,如果它不是 public,则使用反射调用私有方法。所以我暂时将 Google 的方法复制到我自己的 HandlerCompatP class 中,但我注意到如果我调用它 1000 次,那么反射查找将发生 1000 次(我看不到任何缓存).所以这让我开始思考是否有一种只执行一次反射的好方法,并且只在需要时执行。
“不要使用反射”在这里不是答案,因为在这种情况下它是必需的,并且 Google 他们自己打算在他们的兼容性库中实现它。我的问题也不是使用反射是否安全 and/or 好的做法,我很清楚它通常不好,而是考虑到我是否 am 使用反射,这方法将是 safe/better.
Is something like the following 'safe' in Java, and why?
- 不,我不推荐使用反射,除非你必须这样做。
- 大多数时候,开发人员会以某种方式设计他们的 类,以便永远不需要访问隐藏字段或方法。很可能会有更好的方法来访问隐藏内容。
- 尤其是隐藏的字段和方法可以在它们所在的库更新时更改它们的名称。所以你的代码可能会突然停止工作,你不知道为什么,因为编译器不会输出任何错误。
- 直接访问方法或字段比通过反射更快,因为反射首先需要搜索它,而直接访问不需要
因此,如果不需要,请不要使用反射
我不确定你的目标是什么 -- 可能有更好的方法来完成你想做的事情。
第二种方法,使用静态初始化器,是更可取的,因为你的第一个实现有竞争条件。
避免内存一致性错误的关键是理解 happens-before 关系。这种关系只是保证一个特定语句对内存的写入对另一个特定语句可见。
Java 语言规范声明如下:
17.4.5. Happens-before Order
Two actions can be ordered by a happens-before relationship. If one
action happens-before another, then the first is visible to and
ordered before the second.
If we have two actions x and y, we write hb(x, y) to indicate that x
happens-before y.
If x and y are actions of the same thread and x comes before y in
program order, then hb(x, y).
因为,在您的情况下,写入静态字段然后从静态字段读取是在同一个步骤中发生的。因此建立了“发生在之前”的关系。所以读操作总是会看到写操作的效果。
此外,所有线程都将写入相同的数据。更糟糕的是,所有符合条件的线程将同时写入变量。该变量将引用最后分配的对象,其余取消引用的对象将被垃圾收集。
您的 App 中不会有很多线程同时进入同一个方法,这会由于大量对象创建而导致显着的性能下降。但是如果你只想设置变量一次,那么第二种方法更好。作为 static blocks are thread safe.
Java中是否有类似下面的'safe',为什么?
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()
?
附加问题:下面的方法与上面的方法有什么不同(positive/negative)?会是首选吗?
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,当它被制作 public 时没有被改变。在 AndroidX 核心库 Google 的 alpha 版本(还不能使用它)中提供了一个 HandlerCompat 方法,如果它不是 public,则使用反射调用私有方法。所以我暂时将 Google 的方法复制到我自己的 HandlerCompatP class 中,但我注意到如果我调用它 1000 次,那么反射查找将发生 1000 次(我看不到任何缓存).所以这让我开始思考是否有一种只执行一次反射的好方法,并且只在需要时执行。
“不要使用反射”在这里不是答案,因为在这种情况下它是必需的,并且 Google 他们自己打算在他们的兼容性库中实现它。我的问题也不是使用反射是否安全 and/or 好的做法,我很清楚它通常不好,而是考虑到我是否 am 使用反射,这方法将是 safe/better.
Is something like the following 'safe' in Java, and why?
- 不,我不推荐使用反射,除非你必须这样做。
- 大多数时候,开发人员会以某种方式设计他们的 类,以便永远不需要访问隐藏字段或方法。很可能会有更好的方法来访问隐藏内容。
- 尤其是隐藏的字段和方法可以在它们所在的库更新时更改它们的名称。所以你的代码可能会突然停止工作,你不知道为什么,因为编译器不会输出任何错误。
- 直接访问方法或字段比通过反射更快,因为反射首先需要搜索它,而直接访问不需要
因此,如果不需要,请不要使用反射
我不确定你的目标是什么 -- 可能有更好的方法来完成你想做的事情。
第二种方法,使用静态初始化器,是更可取的,因为你的第一个实现有竞争条件。
避免内存一致性错误的关键是理解 happens-before 关系。这种关系只是保证一个特定语句对内存的写入对另一个特定语句可见。
Java 语言规范声明如下:
17.4.5. Happens-before Order
Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.
If we have two actions x and y, we write hb(x, y) to indicate that x happens-before y.
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
因为,在您的情况下,写入静态字段然后从静态字段读取是在同一个步骤中发生的。因此建立了“发生在之前”的关系。所以读操作总是会看到写操作的效果。
此外,所有线程都将写入相同的数据。更糟糕的是,所有符合条件的线程将同时写入变量。该变量将引用最后分配的对象,其余取消引用的对象将被垃圾收集。
您的 App 中不会有很多线程同时进入同一个方法,这会由于大量对象创建而导致显着的性能下降。但是如果你只想设置变量一次,那么第二种方法更好。作为 static blocks are thread safe.