Почему класс должен реализовывать сериализуемый маркерный интерфейс для сериализации?

Технически я знаю, почему класс должен реализовывать сериализуемый. Причина в том, что метод writeObject объекта ObjectOutputStream внутренне проверяет "экземпляр сериализуемого" перед записью состояния объекта.

Но мой вопрос: зачем это нужно? Метод writeObject может просто записать состояние объекта независимо от того, реализует ли объект (состояние которого необходимо записать) сериализуемый или нет?

Согласно вики, класс реализует этот интерфейс, чтобы указать, что его непереходные члены-данные могут быть записаны в ObjectOutputStream. Но опять же вопрос состоит в том, почему класс должен реализовывать сериализуемость только для того, чтобы определить, является ли поле временным или нет. Даже класс, который не реализует serialiable, должен быть сериализован (за исключением полей, помеченных как переходные).

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

Почему класс должен реализовывать сериализуемый маркерный интерфейс для сериализации класса?

3 ответа

Решение

Предоставляя интерфейс маркера, люди могут выбирать, делать ли класс Serializable, Иногда вы можете этого не хотеть! (и, конечно, было бы не правильно делать это по умолчанию: см., например, ответы на этот вопрос: зачем Java нужен интерфейс Serializable?)

При инкапсуляции мы притворяемся, что ничего не раскрывается во внутреннем представлении объекта, и мы взаимодействуем с нашими компонентами только через их открытые интерфейсы; желательный атрибут, который мы обычно используем позже, когда мы хотим изменить внутреннее представление данных в компоненте, не нарушая никакого кода от его пользователей.

И наоборот, сериализация подразумевает раскрытие внутреннего состояния объекта путем преобразования состояния объекта в некоторый другой формат, который можно сохранить и воскресить позже. Это означает, что после сериализации внутренняя структура объекта не может быть изменена без риска успеха этого процесса воскрешения.

Проблемы с сериализацией могут возникать не только в случае открытых систем, но и в распределенных системах, которые каким-то образом полагаются на него. Например, если мы остановим наш сервер приложений, он может выбрать сериализацию объектов в текущем сеансе, чтобы воскресить их позже, когда сервер будет перезапущен, но если мы повторно развернем наше приложение, используя новые версии наших сериализуемых объектов, они все равно будут совместимы, когда сервер пытается их воскресить? В распределенной системе обычно используется мобильность кода, а именно, наборы классов расположены в центральном хранилище, доступном для клиентов и сервера для совместного использования общего кода. При таком подходе, поскольку объекты сериализуются для совместного использования между клиентами и серверами, рискуем ли мы что-нибудь сломать, если обновим сериализуемые классы в этом общем хранилище?

Рассмотрим для примера, что у нас был класс Person следующим образом:

public class Person {
   private String firstName;
   private String lastName;
   private boolean isMale;
   private int age;

  public boolean isMale() {
     return this.isMale;
  }
  public int getAge() {
    return this.age;
  }
  //more getters and setters
}

Допустим, мы выпустили нашу первую версию нашего API с этой абстракцией Person. Для второй версии, однако, мы хотели бы внести два изменения: во-первых, мы обнаружили, что было бы лучше, если бы мы могли сохранить дату рождения человека, а не возраст как целое число, и во-вторых, наше определение Возможно, класс Person произошел, когда в Java не было перечислений, но теперь мы хотели бы использовать их для представления пола человека.

Очевидно, что поскольку поля правильно инкапсулированы, мы могли бы изменить внутреннюю работу класса, не затрагивая общедоступный интерфейс. Примерно так:

public class Person {
   private String firstName;
   private String lastName;
   private Gender gender;
   private Date dateOfBirth;

  public boolean isMale() {
     return this.gender == Gender.MALE;
  }
  public int getAge() {
    Calendar today = Calendar.getInstance();
    Calendar birth = Calendar.getInstance();
    birth.setTime(this.dateOfBirth);
    return today.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
  }
  //the rest of getters and setters
}

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

Тем не менее, учтите, что класс Person был сериализуем по умолчанию, и если наша система является открытой системой, могут существовать тысячи строк кода, полагаясь на тот факт, что они будут способны воскрешать сериализованные объекты на основе исходного класса, или, может быть, даже клиенты, которые сериализовали расширенные классы на основе исходной версии класса как своего родителя. Некоторые из этих объектов могли быть сериализованы в двоичную форму или в какой-либо другой формат пользователями нашего API, которые сейчас хотели бы перейти ко второй версии кода.

Затем, если бы мы хотели внести некоторые изменения, как мы это делали во втором примере, мы бы сразу же нарушили некоторые из них; все те, у кого есть сериализованные объекты, основанные на исходной версии класса, которые сохранили объекты, содержащие поле возраста типа int, содержащее возраст человека, и поле с именем isMale типа boolean, содержащее информацию о поле, могут потерпеть неудачу во время десериализация этих объектов, потому что новое определение класса использует новые поля и новые типы данных.

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

Теперь рассмотрим сценарий, в котором каждый отдельный класс в JDK API по умолчанию сериализуем. Разработчики Java просто не могли развить API Java, не рискуя сломать многие приложения. Они будут вынуждены предположить, что кто-то может иметь сериализованную версию любого из классов в JDK.

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

Несмотря на все это, сериализация также имеет последствия для безопасности, так как важная, конфиденциальная информация о наших объектах может быть легко раскрыта.

Следовательно, помечая сериализуемые классы, разработчики API-интерфейсов могут с ними легче справляться.

Сериализуемый называется маркерным интерфейсом, как Cloneable. Когда вы используете некоторый интерфейс маркера (без реализации метода), вы хотите сообщить jvm или компилятору о добавлении или проверке чего-либо во время выполнения или компиляции.

Определение интерфейса маркера:

"Интерфейс называется интерфейсом маркера, когда он предоставляется в качестве дескриптора интерпретатором Java для маркировки класса, чтобы он мог обеспечивать ему особое поведение во время выполнения, и у них нет каких-либо объявлений методов".

Также маркерный интерфейс является хорошим способом классификации кода. Вы можете создать маркерный интерфейс для логического разделения вашего кода и, если у вас есть собственный инструмент, вы можете выполнить некоторую операцию предварительной обработки этих классов. Особенно полезно для разработки API и фреймворков, таких как Spring или Struts.

Интерфейс маркера может также помочь покрытию кода или инструменту просмотра кода, чтобы найти ошибки, основанные на определенном поведении интерфейсов маркера.

см. интерфейс маркера в вики http://en.wikipedia.org/wiki/Marker_Interface_pattern

Другие вопросы по тегам