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编译错误

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 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 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;
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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());
}
}
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()); } }
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方法的情况下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 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 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;
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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());
}
}
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()); } }
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代码如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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");
}
}
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"); } }
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代码如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package net.mosang.prototype.protectedtest;
public class B extends A{
}
package net.mosang.prototype.protectedtest; public class B extends A{ }
package net.mosang.prototype.protectedtest;

public class B extends A{

}

与基类A同一个包的访问类ClientP1代码如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
}
}
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 } }
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代码如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package net.mosang.prototype.protected2;
import net.mosang.prototype.protectedtest.*;
public class B2 extends A{
}
package net.mosang.prototype.protected2; import net.mosang.prototype.protectedtest.*; public class B2 extends A{ }
package net.mosang.prototype.protected2;
import net.mosang.prototype.protectedtest.*;
public class B2 extends A{

}

与基类A不同于一个包的访问类ClientP2代码如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package net.mosang.prototype.protected2;
public class ClientP2 {
public static void main(String[] args) {
B2 b2 = new B2();
b2.methodA1();
b2.methodA2();
}
}
package net.mosang.prototype.protected2; public class ClientP2 { public static void main(String[] args) { B2 b2 = new B2(); b2.methodA1(); b2.methodA2(); } }
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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();
}
}
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(); } }
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