【问题标题】:How to make a static initialization block execute?如何使静态初始化块执行?
【发布时间】:2014-05-19 21:50:17
【问题描述】:

在寻找我的静态初始化块不执行的原因时,我发现类中的静态初始化器永远不会执行 if it references a final

所以我尝试通过从 MYWS_PROPS 中删除“final”来解决这个问题:

public class HibernateUtil {
  public static String MYWS_PROPS = "/myws.properties";

    private static final Logger LOG = Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory = buildSessionFactory();
    private static Properties sProps;

    static {
        try {
            sProps = new Properties();
            sProps.load(ServiceUtils.class.getResourceAsStream(MYWS_PROPS));
            LOG.info("Loaded (from " + MYWS_PROPS + ") connection url: " + sProps.getProperty("dburl"));
        } 
        catch (IOException e) {
            LOG.severe("Cannot find properties file " + MYWS_PROPS);
        }               
    }   



    private static SessionFactory buildSessionFactory() {
        try {
            Configuration config = new Configuration();
            config = config.configure("resources/hibernate.cfg.xml");
            config.setProperty("hibernate.connection.url", sProps.getProperty("dburl"));    // <== NullPointerException!        
            SessionFactory session = config.buildSessionFactory();
            return session;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

}

但是那个静态块仍然不会执行!

保证静态块执行的正确方法是什么?


更新:下面的所有答案都提到了引用类以便运行静态初始化的要求。所以我再次查看了我的堆栈跟踪并验证了该类确实被引用(否则,为什么它会在同一个类方法中抛出异常?)

Caused by: java.lang.ExceptionInInitializerError
        at com.corp.dept.proj.myws.HibernateUtil.buildSessionFactory(HibernateUtil.java:76)
        at com.corp.dept.proj.myws.HibernateUtil.<clinit>(HibernateUtil.java:38)
        at com.corp.dept.proj.myws.ServicePortImpl.<init>(ServicePortImpl.java:82)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
        ... 70 more
Caused by: java.lang.NullPointerException
        at com.corp.dept.proj.myws.HibernateUtil.buildSessionFactory(HibernateUtil.java:67)
        ... 77 more

但为了安全起见,我尝试了 Class.forName 建议:

Class.forName("com.corp.dept.proj.myws.HibernateUtil");
mProps.load(ServiceUtils.class.getResourceAsStream(HibernateUtil.MYWS_PROPS));

而且那个静态块仍然不会执行。

所以我尝试显式实例化它:

HibernateUtil justExecTheDarnStaticBlock = new HibernateUtil();
mProps.load(ServiceUtils.class.getResourceAsStream(HibernateUtil.MYWS_PROPS));

但是那个静态块仍然不会执行!

我在这里错过了什么?

【问题讨论】:

  • I found that a static initializer in a class never executes if it references a final. 这不是真的。您预计static 块何时执行?
  • 当 JVM(具体来说是类加载器)加载 StaticClass(在代码中第一次引用它时)时,正在运行静态初始化块。我对没有final的要求的理解来自这里:stackoverflow.com/a/16853836/1864054我错过了什么?
  • 这与 static final 字段有关,这些字段也是常量变量,与初始化块无关。

标签: java


【解决方案1】:

如果你创建一个类的实例,静态块肯定会执行。

编辑:在 cmets 中找到的实际解决方案如下:

buildSessionFactory()方法用于sessionFactory的初始化。这取决于已经调用了静态块,但事实并非如此。如果您在静态块中而不是在其声明中调用sessionFactory = buildSessionFactory();,并且在初始化sProps 之后问题将消失。

【讨论】:

  • 我就是这样做的(请参阅我的 OP 中的更新),静态块仍然无法执行。知道为什么吗?
  • @Withheld 您似乎在 sessionFactory 常量的初始化中使用了 buildSessionFactory() 方法。这取决于已经调用的静态块;可能是这个运行的顺序被打破了。尝试在静态块中而不是在声明中调用sessionFactory = buildSessionFactory();(当然在初始化sProps 之后)。
【解决方案2】:

在处理静态初始化时,顺序很重要

举个例子:

public class StaticTest {
    private static final String someString = initSomeString();
    private static final String someOtherString = initSomeOtherString();
    private static final String whoops;

    static {
        System.out.println("static initializer");
        whoops = "whoops";
    }

    public static void main(String[] args) {
        System.out.println(someOtherString);
    }

    private static String initSomeString() {
        System.out.println("initSomeString");
        return "Initialised";
    }

    private static String initSomeOtherString() {
        System.out.println("initSomeOtherString");
        return whoops + "y daisy";
    }
}

这个类的运行结果如下:

initSomeString
initSomeOtherString
static initialiser
nully daisy

静态初始化按它们声明的顺序发生,这意味着initSomeOtherString() 被称为 初始化whoops 的静态初始化块之前。结果,whoops 在那个时候仍然是 null,所以结果 Stringnully daisy 而不是 whoopsy daisy

如果我重新排列类以便在静态初始化器之后初始化someOtherString,我会得到我想要的:

public class StaticTest {
    private static final String someString = initSomeString();
    private static final String whoops;

    static {
        System.out.println("static initialiser");
        whoops = "whoops";
    }

    private static final String someOtherString = initSomeOtherString();

    public static void main(String[] args) {
        System.out.println(someOtherString);
    }

    private static String initSomeString() {
        System.out.println("initSomeString");
        return "Initialised";
    }

    private static String initSomeOtherString() {
        System.out.println("initSomeOtherString");
        return whoops + "y daisy";
    }
}

打印

initSomeString
static initialiser
initSomeOtherString
whoopsy daisy

这是您遇到并导致问题的行为。您正在通过调用 buildSessionFactory() 来初始化 sessionFactory,这依赖于初始化 sProps

config.setProperty("hibernate.connection.url", sProps.getProperty("dburl")
                                               ^ This hasn't been initialised yet

解决方案? sessionFactory 的声明移动到下面你的静态初始化块。

【讨论】:

  • +1 以获得非常详细的解释。我应该接受你的回答,但@blalasaadri 先给了我这个提示。所以应该让接受多个答案。 :)
【解决方案3】:

第一次引用该类时将执行静态块。 (实例化或对静态成员的引用)

【讨论】:

  • 实例化或对静态成员(方法或字段)的引用。
  • @PM77-1 感谢您的更正。编辑了答案。
【解决方案4】:

如果你使用类,例如调用它的静态方法,创建它的一个实例,静态块就会被执行。

另一种方法是通过调用Class.forName 显式加载类。当您只知道名称(作为字符串)时,这很有用。有时您必须扫描 jar 文件以获取名称并使用 Class.forname 来加载它。

【讨论】:

    【解决方案5】:

    ClassLoader 需要在静态块执行之前加载类。第一次引用该类时会发生这种情况。

    您可以使用Class.forName(""); 强制执行此操作。有了这个,你可以使用final变量

    【讨论】:

    • 我很清楚我错过了一些非常基本的东西。请参阅上面的更新。额外的见解?谢谢。
    【解决方案6】:

    您链接到的问题只是在谈论编译时常量;引用它们不会加载类。但是你为加载类所做的任何事情——例如,Class.forName("HibernateUtil")——都必然会运行静态初始化程序。

    此外,由于您正在处理的 static 变量不是原始类型或 String,因此它无论如何都不能是编译时常量。

    【讨论】:

      猜你喜欢
      • 2011-01-26
      • 2016-02-21
      • 2011-01-12
      • 1970-01-01
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      • 2013-11-02
      相关资源
      最近更新 更多