JavaRush /Курсы /Java Multithreading /Интерфейсы-маркеры, глубокое клонирование

Интерфейсы-маркеры, глубокое клонирование

Java Multithreading
4 уровень , 1 лекция
Открыта
Интерфейсы-маркеры, глубокое клонирование - 1

— Привет, Амиго!

— Привет, Билаабо!

— Сегодня я расскажу тебе про интерфейсы-маркеры.

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

Примеры таких интерфейсов: Cloneable, Serializable, Remote.

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

Интерфейс Remote используется, чтобы обозначать объекты, которые поддерживают удаленный вызов – вызов из другой Java-машины и/или другого компьютера.

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

Кстати, о клонировании.

Клонирование делится на два типа – обычное клонирование и глубокое клонирование.

Обычное клонирование – это когда создается дубликат только указанного объекта, без его внутренних объектов.

Глубокое клонирование – это когда создается дубликат объекта, объектов, на которые он ссылается, объектов, на которые ссылаются они и т.д.

Есть очень хороший способ выполнить качественное глубокое клонирование.

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

Вот что можно сделать:

1) Создать буфер (массив байт) в памяти.

2) Сериализовать в него нужный объект с подобъектами.

3) Десериализовать из буфера копию сохраненной в него группы объектов.

Код
BigObject objectOriginal = new BigObject();

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();

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

На третьей строчке мы создаем ByteArrayOutputStream – массив байт, который будет динамически растягиваться при добавлении к нему новых данных (как ArrayList).

На 4-й строчке мы создаем ObjectOutputStream, который используется для сериализации.

В пятой строчке мы сериализуем объект objectOriginal в массив байт с помощью outputStream и сохраняем его в массив writeBuffer.

На 8-й строчке мы преобразовываем writeBuffer в обычный массив байт. Дальше мы из этого массива будем «читать» наш новый объект.

На 9-й строчке мы оборачиваем buffer в класс ByteArrayInputStream, чтобы из него можно было читать, как из InputStream.

На 10-й строчке передаем объект readBuffer классу ObjectInputStream, для чтения (десериализации) объекта.

На 11-й строчке мы читаем наш объект и преобразуем его к типу BigObject.

Как тебе?

— Красота.

Кстати, когда код раскрашен разными цветами – гораздо легче его понимать.

Комментарии (71)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
The Sanya Уровень 29
6 июля 2025
// Создаём оригинальный объект, который будем копировать BigObject objectOriginal = new BigObject(); // Создаём буфер для записи байтов (в памяти), куда будет записан сериализованный объект ByteArrayOutputStream writeBuffer = new ByteArrayOutputStream(); // Оборачиваем буфер объектным потоком для сериализации Java-объектов 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 BigObject objectCopy = (BigObject)inputStream.readObject();
Алексей Уровень 7
10 ноября 2024
В смысле "Красота"??? Ты вопросы будешь задавать, а?
Gans Electro Уровень 42
26 мая 2023
В статье, в этом блоке 8 цветов

BigObject objectOriginal = new BigObject();

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();
Rolik Уровень 41
14 апреля 2023
Реализация Cloneable не гарантирует глубокое клонирование. Только сериализация.
Ra Уровень 16 Student
16 января 2023
Чем дальше в лес, тем толще партизаны
Buenos Уровень 49
1 июля 2022
у меня как юриста картинка вызвала замешательство...))
Oleg Khilko Уровень 51
25 августа 2022
а я, как юрист, искренне над ней поржал)
7 февраля 2022
Сериализация - глубокое клонирование, создается копия объектов всех полей, и объектов полей полей и т.д. В итоге все ссылки нового объекта будут указывать на новый объект. Клонирование - создается копия самого объекта, со ссылками на поля старого объекта.
BlackGrizzli Team Уровень 46
16 декабря 2022
"В итоге все ссылки нового объекта будут указывать на новый объект." А разве для этого мы и не прибегаем к клонированию, чтобы он был идентичен оригиналу, но при этом независим ?

public class Solution implements Cloneable {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Solution(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Solution(Solution solution) {
        this.name = solution.name;
        this.age = solution.age;
    }

    public static void main(String[] args) {
        Solution solution = new Solution("DIMA", 30);
        Solution clone = solution.clone();
        System.out.println(solution.equals(clone));
        clone.setName("Vova");
        System.out.println(solution.name + "--> solution.name");
        System.out.println(clone.name + "--> clone.name");
    }

    @Override
    public Solution clone() {
        try {
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return (Solution) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
И вроде как в стандартном клонировании объекты становятся независимы друг от друга

false
DIMA--> solution.name
Vova--> clone.name
17 декабря 2022
По выводу - первая строка дает false, тк метод equals не переопределен и у объектов разные ссылки в памяти. По второй и третьей строке - если у объектов оказались разные имена после того, как мы засетили поле name у объекта clone, то это не значит что он был независим от объекта solution после клонирования. В первом блоке после создания объекта clone в 25-й строке у него все ссылочные поля ссылаются на поля объекта solution. Можешь сразу после клонирования сделать

 System.out.println(solution.name == clone.name);
После 27-й строки мы указываем, что поле name будет ссылаться на что-то другое, например String Pool.
Руслан Уровень 30
4 декабря 2021
Я всё забыл, походу на этом уровне застряну на какое-то время
Артём Уровень 26
15 сентября 2021
Для тех кто хочет раскрасить свой код , для лучшего понимания: Settings > Editor > Color Scheme > выбираем Java > Senematic highlighting > и в правой колонке ставим галочку на Senematic highlighting > Apply ! И готово
Oleksii Уровень 36
29 июля 2021
У меня IDEA не понимает этот код. Строки 5 и 6 :

 oos.writeObject(objectOriginal);
    oos.close();
writeObject and close - красным шрифтом и ничего поделать не могу. Why?
Konstantin Уровень 51
6 сентября 2021
Методом гадания на кофейной гуще попробуем определить... не хватает import java.io.ObjectOutputStream; или в oos.writeObject(objectOriginal); oss - имеет тип не ObjectOutputStream или objectOriginal не Serializable