单例中类字段和方法的Java顺序

时间:2014-06-18 20:28:34

标签: java mysql singleton connection-pooling

我有一个令人惊讶的问题,任何数量的谷歌搜索没有帮助。我希望我能在这里澄清一下。

我使用eager初始化声明了一个Java单例,用于连接池我的JDBC / MySQL数据库,如下所示:

public class SomeConnectionPool
{
   private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
   private static SomeDataSource sDS = null;
   private static SomeConnectionPool sCP = new SomeConnectionPool ();

   private SomeConnectionPool ()
   {
     // Configure DataSource, connect to DB
      sDS = new SomeDataSource (config);
   }

   public static SomeConnectionPool getInstance ()
   {
      return sCP;
   }

   public static SomeDataSource getDataSource ()
   {
      return sDS;
   }
}

在我的应用程序中,我使用单身如下:

SomeDataSource sds = SomeConnectionPool.getInstance ().getDataSource ();
Connection connection = sds.getConnection ();

这可以按预期工作。

但是,如果我更改SomeConnectionPool类中成员的顺序如下:

public class SomeConnectionPool
{
   private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
   private static SomeConnectionPool sCP = new SomeConnectionPool ();
   private static SomeDataSource sDS = null;

   ... // the rest is the same code as before
}

我在请求getConnection()的行上得到一个空指针异常。我可以看到连接对象正在MySQL DB上分配(show processlist列出它们)。

成员的顺序真的重要吗?这违背了我以前的经验。我错过了什么?

3 个答案:

答案 0 :(得分:4)

在第一个订购中:

   private static SomeDataSource sDS = null;
   private static SomeConnectionPool sCP = new SomeConnectionPool ();

首先将sDS初始化为null,然后在SomeConnectionPool的构造函数中为其赋予非null值。因此,它不是空的。

在第二顺序中:

   private static SomeConnectionPool sCP = new SomeConnectionPool ();
   private static SomeDataSource sDS = null;

SomeConnectionPool sCP = new SomeConnectionPool ();在其构造函数中初始化sDS,稍后SomeDataSource sDS = null;将其重置为null。这就是你得到空指针异常的原因(SomeConnectionPool.getInstance ().getDataSource ();返回null)。

答案 1 :(得分:1)

是。这种变化是有道理的。

static初始值设定项等按行顺序运行。所以之前,你有:

  1. sDS设为null // sDS is null
  2. sCP = new SomeConnectionPool();
  3. 进入构造函数
  4. sDS设为new SomeDataSource(config); // sDS is not null
  5. 退出构造函数
  6. 继续
  7. 因此,sDS的最终结果不是null。但之后,你有了

    1. sCP = new SomeConnectionPool();
    2. 进入构造函数
    3. sDS设为new SomeDataSource(config); // sDS is not null
    4. 退出构造函数
    5. sDS设为null // Now sDS is null
    6. 继续
    7. 现在sDSnull

答案 2 :(得分:1)

您必须了解创建类时发生的情况。创建类时,也会逐行创建其静态成员。

我认为真正的问题是您将SomeDataSource声明为静态,而不应该是。但是,要解释造成问题的原因,请继续关注它。

在你的第一个例子中

private static SomeDataSource sDS = null;
private static SomeConnectionPool sCP = new SomeConnectionPool ();

sDs = null。 sCP被构造并且sDs被构造为结果。

当您翻转订单时,sCP会被构建,sD也是如此。比下一行将sDs设置为null。出于这个原因,您实际上应该尽量避免在构造函数中初始化静态。

相反,你应该:

public class SomeConnectionPool
{
   private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
   private static SomeDataSource sDS = new SomeDataSource (config);
   private static SomeConnectionPool sCP = new SomeConnectionPool ();

   private SomeConnectionPool ()
   {
     // Configure DataSource, connect to DB
   }

但是,除了实例之外,典型的单例模式没有任何静态。目的是具有特定于实例的逻辑,但强制实施只有一个版本的实例。例如,更合适的单身人士将是:

public class SomeConnectionPool
{
   private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
   private static SomeConnectionPool instance = new SomeConnectionPool ();
   private SomeDataSource sDS = null;

   private SomeConnectionPool ()
   {
     // Configure DataSource, connect to DB
      this.sDs = new SomeDataSource (config);
   }
   public static SomeConnectionPool getInstance ()
   {
     return instance;
   }
   public SomeDataSource getDataSource ()
   {
      return this.sDs;
   }

请注意在某些地方删除static