GrabDuck

Ссылочные типы и клонирование объектов | Java

:

Последнее обновление: 30.10.2015

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

Book book = new Book("Война и мир", "Л. Толстой", 1863);
Book book2 = book;
book2.setName("Отцы и дети");
System.out.println(book.getName());

Здесь создаем два объекта Book и один присваиваем другому. Но, несмотря на то, что мы изменяем только объект book2, вместе с ним изменяется и объект book. Потому что после присвоения они указывают на одну и ту же область в памяти, где собственно данные об объекте Book и его полях и хранятся.

Клонирование объектов в Java

Чтобы избежать этой проблемы, необходимо создать отдельный объект для переменной book2, например, с помощью метода clone:

class Book implements Cloneable{

	//остльной код класса
	
	public Book clone() throws CloneNotSupportedException{
    
        return (Book) super.clone();
    }
}

Для реализации клонирования класс Book должен применить интерфейс Cloneable, который определяет метод clone. Реализация этого метода просто возвращает вызов метода clone для родительского класса - то есть класса Object с преобразованием к типу Book.

Кроме того, на случай если класс не поддерживает клонирование, метод должен выбрасывать исключение CloneNotSupportedException, что определяется с помощью оператора throws.

Затем с помощью вызова этого метода мы можем осуществить копирование:

try{
    Book book = new Book("Война и мир", "Л. Толстой", 1863);
    Book book2 = book.clone();
}
catch(CloneNotSupportedException ex){
        
    System.out.println("Не поддерживается клонирование");
}

Однако данный способ осуществляет неполное копирование и подойдет, если клонируемый объект не содержит сложных объектов. Например, пусть класс Book имеет следующее определение:

class Book implements Cloneable{

    private String name;
    private Author author;
    
    public void setName(String n){ name=n;}
    public String getName(){ return name;}
    
    public void setAuthor(String n){ author.setName(n);}
    public String getAuthor(){ return author.getName();}

    Book(String name, String author){
        
        this.name = name;
        this.author = new Author(author);
    }
    
    public String toString(){
        
        return "Книга '" + name + "' (автор " +  author + ")";
    }
    
    public Book clone() throws CloneNotSupportedException{
    
        return (Book) super.clone();
    }
}

class Author{

    private String name;
    
    public void setName(String n){ name=n;}
    public String getName(){ return name;}
    
    public Author(String name){
    
        this.name=name;
    }
}

Если мы попробуем изменить автора книги, нас последует неудача:

try{
    Book book = new Book("Война и мир", "Л. Толстой");
    Book book2 = book.clone();
    book2.setAuthor("И. Тургенев");
    System.out.println(book.getAuthor());
}
catch(CloneNotSupportedException ex){
        
    System.out.println("Не поддерживается клонирование");
}

В этом случае, хотя переменные book и book2 будут указывать на разные объекты в памяти, но эти объекты при этом будут указывать на один объект Author.

Неполное копирование объектов в Java

И в этом случае нам необходимо выполнить полное копирование. Для этого, во-первых, надо определить метод клонирования у класса Author:

class Author implements Cloneable{

    // остальной код класса
    
    public Author clone() throws CloneNotSupportedException{
    
        return (Author) super.clone();
    }
}

И затем исправим метод clone в классе Book следующим образом:

public Book clone() throws CloneNotSupportedException{
    
    Book newBook = (Book) super.clone();
    newBook.author=(Author) author.clone();
    return newBook;
}