天天看點

java cloneable 接口_Cloneable 接口 記号接口(标記接口)

Cloneable 接口訓示了一個類提供了一個安全的clone方法。

首先了解Object.clone()方法:

clone是Object超類的一個protected方法,使用者代碼不能直接調用這個方法。Object的子類隻能調用Object超類中受保護的clone方法來克隆它自己的對象,必須重新定義clone為public才能允許所有方法調用這個類的執行個體的clone方法克隆對象。

clone方法的作用:clone方法對Employee調用它對這個對象一無所知,是以隻能逐個域地進行拷貝。a. 如果對象中的所有資料域都是數值或者其他基本類型,拷貝這些域沒有問題;b. 但是如果對象中包含子對象的引用,拷貝域就會得到相同子對象的另一個引用(淺拷貝)。這樣一來,原對象和克隆的對象仍然會共享一些資訊。

淺拷貝的影響:如果原對象和淺克隆對象共享的子對象是不可變的,那麼這種共享就是安全的。如果子對象屬于一個不可變的類,如String,就是這種情況。或者在對象的生命周期中,子對象一直包含不變的常量,沒有更改器會改變它,就是沒有方法會生成它的引用,這種情況同樣是安全的(舉個栗子:下面例子中hiereDay是一個Date對象,Date類的域是可變的,是以hireDay需要深拷貝。而LocalDate的域不可變,如果hireDay是不可變的LocaDate類的一個執行個體,就無需我們做任何處理了)。

public class Employee implements Cloneable {

private String name;

private double salary;

private Date hireDay;

public Employee(String name, double salary)

{

this.name = name;

this.salary = salary;

hireDay = new Date();

}

@Override

public Employee clone() throws CloneNotSupportedException

{

// call Object.clone() Employee cloned = (Employee) super.clone(); // clone會逐個域的進行拷貝

// clone mutable fields cloned.hireDay = (Date) hireDay.clone();

return cloned;

}

public void setHireDay(int year, int month, int day)

{

Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();

// Example of instance field mutation hireDay.setTime(newHireDay.getTime());

}

public void raiseSalary(double byPercent)

{

double raise = salary * byPercent / 100;

salary += raise;

}

public String toString()

{

return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";

}

}

public class Main {

public static void main(String[] args)

{

Employee employee = new Employee("jack1", 20000);

try {

Employee clone = employee.clone();

employee.raiseSalary(20);

employee.setHireDay(2020, 10, 22);

System.out.println(employee.toString());

System.out.println(clone.toString());

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

}

}

再看一個栗子,hireDay為不可變LocalDate類的執行個體時(private LocalDate hireDay;),Main.java類代碼基本不變(構造函數有改變 -> Employee employee =newEmployee("jack1",20000,2019,10,13);):

public class Employee implements Cloneable {

private String name;

private double salary;

private LocalDate hireDay;

public Employee(String name, double salary, int year, int month, int day)

{

this.name = name;

this.salary = salary;

hireDay = LocalDate.of(year, month, day);

}

@Override

public Employee clone() throws CloneNotSupportedException

{

// call Object.clone() Employee cloned = (Employee) super.clone();

// // clone mutable fields// cloned.hireDay = (Date) hireDay.clone();

return cloned;

}

public void setHireDay(int year, int month, int day)

{

// Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();// // Example of instance field mutation// hireDay.setTime(newHireDay.getTime()); hireDay = LocalDate.of(year, month, day);

}

public void raiseSalary(double byPercent)

{

double raise = salary * byPercent / 100;

salary += raise;

}

public String toString()

{

return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";

}

}

對于一個要實作clone方法的類,需要确定:實作Cloneable接口;

重新定義clone方法,并且制定public通路修飾符。

注意:

Cloneable接口的出現與接口的正常實作沒有關系。具體講,它(Cloneable接口)沒有指定clone方法,這個方法是從Object類繼承的(應該是實作接口的類從Object類繼承)。Cloneable接口的作用隻是作為一個标記,訓示 類設計者 了解克隆過程。對象對于克隆很“偏執”,如果一個對象請求克隆,但沒有實作這個接口,就會生成一個受查異常(報錯:java.lang.CloneNotSupportedException)。

Cloneable接口是Java提供的一組标記接口(tagging interface)之一。有些程式員也稱之為記号接口(marker interface)。注意:Comparable等接口的通常用途是確定一個類實作一個或一組特定的方法。标記接口不包含任何方法,它唯一的作用就是允許在類型查詢中使用instanceof:

if (obj instanceof Cloneable) ..

建議自己程式中不要使用标記接口。

即使clone的預設(淺拷貝)實作能夠滿足要求,還是需要實作Cloneable接口,将clone方法重新定義為public,再調用super.clone()。

class Employee implements Cloneable()

{

// raise visibility level to public, change return type public Employee clone() throws CloneNotSupportedException

{

return (Employee) super.clone();

}

...

}

如果一個類重寫了clone方法,但是類沒有聲明實作接口 implements Cloneable,類會抛出一個CloneNotSupportedException異常。當然,Employee和Date類實作了Cloneable接口,是以不會抛出這個異常。不過編譯器不會了解這一點,是以我們聲明了這個異常:

public Employee clone() throws CloneNotSupportedException

這樣,子類在不支援克隆時選擇抛出一個CloneNotSupportedException異常。

clone沒有想象中的那麼常用,标準庫中隻有5%的類實作了clone。