根类Object中的clone方法,其权限为protected(protected native Object clone() throws CloneNotSupportedException),意味着要使一个类可以通过clone方法克隆,除了需要该类要实现Cloneable接口外,要么重写clone方法,要么与Object位于同一个包中。
【Class SubB has access only to the inherited from Base protected elements, i.e. its own elements, but the protecteddata of other Base instances is not accessible from SubB.】
显然,与Object位于同一个包是不现实的事,那么唯一的方式就是重写clone方法了。
下面我们来做两组实验:
1.不重写clone方法情况下,出现The method clone() from the type Object is not visible编译错误
package net.mosang.clonetest; public class Ship implements Cloneable { private String comfrom; public void sailing() { System.out.println("happy sailing..."); } public Ship(String comfrom) { this.comfrom = comfrom; } public String getComfrom() { return comfrom; } public void setComfrom(String comfrom) { this.comfrom = comfrom; } }
package net.mosang.clonetest; public class ShipDemo { public static void main(String[] args) { Ship ship = new Ship("China"); System.out.println("这艘船来自--->"+ship.getComfrom()); Ship ship2 = ship.clone(); //错误:The method clone() from the type Object is not visible System.out.println("这艘船来自--->"+ship2.getComfrom()); } }
这就是我们常见的 “The method clone() from the type Object is not visible“错误。虽然所有类都是Object的直接或间接子类,但对于对于基类受保护的方法或成员变量,派生类只能访问自身从基类继承而来的受保护成员,而不能访问基类实例本身的受保护成员。
2.子类重写clone方法的情况下:
package net.mosang.clonetest; public class Ship implements Cloneable { private String comfrom; public void sailing() { System.out.println("happy sailing..."); } //重写clone方法 @Override protected Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return super.clone(); } public Ship(String comfrom) { this.comfrom = comfrom; } public String getComfrom() { return comfrom; } public void setComfrom(String comfrom) { this.comfrom = comfrom; } }
package net.mosang.clonetest; public class ShipDemo { public static void main(String[] args) throws CloneNotSupportedException { Ship ship = new Ship("China"); System.out.println("这艘船来自--->"+ship.getComfrom()); Ship ship2 = (Ship)ship.clone(); System.out.println("这艘船来自--->"+ship2.getComfrom()); } }
运行一切正常,ShipDemo类中创建的Ship实例,显然调用的是其Ship类自身重写的clone方法。
下面我们再通过仔细研究protected权限来理解clone方法的使用。
很多书上,提到protected,都会以“protected 修饰属性,对于类本身、本包和其子类可见 ”概之,实际上这不完全正确,下面通过实例说明。
现有基类A,类B、B2均继承自A,但B、B2分别位于不同包中,ClientP1、ClientP2均为创建实例类(访问类),也分别位于不同包中,包结构如下图所示:
基类A代码如下:
package net.mosang.prototype.protectedtest; public class A { public String a1="abc"; protected String a2 = "def"; public void methodA1() { System.out.println("public method A1"); } protected void methodA2() { System.out.println("protected method A2"); } }
与基类A同一个包的子类B代码如下:
package net.mosang.prototype.protectedtest; public class B extends A{ }
与基类A同一个包的访问类ClientP1代码如下:
package net.mosang.prototype.protectedtest; public class ClientP1 { public static void main(String[] args) { B b = new B(); b.methodA1(); //输出 public method A1 b.methodA2(); //输出 protected method A2 } }
输出结果:
可以看到,当访问类与基类同一个包时,创建的实例可以访问基类的protected成员方法。
再来看访问类与基类不同包情况下:
与基类A不同于一个包的子类B2代码如下:
package net.mosang.prototype.protected2; import net.mosang.prototype.protectedtest.*; public class B2 extends A{ }
与基类A不同于一个包的访问类ClientP2代码如下:
package net.mosang.prototype.protected2; public class ClientP2 { public static void main(String[] args) { B2 b2 = new B2(); b2.methodA1(); b2.methodA2(); } }
运行结果:
可以看到,出现了“The method methodA2() from the type A is not visible”编译错误。显然,这是如下原因导致的:在访问类ClientP2创建的实例b2,调用的methodA2() 来自于基类,因为B2并没有重写methodA2() 方法,而ClientP2与基类A并不在同一个包中,因此protected修饰的methodA2()自然对ClientP2不可见,无法调用。
现在我们在B2中重写methodA2方法,并重新运行ClientP2:
package net.mosang.prototype.protected2; import net.mosang.prototype.protectedtest.*; public class B2 extends A{ @Override protected void methodA2() { System.out.println("这里重写了method2方法:"); // TODO Auto-generated method stub super.methodA2(); } }
可以看到,重写method2方法后,实例调用的实际上是重写后的方法,而不是基类的方法,顺利通过编译。
通过以上实验,我们就不难理解为什么会出现调用clone方法常见错误“The method clone() from the type Object is not visible”了。因此我们要想在原型模式中使用clone方法,做到以下两点就能保证不会出现以上错误:
1. 原型类重写clone方法。
2. 确保访问类与原型类在同一个包中(如重写保留默认protected修饰)。
>>