Java порядок полей и методов класса в singleton

#java #mysql #singleton #connection-pooling

#java #mysql #singleton #объединение в пул соединений

Вопрос:

У меня удивительная проблема, заключающаяся в том, что любое количество поисковых запросов не помогает. Я надеялся, что смогу уточнить здесь.

Я объявил Java singleton для объединения в пул соединений моей базы данных 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;
   }
}
 

В моем приложении я использую singleton следующим образом:

 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
}
 

Я получаю исключение нулевого указателя в строке, которая запрашивает a getConnection() . Я вижу, что объекты connection выделяются в базе данных MySQL ( show processlist перечисляет их).

Действительно ли порядок элементов имеет значение? Это противоречит моему предыдущему опыту. Чего мне не хватает?

Ответ №1:

В первом порядке :

    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. Спасибо за все ответы, но это была безнадежная оплошность с моей стороны — я слишком долго смотрел на этот код, и поэтому я пропустил =null из-за слепого вырезания и вставки. Тем не менее, урок static хорошо усвоен— Я запутался с переупорядочением инструкций Java — представлял, что это как-то связано с этим. Модераторы — я пытался удалить этот вопрос, но не смог.

2. @Sonny Здесь считается плохой практикой удалять вопрос после получения ответа. Задаваемые здесь вопросы и ответы на них призваны помочь будущим посетителям.

3. Достаточно справедливо @Eran. Спасибо за быстрый ответ.

Ответ №2:

Вы должны понимать, что происходит при создании класса. При создании класса также создаются его статические члены, построчно.

Я думаю, что реальная проблема заключается в том, что вы объявили SomeDataSource как статический, когда этого не должно быть. Однако, чтобы объяснить, что вызывает проблему, давайте продолжим ее рассмотрение.

В вашем первом примере

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

sDs = null. В результате создается sCP и создается sDs.

Когда вы меняете порядок, создается sCP, как и sDs. Затем следующая строка возвращает 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 в определенных местах.

Комментарии:

1. Это определенно странный шаблон. Нет данных элемента экземпляра или специфичной для экземпляра логики, и вызов «getInstance» вернет один и тот же объект всем вызывающим (после повторной инициализации SDS статического элемента). Похоже, что весь класс можно сделать статическим без изменения его поведения, по крайней мере, как показано?

2. @T3am5hark Это правда. Это очень странный случай. Я действительно этого не заметил. Я отредактирую свой ответ, чтобы показать более правильный шаблон singleton.

Ответ №3:

ДА. Изменение имеет смысл.

static инициализаторы и тому подобное выполняются в порядке строк. Итак, раньше у вас было:

  1. Установить sDS в null // sDS is null
  2. sCP = new SomeConnectionPool();
  3. Перейдите в конструктор
  4. Установить sDS в new SomeDataSource(config); // sDS is not null
  5. Конструктор выхода
  6. продолжить

Итак, ваш конечный результат — 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. продолжить

И теперь sDS есть null .