我有一个令人惊讶的问题,任何数量的谷歌搜索没有帮助。我希望我能在这里澄清一下。
我使用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
列出它们)。
成员的顺序真的重要吗?这违背了我以前的经验。我错过了什么?
答案 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
初始值设定项等按行顺序运行。所以之前,你有:
sDS
设为null
// sDS is null
sCP = new SomeConnectionPool();
sDS
设为new SomeDataSource(config);
// sDS is not null
因此,sDS
的最终结果不是null
。但之后,你有了
sCP = new SomeConnectionPool();
sDS
设为new SomeDataSource(config);
// sDS is not null
sDS
设为null
// Now sDS is null
现在sDS
是null
。
答案 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
。