Резюме глубоких и поверхностных вопросов интервью с клонами - с подробными ответами

Java опрос
Резюме глубоких и поверхностных вопросов интервью с клонами - с подробными ответами

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

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 等号赋值( 基本类型)
        int number = 6;
        int number2 = number;
        // 修改 number2 的值
        number2 = 9;
        System.out.println("number:" + number);
        System.out.println("number2:" + number2);
        // 等号赋值(对象)
        Dog dog = new Dog();
        dog.name = "旺财";
        dog.age = 5;
        Dog dog2 = dog;
        // 修改 dog2 的值
        dog2.name = "大黄";
        dog2.age = 3;
        System.out.println(dog.name + "," + dog.age + "岁");
        System.out.println(dog2.name + "," + dog2.age + "岁");
    }
}

Результат выполнения программы:

number:6
number2:9
大黄,3岁
大黄,3岁

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

Следовательно, чтобы предотвратить возникновение такого рода проблем, необходимо использовать клонирование объектов для решения проблемы копирования ссылочного типа.

1. Мелкий клон

Методом реализации мелкого клонирования по умолчанию является clone(), а код реализации выглядит следующим образом:

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Dog dog = new Dog();
        dog.name = "旺财";
        dog.age = 5;
        // 克隆
        Dog dog3 = (Dog) dog.clone();
        dog3.name = "小白";
        dog3.age = 2;
        System.out.println(dog.name + "," + dog.age + "岁");
        System.out.println(dog3.name + "," + dog3.age + "岁");
    }
}
class Dog implements Cloneable {
    public String name;
    public int age;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Результат выполнения программы:

旺财,5岁
小白,2岁

Видно, что использование клона может решить проблему копирования ссылочного типа, принцип показан на следующем рисунке:

Этот способ копирования называетсяМелкий клон. Условия реализации мелкого клонирования:Объекты, которые необходимо клонировать, должны реализовыватьCloneableинтерфейс и переопределитьclone()метод для клонирования этого объекта. тем не мениеТакже есть проблема с использованием неглубоких клонов., пожалуйста, обратитесь к следующему коду.

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        DogChild dogChild = new DogChild();
        dogChild.name = "二狗";
        Dog dog4 = new Dog();
        dog4.name = "大黄";
        dog4.dogChild = dogChild;
        Dog dog5 = (Dog) dog4.clone();
        dog5.name = "旺财";
        dog5.dogChild.name = "狗二";
        System.out.println("dog name 4:"+dog4.name);
        System.out.println("dog name 5:"+dog5.name);
        System.out.println("dog child name 4:"+dog4.dogChild.name);
        System.out.println("dog child name 5:"+dog5.dogChild.name);
    }
}
class Dog implements Cloneable {
    public String name;
    public DogChild dogChild;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class DogChild {
    public String name;
}

Результат выполнения программы:

dog name 4:大黄
dog name 5:旺财
dog child name 4:狗二
dog child name 5:狗二

То есть неглубокий клон будет копировать только тип значения объекта, но не ссылочный тип объекта. Причина показана на рисунке ниже:

Чтобы решить проблему, связанную с тем, что ссылочные типы не копируются, используйте методглубокий клон.

2. Глубокое клонирование

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

  • Сериализация реализует глубокое клонирование: сначала сериализуйте исходный объект в поток байтов памяти, а затем десериализуйте только что сохраненный объект из потока байтов. Новый объект и исходный объект не имеют общих адресов. Это обеспечивает глубокое клонирование.
  • Все ссылочные типы реализуют клонирование: все ссылочные типы, для которых объект должен быть скопирован, реализуют клонирование, и все объекты являются копируемыми новыми объектами, таким образом реализуя глубокое клонирование.

Метод реализации глубокого клонирования 1: сериализация

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

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        BirdChild birdChild = new BirdChild();
        birdChild.name = "小小鸟";
        Bird bird = new Bird();
        bird.name = "小鸟";
        bird.birdChild = birdChild;
        // 使用序列化克隆对象
        Bird bird2 = CloneUtils.clone(bird);
        bird2.name = "黄雀";
        bird2.birdChild.name = "小黄雀";
        System.out.println("bird name:" + bird.name);
        System.out.println("bird child name:" + bird.birdChild.name);
        System.out.println("bird name 2:" + bird2.name);
        System.out.println("bird child name 2:" + bird2.birdChild.name);
    }
}
class CloneUtils {
    public static <T extends Serializable> T clone(T obj) {
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bo);
            oos.writeObject(obj);
            oos.close();
            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流
            ObjectInputStream oi = new ObjectInputStream(bi);
            //返回生成的新对象
            cloneObj = (T) oi.readObject();
            oi.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

Результат выполнения программы:

bird name:小鸟
bird child name:小小鸟
bird name 2:黄雀
bird child name 2:小黄雀

Метод реализации глубокого клонирования 2: все ссылочные типы реализуют клонирование

class SerializableTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    ParrotChild parrotChild = new ParrotChild();
        parrotChild.name = "小鹦鹉";
        Parrot parrot = new Parrot();
        parrot.name = "大鹦鹉";
        parrot.parrotChild = parrotChild;
        // 克隆
        Parrot parrot2 = (Parrot) parrot.clone();
        parrot2.name = "老鹦鹉";
        parrot2.parrotChild.name = "少鹦鹉";
        System.out.println("parrot name:" + parrot.name);
        System.out.println("parrot child name:" + parrot.parrotChild.name);
        System.out.println("parrot name 2:" + parrot2.name);
        System.out.println("parrot child name 2:" + parrot2.parrotChild.name);
    }
 }
class Parrot implements Cloneable {
    public String name;
    public ParrotChild parrotChild;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Parrot bird = (Parrot) super.clone();
        bird.parrotChild = (ParrotChild) parrotChild.clone();
        return bird;
    }
}
class ParrotChild implements Cloneable {
    public String name;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Результат выполнения программы:

parrot name:大鹦鹉
parrot child name:小鹦鹉
parrot name 2:老鹦鹉
parrot child name 2:少鹦鹉

3. Копирование вопросов, связанных с интервью

1. Каковы преимущества использования клонов?

О: Преимущества включают в себя следующее:

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

исходный код clone(), как показано ниже:

2. В чем разница между поверхностным клонированием и глубоким клонированием?

Ответ: Разница в основном в копировании ссылочных типов, конкретная информация следующая:

  • Неглубокое клонирование: копируется только тип значения объекта, а не ссылочный тип объекта;
  • Глубокое клонирование: копирует весь объект, включая типы значений и ссылочные типы.

3. Как реализовать мелкое клонирование?

Ответ: Клонированный объект реализует интерфейс Cloneable и переопределяет метод clone() для достижения поверхностного клонирования.

4. Каков результат выполнения следующего кода?

import java.util.Arrays;
class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        CloneObj cloneObj = new CloneObj();
        cloneObj.name = "老王";
        cloneObj.age = 30;
        cloneObj.sistersAge = new int[]{18, 19};
        CloneObj cloneObj2 = (CloneObj) cloneObj.clone();
        cloneObj2.name = "磊哥";
        cloneObj2.age = 33;
        cloneObj2.sistersAge[0] = 20;
        System.out.println(cloneObj.name + "|" + cloneObj2.name);
        System.out.println(cloneObj.age + "|" + cloneObj2.age);
        System.out.println(Arrays.toString(cloneObj.sistersAge) + "|" + Arrays.toString(cloneObj2.sistersAge));
    }
}
class CloneObj implements Cloneable {
    public String name;
    public int age;
    public int[] sistersAge;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Ответ: Результат выполнения следующий.

老王|磊哥
30|33
[20, 19]|[20, 19]

5. Как реализовано глубокое клонирование? Есть несколько реализаций?

Ответ: Существует два общих метода реализации:

  • Глубокое клонирование посредством сериализации (реализация сериализации: собственная сериализация Java, сериализация JSON, сериализация Гессе);
  • Все ссылочные типы реализуют клонирование, что позволяет осуществлять глубокое клонирование.

6. Почему мы не можем использовать метод Clone объекта Object напрямую, и нам нужно переопределить метод clone(), прежде чем мы сможем клонировать?

Ответ: Хотя все классы являются подклассами Object, поскольку метод clone() в Object объявлен как защищенный уровень доступа, другие классы, не входящие в пакет java.lang, не могут использоваться напрямую. Поэтому для реализации функции клонирования необходимо реализовать Cloneable и переопределить метод clone().

7. Может ли сериализация реализовать глубокое клонирование? Каков принцип реализации?

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

4. Резюме

Вызов метода clone() в классе Object по умолчанию является неглубоким клонированием. Неглубокие клоны могут копировать только типы значений, а не ссылочные типы. Поэтому в большинстве случаев нам нужны глубокие клоны. Глубокие клоны обычно реализуются двумя способами: сериализацией кстати или клонировать все ссылочные типы.

Нажмите здесь, чтобы загрузить исходный код этой статьи


Специальное примечание: эта статья взята из«Полный анализ вопросов на собеседовании по Java»