【问题标题】:How to force a class to be initialised? [closed]如何强制初始化一个类? [关闭]
【发布时间】:2011-04-03 08:55:33
【问题描述】:

最好和最干净的方法是什么?具体来说,我需要静态初始化块中的一些代码才能在该类中运行,但我想让它看起来尽可能干净。

【问题讨论】:

  • 我见过这样的建议:Class aClass = A.class;但它看起来有点难看,并且会生成一个未使用的变量警告。
  • 您可以使用@SuppressWarnings("unused") 禁止警告
  • 引用这样的类文字不会导致类被初始化。在旧版本中,它确实如此,但在 Java 5 中,它没有。
  • @erickson 虽然如果你用-source 1.4 -target 1.4 编译,它仍然会在 1.5+ 上初始化类。
  • @Tom H. 是的,但你为什么要这样做?

标签: java coding-style static-initializer


【解决方案1】:

类之间不可见的依赖关系不是一个好主意。我建议将静态初始化程序块中的代码移动到静态方法并直接在依赖类中调用它。可以重写静态初始化程序块以调用新创建的静态方法。

【讨论】:

  • 这是一个非常非常特殊的用途。我想要一组可以覆盖以进行测试的静态标志。有点像设置属性。将此初始化放在函数调用中为在那里运行的各种代码打开了大门,但我宁愿将其限制为 static { DebugUtils.FLAG_TEST_USER = true; }
  • 所有可以在静态方法中运行的代码都可以在静态块中运行,对吧?它是如何限制的?
  • 我想我很困惑......由于静态块通常有变量初始化,我认为它仅限于此。所以没有限制?
  • @Artem:你可以在静态块中做几乎任何事情,一个类的静态块保证在任何代码访问该类之前自动执行。等等,如果您对我的回答发表评论,这是否是您要问的,我很困惑。
【解决方案2】:
try
{
  Class.forName(class name as string)
}
catch(ClassNotFoundException e)
{
  whatever
}

应该可以的。

@Longpoke

也许我误解了什么。你能创建一个加载类但执行静态初始化程序的示例吗?这是一个示例,它只打印出它已加载的内容:

package test;

public class TestStatic 
{
    public static void main(String[] args) 
    {
        try 
        {
            Class.forName("test.Static");
        }
        catch (ClassNotFoundException e) 
        {
            e.printStackTrace();
        }
    }
}

正在加载以下静态类:

package test;

public class Static 
{
    static
    {
        System.out.println("Static Initializer ran...");
    }
}

如果在满足您列出的条件之前未运行静态初始化程序,那么为什么在我运行测试时会执行此 println?那就是我满足你列出的哪个条件?

【讨论】:

  • 你可以使用 Class.forName download.oracle.com/javase/6/docs/api/java/lang/…, boolean, java.lang.ClassLoader)> 的重载形式。第二个参数确定是否要初始化类。另一种可能性是使用 Static.class 来获取对该类的引用,这不会引发初始化。
  • 非常感谢您向我解释!我从未使用过其他形式的 forName() 并且不知道单参数形式将初始化标志默认为 true。我的回答仍然有效,因为它会导致静态初始化程序执行,这是我相信 Artem 所追求的。我建议的表单加载 AND 显式初始化类。这么多年过去了,我还在学习新东西……
  • 您的方法确实有效,但实际上您应该使用完整的形式,尤其是在您不知道哪个 ClassLoader 加载了类的更复杂的情况下。这在 Sun 的指南中有所介绍:java.sun.com/j2se/1.5.0/compatibility.html(见第 5 点)
【解决方案3】:

一种解决方案是调用静态方法:

public class A {
   static { DebugUtils.FLAG_TEST_USER = true; }

   static void init() {}
}

然后调用A.init()强制初始化。

但是,这样做完全是代码异味。考虑用单例对象中的标准构造函数替换您的静态代码。

【讨论】:

    【解决方案4】:

    如果你需要在一个类中静态初始化某些东西,这意味着必须有依赖于它的客户端类。

    如果有一个客户端,或者我们称它为初始化块的自然归宿,我认为在那里初始化它是最干净的。

    对于许多相等的客户端情况,从这些类中验证另一个类中的静态初始化是否成功可能是一个好主意。然后记录耦合,并且您确定该类总是在第一个客户端需要它之前被初始化。

    【讨论】:

      【解决方案5】:

      正在加载!= 初始化。

      您希望初始化您的类(这是在执行静态块时,除其他外)。

      Java Language Specification 的摘录说:

      一个类或接口类型 T 将在第一次出现>以下任何一项之前立即初始化:

      • T 是一个类,并创建了一个 T 的实例。
      • T 是一个类,调用了 T 声明的静态方法。
      • 分配了一个由 T 声明的静态字段。
      • 使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。
      • T 是一个顶级类,在 T 中执行词法嵌套的断言语句(第 14.10 节)。

      在类 Class 和包 java.lang.reflect 中调用某些反射方法也会导致类或接口初始化。在任何其他情况下,类或接口都不会被初始化。

      Doh,anovstrup,已经说过了:只需创建一个名为 init 的空静态函数即可。一定要好好记录……不过,在格式良好的代码的上下文中,我个人看不到任何用例。

      【讨论】:

      • +1 引用规范!
      • 谢谢长戳。很有意义。请参阅我上面关于使用它来覆盖标志的评论。我当然可以在静态函数中做到这一点,我只是认为在静态块中设置静态变量更好......
      • 不,设置变量不必在静态块中完成,存在静态初始化器,因此您可以使用多个语句来初始化静态变量,而普通的静态变量声明(即:static int x = 123; static int y = new Thing().getY(); ) 只能包含表达式。
      • 出于性能原因这样做怎么样?例如在显示初始屏幕时强制进行静态初始化?
      【解决方案6】:

      您可以使用以下代码来强制初始化一个类:

      //... Foo.class ...          //OLD CODE
      ... forceInit(Foo.class) ... //NEW CODE
      
      /**
       * Forces the initialization of the class pertaining to 
       * the specified <tt>Class</tt> object. 
       * This method does nothing if the class is already
       * initialized prior to invocation.
       *
       * @param klass the class for which to force initialization
       * @return <tt>klass</tt>
      
       */
      public static <T> Class<T> forceInit(Class<T> klass) {
          try {
              Class.forName(klass.getName(), true, klass.getClassLoader());
          } catch (ClassNotFoundException e) {
              throw new AssertionError(e);  // Can't happen
          }
          return klass;
      } 
      

      【讨论】:

      • 从技术上讲,只需将 Foo.class 作为参数传递就可以加载它;)
      • @ssinghal:你说“这行不通!”,但它确实行得通。如果该类尚未初始化,则此代码将对其进行初始化。如果该类已经被初始化,那么这段代码实际上什么也不做,这是它在这种情况下应该做的。
      • 午夜,不,我刚刚检查了 Java 8:“.class”和“.class.getName()”都没有加载类。 “Class.forName”可以。
      猜你喜欢
      • 2013-03-05
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多