В настоящее время я разрабатываю объектно-ориентированную систему в Java. Мне необходимо назначить уникальный целочисленный идентификатор каждому объекту в приложении, а также иметь возможность сериализовать и десериализовать все содержимое приложения. Мне указали на шаблон проектирования Singleton как на средство хранения Java-эквивалента «глобальных переменных», но общие реализации в Java с использованием Class или Enum являются несериализуемыми или неизменяемыми соответственно. Как мне сделать синглтон, который был бы одновременно изменяемым и сериализуемым?
Ниже приведен мой текущий Java-код для написанного мною класса Singleton, который не должен быть сериализуемым, поскольку idCountSingleton является статическим:
import java.io.Serializable;
public class idCountSingleton implements Serializable{
private static idCountSingleton INSTANCE;
private int count;
private idCountSingleton(int id){
idCountSingleton INSTANCE = new idCountSingleton(id);
count = id + 1;
}
public int getID(){
return this.count;
}
public idCountSingleton getInstance(){
return this.INSTANCE;
}
}
Я не думаю, что ваш код делает то, что вы хотите. Вы объявляете локальную переменную с именем INSTANCE и присваиваете ей значение, поэтому ваше статическое поле INSTANCE всегда имеет значение null. Поле count никогда не меняется. Это то что ты хочешь?
@tgdavies Цель состоит в том, чтобы счетчик увеличивался при каждом вызове getInstance(). Очевидно, что я не смог это реализовать.
1. Это не синглтон. Это не что-то конкретное, просто кусок неработающего кода. 2. Никто не может вызвать getInstance(), так как для его вызова нужен экземпляр, а получить его можно только из getInstance(), который, следовательно, должен быть static, и в этом случае он не скомпилируется из-за this, который также необходимо изменить. 3. Это сериализуемо. 4. Переменная count всегда должна быть равна 1 для реального синглтона, поэтому она бессмысленна. Непонятно, о чем вы спрашиваете.




Синглтон не обязательно должен быть неизменяемым: все, что утверждает шаблон синглтона, — это то, что во всем приложении существует только один объект этого типа. Другие объекты, которым нужен доступ к idCountSingleton, могут получить его только с помощью метода getInstance() и не могут создать его самостоятельно.
Чтобы завершить создание синглтона класса:
getInstance() также статичен, чтобы другие классы могли получить к нему доступ по имени класса (например, idCountSingleton.getInstance()).getInstance():public static synchronized idCountSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new ClassSingleton();
}
return INSTANCE;
}
Проверка if-null лениво конструирует объект, ожидая вызова getInstance() перед созданием одноэлементного объекта. Ключевое слово synchronized в сигнатуре метода гарантирует, что только один поток одновременно может войти в метод для дополнительной обработки условий гонки (например, что произойдет, если два потока пройдут проверку if-null до того, как один из них создаст объект?)
Сериализация объекта (предположительно, при завершении работы/автосохранении) может быть обработана путем доступа к объекту с помощью getInstance() из любого класса, сохраняющего состояние приложения, а десериализация может произойти внутри частного конструктора (при условии, что это одноэлементный объект, вы можете сохранить его данные в фиксированное место)
Обратите внимание: единственное, что вам нужно сериализовать, — это поле count. static поля функционально преходящи.
Сложная часть — правильно назначить INSTANCE при десериализации.
Если вы десериализуете, а затем просто вызываете getInstance, вы получите новый экземпляр вашего синглтона и потеряете значение id.
Вы можете установить INSTANCE, добавив побочный эффект к readObject, который устанавливает статическое поле:
package com.example.so;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;
import java.io.Serializable;
public class SerializableSingleton implements Serializable {
private static SerializableSingleton INSTANCE;
private long id;
public static synchronized SerializableSingleton getInstance() {
if (INSTANCE == null) {
System.out.println("Creating new SerializableSingleton");
INSTANCE = new SerializableSingleton();
}
return INSTANCE;
}
long getAndIncrementId() {
return id++;
}
@Serial
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
INSTANCE = this;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
if (new File("test").exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test"))) {
// read and discard the object
ois.readObject();
System.out.println("Read singleton, next id:" + SerializableSingleton.getInstance().getAndIncrementId());
}
}
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test"))) {
oos.writeObject(SerializableSingleton.getInstance());
}
}
}
Я изменил ваш класс, чтобы он увеличивал идентификатор при получении идентификатора из экземпляра, а не при вызове getInstance, чтобы вы не могли случайно получить один и тот же идентификатор дважды.
Этот код не компилируется. Пожалуйста, опубликуйте свой фактический код.