clone权限问题探究

By heiry on 2019-08-06 [ in 技术 ]

java clone方法权限分析

根类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();
  }
}

protected权限深入探究

可以看到,重写method2方法后,实例调用的实际上是重写后的方法,而不是基类的方法,顺利通过编译。

通过以上实验,我们就不难理解为什么会出现调用clone方法常见错误“The method clone() from the type Object is not visible”了。因此我们要想在原型模式中使用clone方法,做到以下两点就能保证不会出现以上错误:

1. 原型类重写clone方法。

2. 确保访问类与原型类在同一个包中(如重写保留默认protected修饰)。

 >>



© 2009-2024 MOSANG.NET DESIGNED BY HEIRY