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

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

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

public class Program{
     
	public static void main(String[] args) {
         
		Person tom = new Person("Tom", 23);
		tom.display();		// Person Tom
		Person bob = tom;
		bob.setName("Bob");
		tom.display();		// Person Bob
	}
}
class Person{
	private String name;
	private int age;
	
	Person(String name, int age){
		this.name=name;
		this.age=age;
	}
	void setName(String name){
		this.name = name;
	}
	void setAge(int age){
		this.age = age;
	}
	void display(){
		System.out.printf("Person Name: %s \n", name);
	}
}

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

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

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

class Person implements Cloneable{
	private String name;
	private int age;
	
	Person(String name, int age){
		this.name=name;
		this.age=age;
	}
	void setName(String name){
		this.name = name;
	}
	void setAge(int age){
		this.age = age;
	}
	void display(){
		System.out.printf("Person %s \n", name);
	}
	
	public Person clone() throws CloneNotSupportedException{
     
        return (Person) super.clone();
    }
}

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

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

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

try{
	Person tom = new Person("Tom", 23);
	Person bob = tom.clone();
	bob.setName("Bob");
	tom.display();		// Person Tom
}
catch(CloneNotSupportedException ex){
				 
	System.out.println("Clonable not implemented");
}

Однако данный способ осуществляет неполное копирование и подойдет, если клонируемый объект не содержит сложных объектов. Например, пусть класс 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("War and Peace", "Leo Tolstoy");
    Book book2 = book.clone();
    book2.setAuthor("Ivan Turgenev");
    System.out.println(book.getAuthor());
}
catch(CloneNotSupportedException ex){
        
    System.out.println("Cloneable not implemented");
}

В этом случае, хотя переменные 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;
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850