Интерфейсы - маркеры

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

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

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

Смотрите на свой класс " Ага, не ссылаются поля и хорошо поставлю маркер Serializable".
А когда поставите, это будет значить, что объекты, которые вы пометили могут быть записаны
в ObjectOutputStream. У класса ObjectOutputStream есть метод writeObject0(),
а в нем находятся проверки instanceof, проверяющие можно ли записывать объект и если вся серия проверок проваливается, то выбрасывается исключение NotSerializableException, а если нет- все аккуратненько пишется в память.

Создадим класс BigObject, экземпляры которого будем сериализовывать


    package post1;
    import java.io.Serializable;

    public class BigObject implements Serializable {

        private int id;
        public void setId(final int id){
            this.id = id;
        }

        public int getId() {
           return id;
        }
    }


Класс BigObject уже помечен как Serializable. У него есть одно поле id и сопутствующие методы get/set.

package post1;
    import post1.BigObject;
    import java.io.*;
    
    public class InterfaceMarker  {

        public static void main(String[] args) throws IOException, ClassNotFoundException {
            int originalId = 12;
            BigObject objectOriginal = new BigObject();
            objectOriginal.setId(originalId);
            ByteArrayOutputStream writeBuffer = new ByteArrayOutputStream();
            ObjectOutputStream outputStream = new ObjectOutputStream(writeBuffer);
            outputStream.writeObject(objectOriginal);
            outputStream.close();

            byte[] buffer = writeBuffer.toByteArray();
            ByteArrayInputStream readBuffer = new ByteArrayInputStream(buffer);
            ObjectInputStream inputStream = new ObjectInputStream(readBuffer);
            BigObject objectCopy = (BigObject)inputStream.readObject();
            if (objectCopy.getId() == originalId)
                System.out.println( "originalId equals copiedId");
        }
    }

Запись объекта


В начале создаем объект сериализируемого класса BigObject, затем готовим под него буфер данных — место куда все бантики будем складывать ByteArrayOutputStream. Затем готовим уже упомянутый ObjectOutputStream и передаем ему буфер.Записываем наш объект в поток и закрываем его.

Чтение объекта


Подготовим буфер записи readBuffer, передадим inputStream, прочтем readObject(). Теперь объект восстановлен.

Благодаря тому, что поле `id` не являлось ссылкой, мы смогли пометить Serializable класс, который полностью содержит в себе свои данные. Вместо того, чтобы добавлять функции проверки внутри нашего класса маркеры позволяют упростить процедуру валидации классов.
  • ,

level20.lesson10.home03. Если класс наследует некоторый интерфейс, то и вложенные классы его наследуют?

Решил задачу, но есть два вопроса.
Первый вопрос: Если класс наследует некоторый интерфейс, то и вложенные классы его наследуют?
Например:
public class Solution implements Serializable
{
    public static class A
    {
        protected String name = "A";

        public static final long serialVersionUID = 2L;

        public A(String name)
        {
            this.name += name;
        }

        public A()
        {
        }
    }

    public class B extends A implements Serializable
    {
        public static final long serialVersionUID = 1L;

        public B(String name)
        {
            super(name);
            this.name += name;
        }

        private void writeObject(ObjectOutputStream out) throws IOException
        {
            out.writeObject(name);
        }

        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            name = (String) in.readObject();
        }
    }
}


В данном случае класс А наследует интерфейс Serializable?

Второй вопрос: В классе, наследующем интерфейс Serializable, возможна реализация дополнительных методов, которые осуществляют сериализацию (в данном случае writeObject(ObjectOutputStream out) и readObject(ObjectInputStream in))?
Спасибо!)
  • ,

Сериализация как она есть. Часть 2

Производительность


Как я уже говорил, стандартная сериализация работает через Reflection API. Что означает, что для сериализации берется класс сериализуемого объекта, у него берется список полей, по всем полям в цикле проверяются различные условия ( transient или нет, если объект, то Externalizable или Serializable), значения пишутся в поток, причем достаются из полей тоже через reflection… В общем, ситуация ясна. В противоположность этому методу, вся процедура при использовании расширенной сериализации контролируется самим разработчиком. Осталось выяснить, какие преимущества это дает по скорости.

Итак, условия теста. Объект произвольной структуры. Два варианта – один Serializable, второй Externalizable. Некоторое количество объектов обоих вариантов инициализируется произвольными (идентичными для каждой пары объектов) данными, после чего помещается в контейнер. Контейнер тоже в одном случае Serializable, в другом Externalizable. Далее контейнеры будут сериализованы и десериализованы с замерами времени.

Полный код теста вместе с build-файлом для ant можно найти тут – serialization.zip (скачать можно с сайта-первоисточника). В тексте я буду приводить только отрывки.

Сериализуемый объект содержит следующий набор полей:

private int fieldInt;
private boolean fieldBoolean;
private long fieldLong;
private float fieldFloat;
private double fieldDouble;
private String fieldString;


Тест содержит три реализации Externalizable контейнеров. Первая из них, ContainerExt1, простейшая. Это просто сериализация содержащего объекты java.util.List:

public void writeExternal(ObjectOutput out) throws IOException {
    out.writeObject(items);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    items = (List<ItemExt>)in.readObject();
}


Вторая реализация, ContainerExt2, сериализует последовательно все имеющиеся объекты, предваряя их количеством объектов:
  • ,

Сериализация как она есть. Часть 1

На первый взгляд, сериализация кажется тривиальным процессом. Действительно, что может быть проще? Объявил класс реализующим интерфейс java.io.Serializable – и все дела. Можно сериализовать класс без проблем.

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

Статью эту можно разделить на следующие части:

  • Тонкости механизмов
  • Зачем нужен Externalizable
  • Производительность
  • Обратная сторона медали
  • Безопасность данных
  • Сериализация объектов Singleton

Приступим к первой части –

Тонкости механизмов


Прежде всего, вопрос на засыпку. А сколько существует способов сделать объект сериализуемым? Практика показывает, что более 90% разработчиков отвечают на этот вопрос приблизительно одинаково (с точностью до формулировки) – такой способ один. Между тем, их два. Про второй вспоминают далеко не все, не говоря уж о том, чтобы сказать что-то внятное о его особенностях.

Итак, каковы же эти способы? Про первый помнят все. Это уже упомянутая реализация java.io.Serializable, не требующая никаких усилий. Второй способ – это тоже реализация интерфейса, но уже другого: java.io.Externalizable. В отличие от java.io.Serializable, он содержит два метода, которые необходимо реализовать – writeExternal(ObjectOutput) и readExternal(ObjectInput). В этих методах как раз и находится логика сериализации/десериализации.

Замечание. В дальнейшем сериализацию с реализацией Serializable я буду иногда называть стандартной, а реализацию Externalizable – расширенной.
  • ,

level20.lesson04.task03


/* Как сериализовать Singleton?
Два десериализованных объекта singleton и singleton1 имеют разные ссылки в памяти, а должны иметь одинаковые.
В класс Singleton добавьте один метод (погуглите), чтобы после десериализации ссылки на объекты были равны.
Метод main не участвует в тестировании.
*/


Можно не гуглить в переводах есть статья на эту тему.
  • ,

Как работает сериализация в Java

В этой статье мы расскажем, что такое сериализация и как она работает в Java.


Введение

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

Сериализация объектов, как новая возможность введенная в JDK 1.1, предоставляет функцию для преобразования групп или отдельных объектов, в поток битов или массив байтов, для хранения или передаче по сети. И как было сказано, данный поток битов или массив байтов, можно преобразовать обратно в объекты Java. Главным образом это происходит автоматически благодаря классам ObjectInputStream и ObjectOutputStream. Программист может решить реализовать эту функцию, путем реализации интерфейса Serializable при создании класса.
  • ,

Мини гид по реализации интерфейса Serializable в Java (Часть 1).

Все мы знаем, что интерфейс Serializable нужен для сериализации классов. Также этот интерфейс рекомендует вам использовать поле serialVersionUID. Но знаете ли вы, что даже если вы все сделаете согласно рекомендациям, ваш код все равно может не работать? Давайте определим дальнейшие изменения в ваших классах которые не сломают совместимость с предыдущими версиями, и те которые ее точно сломают.
  • ,

Мини гид по реализации интерфейса Serializable в Java (Часть 2).

Еще несколько вещей которые нужно помнить
  1. Используйте @serial тэг чтобы обозначать сериализуемые поля.
  2. Расширение .ser обычно используется для файлов с сериализованными объектами.
  3. Статические и несериализуемые поля не сериализуются
  4. Расширяемые классы не должны быть сериализуемыми, кроме как при необходимости.
  5. Внутренние классы редко, если вообще, могут быть сериализуемыми.
  6. Классы контейнеры должны обычно следовать стилю Hashtable который реализует интерфейс Serializable с парами ключ-значение в противоположность большим структурам данных.