Java存储在类中静态反射的方法:安全?

Java存储在类中静态反射的方法:安全?,java,methods,reflection,static,singleton,Java,Methods,Reflection,Static,Singleton,在Java中类似于下面的“安全”吗?为什么 public final class Utility { private Utility() {} private static Method sFooMethod = null; public static void callFoo(SomeThing thing) { try { if(sFooMethod == null) sFooMethod

在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之前是私有的方法,当API 29公开时,它没有被更改。在AndroidX核心库的alpha版本(目前还不能使用)中,Google提供了一个HandlerCompat方法,如果私有方法不是公共的,它可以使用反射来调用私有方法。所以现在我将Google的方法复制到我自己的HandlerCompatP类中,但是我注意到如果我调用它1000次,那么反射查找将发生1000次(我看不到任何缓存)。因此,我开始思考是否有一种好方法可以只执行一次反射,并且仅在需要时执行

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

在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
    }

}
  • 不,我不建议使用反射,除非你必须这样做
  • 大多数时候,开发人员设计类的方式都不需要访问隐藏的字段或方法。很可能会有更好的方法来访问隐藏内容
  • 尤其是隐藏的字段和方法,在更新它们所包含的库时,可能会更改它们的名称。因此,您的代码可能会突然停止工作,您不知道原因,因为编译器不会输出任何错误
  • 直接访问方法或字段比通过反射更快,因为反射首先需要搜索它,而直接访问则不需要
  • 因此,如果不需要,请不要使用反射

    在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
        }
    
    }
    
  • 不,我不建议使用反射,除非你必须这样做
  • 大多数时候,开发人员设计类的方式都不需要访问隐藏的字段或方法。很可能会有更好的方法来访问隐藏内容
  • 尤其是隐藏的字段和方法,在更新它们所包含的库时,可能会更改它们的名称。因此,您的代码可能会突然停止工作,您不知道原因,因为编译器不会输出任何错误
  • 直接访问方法或字段比通过反射更快,因为反射首先需要搜索它,而直接访问则不需要

  • 因此,如果你不需要思考,就不要使用思考。我不确定你的目标是什么——也许有更好的方法来完成你想做的事情


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

    我不确定您的目标是什么——可能有更好的方法来完成您要做的事情


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

    避免内存一致性错误的关键是理解“先发生后发生”关系。这种关系只是保证一条特定语句的内存写入对另一条特定语句可见。
    Java语言规范说明如下:

    两个动作可以由“发生在之前”关系排序。如果有 动作发生在另一个动作之前,然后第一个动作对和可见 在第二天之前订购

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

    如果x和y是同一个线程的动作,并且x在y之前 程序顺序,然后是hb(x,y)

    在您的例子中,向静态字段写入数据和从静态字段读取数据是以相同的方式进行的。因此,建立了“发生在”之前的关系。因此,读取操作将始终看到写入操作的效果。
    此外,所有线程都将写入相同的数据。更糟糕的是,所有符合条件的线程将同时写入变量。该变量将引用最后分配的对象,其余被取消引用的对象将被垃圾收集。


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

    避免内存一致性错误的关键是理解“发生在发生之前”的关系。这种关系只是保证一条特定语句的内存写入对另一条特定语句可见。
    Java语言规范说明如下:

    两个动作可以由“发生在之前”关系排序。如果有 动作发生在另一个动作之前,然后第一个动作对和可见 在第二天之前订购

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

    如果x和y是同一个线程的动作,并且x在y之前 程序顺序,然后是hb(x,y)

    在您的例子中,向静态字段写入数据和从静态字段读取数据是以相同的方式进行的。因此,建立了“发生在”之前的关系。因此,读取操作将始终看到写入操作的效果。
    此外,所有线程都将写入相同的数据。更糟糕的是,所有符合条件的线程都将被删除