Вопрос об интервью: о сериализации Java и [закрытых] одиночных элементах

В качестве альтернативы вы можете убить процесс Excel, как описано здесь здесь .

Сначала импортируйте функцию SendMessage:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

Затем отправьте WM_CLOSE сообщение в главное окно:

SendMessage((IntPtr)excel.Hwnd, 0x10, IntPtr.Zero, IntPtr.Zero);

26
задан BalusC 2 June 2010 в 14:59
поделиться

6 ответов

Вопрос, вероятно, лучше сформулировать так: "Можно ли использовать сериализацию и десериализацию с классом C, не нарушая паттерна синглтона?"

Ответ, в основном, да:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

public class AppState implements Serializable
{
    private static AppState s_instance = null;

    public static synchronized AppState getInstance() {
        if (s_instance == null) {
            s_instance = new AppState();
        }
        return s_instance;
    }

    private AppState() {
        // initialize
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        synchronized (AppState.class) {
            if (s_instance == null) {
                // re-initialize if needed

                s_instance = this; // only if everything succeeds
            }
        }
    }

    // this function must not be called other than by the deserialization runtime
    private Object readResolve() throws ObjectStreamException {
        assert(s_instance != null);
        return s_instance;
    }

    public static void main(String[] args) throws Throwable {
        assert(getInstance() == getInstance());

            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
            java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
            oos.writeObject(getInstance());
            oos.close();

            java.io.InputStream is = new java.io.ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(is);
            AppState s = (AppState)ois.readObject();
            assert(s == getInstance());
    }
}

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

Для ответов на два других вопроса (В каком сценарии мы должны сериализовать синглтон? Можно ли разработать класс, объект которого не может быть сериализован?), смотрите ответ @Michael Borgwardt.

23
ответ дан 28 November 2019 в 06:37
поделиться

я сказал да

Не по умолчанию. Помимо реализации java.io.Serializable вам необходимо переопределить методы readObject() и writeObject() readResolve(), поскольку вы не можете сериализовать статические поля. Синглтон хранит свой экземпляр в статическом поле.

, но в каком сценарии мы должны сериализовать синглтон.

На самом деле ни один полезный сценарий реального мира не приходит в голову. Синглтон, как правило, не меняет состояния в течение своего времени жизни и не содержит состояния , которое вы хотели бы сохранить / восстановить. Если это так, то было уже неправильно считать его синглтоном.

Два реальных примера синглтон-паттерна в Java SE API - это java.lang.Runtime#getRuntime() и java.awt.Desktop#getDesktop() . Ни один из них не реализует сериализуемый. Это также не имеет никакого смысла , потому что они правильно возвращают правильный / желаемый / ожидаемый экземпляр при каждом вызове. Если вы сериализуете и десериализуетесь, вы можете получить несколько экземпляров. Если вы переключитесь из среды в это время, экземпляр может вообще не работать.

И можно ли разработать класс, объект которого нельзя сериализовать.

Да. Только не позволяйте классу реализовывать интерфейс java.io.Serializable .

5
ответ дан BalusC 28 November 2019 в 06:37
поделиться

Класс, который можно сериализовать, можно создать, десериализовав его, допуская несколько экземпляров, сделав его не единичным.

ко второму вопросу из документа java для java.io.Serializable

Сериализуемость класса включена классом, реализующим интерфейс java.io.Serializable. .

Итак, чтобы реализовать класс, который не сериализуем, не реализуйте Serializable.

1
ответ дан Angelo Genovese 28 November 2019 в 06:37
поделиться

в каком сценарии мы должны сериализовать синглтон.

Представьте, что у вас есть долго работающее приложение, и вы хотите иметь возможность выключить его, а затем продолжить работу с того места, где оно было выключено (например, для обслуживания оборудования). Если приложение использует синглтон с состоянием, вам нужно иметь возможность сохранять и восстанавливать состояние синглтона, что проще всего сделать путем его сериализации.

А можно ли спроектировать класс, объект которого не может быть сериализован?

Очень просто: просто не реализуйте Serializable и сделайте класс final

23
ответ дан 28 November 2019 в 06:37
поделиться

Проблема с сериализацией синглтона заключается в том, что в итоге вы получаете два экземпляра - оригинал и десериализованную копию.

Самый очевидный способ предотвратить сериализацию - просто не реализовывать serializable. Однако, иногда вы захотите, чтобы ваш синглтон реализовал serializable, чтобы на него можно было ссылаться в сериализованном объекте без проблем.

Если это проблема, у вас есть несколько вариантов. Лучше всего сделать синглтон одночленом перечисления, если это возможно. Тогда базовая реализация Java позаботится обо всех деталях.

Если это невозможно, то вам нужно реализовать соответствующие методы readObject и writeObject, чтобы убедиться, что сериализация не создает отдельную копию.

4
ответ дан 28 November 2019 в 06:37
поделиться

Можно ли сериализовать одноэлементный объект?

Это зависит от того, как реализован одноэлементный объект. Если ваш синглтон реализован как тип перечисления с одним элементом, то это по умолчанию:

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() { ... }
}

Если ваш синглтон реализован не с использованием одноэлементного типа перечисления, а, скажем, с использованием статического фабричного метода (вариант заключается в использовании public static final field):

// Singleton with static factory
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public static Elvis getInstance() { return INSTANCE; }
    public void leaveTheBuilding() { ... }
}

Тогда недостаточно добавить реализует Serializable , чтобы сделать его сериализуемым , вы должны объявить все поля экземпляра временными (чтобы предотвратить атаку сериализации) и предоставить метод readResolve .

Чтобы сохранить одноэлементную гарантию, вы должны объявить все экземпляры поля переходные и обеспечивают метод readResolve (элемент 77). В противном случае каждый раз, когда сериализованный экземпляр десериализован, новый экземпляр будет создан, ведущий, в случай нашего примера, к ложному Наблюдения за Элвисом. Чтобы этого не произошло, добавьте этот метод readResolve для Элвиса class:

 // метод readResolve для сохранения свойства singleton
частный объект readResolve () {
 // Возвращаем истинного Элвиса и позволяем сборщику мусора
 // позаботьтесь о имитаторе Элвиса.return INSTANCE;
}

Это подробно обсуждается в Эффективной Java (где также показана атака сериализации):

  • Правило 3: Обеспечьте применение свойства singleton с помощью частного конструктора или типа перечисления
  • Правило 77: Например, управление экземпляром, предпочитайте типы перечисления to readResolve

, в каком сценарии мы должны сериализовать синглтон

. Например, для временного, краткосрочного хранения или для транспортировки объектов по сети (например, с помощью RMI).

И возможно ли создать класс, объект которого нельзя сериализовать.

Как говорили другие, не реализуйте Serializable . И даже если объект или один из его суперклассов реализует Serializable , вы все равно можете предотвратить его сериализацию, вызвав исключение NotSerializableException из writeObject () .

7
ответ дан 28 November 2019 в 06:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: