`
qingdaoguy
  • 浏览: 23618 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java中名字重用技术探究

阅读更多

Java中名字重用可分为以下几种情况:覆盖、隐藏、重载、遮蔽、遮掩,本文将就以上概念进行简述,并就需要注意的地方用例子的形式展现。

 

覆盖(override

一个实例方法可以覆盖(override)在其超类中可以访问到的具有相同签名的所有实例方法,从而使能了动态分派;也就是说,VM将基于实例的运行期类型来选择要调用的覆盖方法。覆盖是面向对象编程技术的基础,并且是唯一未被普遍劝阻的名字重用形式。

基本形式如下:

 

class  Father{

         Public void doSomething(){}

}

 

class Child extends Father{

         Public void doSomething(){}  //override Father.doSomething()

}

 

隐藏(hide

一个域、静态方法或成员类型可以分别隐藏(hide)在其超类中可以访问到的相同名字(对方法而言就是相同的方法签名)的所有域、静态方法或成员类型,隐藏一个成员将阻止其被继承。

基本形式如下:

class  Father{

         Public static void doSomething(){}

}

 

class Child extends Father{

         Private static void doSomething(){}  //hide Father.doSomething()

}

 

重载(overload

在某个类中的方法可以重载(overload)另一个方法,只要他们有相同的名字和不同的签名。由调用所指定的重载方法在编译器选定。

基本形式:

Class demo{

         Public static void doSomething(int  num){}   //int overloading

Public static void doSomething(String str){}    //string overloading

}

 

遮蔽(shadow

一个变量、方法或类型可以分别遮蔽(shadow)在一个闭合的文本范围内的具有相同名字的所有变量、方法或类型。如果一个实体被遮蔽了,那么用他的简单明是无法引用到的。

基本形式:

public Demo{

static String str = “ static String”;

public void doSomething(){

         String str = “shadow static String”; //shadows static String str

         System.out.println(str);  //打印shadow static String

}

}

 

遮蔽一般都是被劝阻的,但是一种通用的管用法虽然设计确依旧被使用,主要是大多数程序员认为这种风格带来的实惠超过其风险,该用法如下:

public Demo{

  String str = “ static String”;

public Deomg(String str){ //str参数遮蔽了成员变量str

         this.str = str;  

}

}

遮掩(obscure

一个变量可以遮掩与之有相同名字的一个类型,只要他们都在一个范围内:如果这个名字被用于变量与类型都被许可的范围,那么它将引用到变量上。相似的,一个变量或一个类型可以遮掩一个包。遮掩是唯一一个两种名字位于不同的名字空间的名字重用形式,这些名字空间包括:变量、包、方法或类型。如果一个类型或一个包被遮掩了,不能通过其简单名引用到它,除非是在这样一个上下文环境中,即语法只允许在其名字空间中出现一种名字。遵守命名规范就可以极大的消除产生遮掩的可能性。

形式:

public class Obscure{

         static String System; //obscures type java.lang.System

         public void doSomething(){

         System.out.println(“Hello world”); //编译错误,这里System指向String类型

}

}

 

以上仅仅限于概念,下面将举例做进一步说明。

首先看以下例子:

Example 1

package com.test;

class Father{

    public String name ="father";

    publicvoid printName(){

        System.out.println(this.getClass().getName()+" "+this.name);

    }

}

 

publicclass Child extends Father{

    public String name ="Child";   

    publicstaticvoid main(String[] args) {

        Child h = new Child();

        h.printName();

    }

}

 

以上程序会打印什么?想必只有这个name的值会让我们犹豫不决。

如果还是想不通,请记住以下准则:

父类的方法无法访问子类仅有的成员变量。

子类继承父类的方法,该方法可以调用的变量有两类:

1、被子类继承的成员变量:子类可访问可修改,方法中该成员变量值会随子类的修改而修改。

2、未被子类继承的成员变量:子类无法访问。

 

这个准则有点废话,这里就是为了强调作用。

 

根据以上原则,显然,name被子类child继承,那么子类中将name赋值为Child,那么结果应该是:com.test.Child Child了。如果运行程序,你发现打印结果并不是这样,而是com.test.Child Father。以上结果错了?

不是,注意,这里的子类中的name不是继承父类的,不能理解成父类的name被重新赋值。子类的name是子类特有的,与父类的name变量没有一毛钱关系,非但如此,子类的name导致子类无法继承父类的name变量。这就是隐藏的例子。

 

现在再看以上准则,printName方法打印的只可能是Father.name,子类的name成员对该方法是透明的。

 

另外,强调一点,父类、子类的name的访问权限任意组合下不影响该结果。

通过该例子,也可略窥:为何不鼓励使用隐藏。

 

Example 2

package com.test;

class Father{

    public String name ="father";  

    public String getName(){

        returnname;

    }

    publicvoid printName(){

        System.out.println(this.getClass().getName()+" "+this.getName());

    }

}

 

publicclass Child extends Father{

    public String name ="Child";   

    public String getName(){

        returnname;

    }

    publicstaticvoid main(String[] args) {

        Child h = new Child();

        h.printName();

    }

}

 

这个程序又该打印什么?

如果你判断错了,说明你混淆了一点:隐藏与覆盖的概念。再仔细看看以上概念,不难判断,这是覆盖的情况,即子类的getName方法覆盖了父类的getName方法,printName调用的getName方法只能是子类的,而非父类的,子类getName方法中调用的name只能是子类的name变量,因为按照example1的分析,子类未继承父类的name,所以结果脱口而出,没错,就是:com.test.Child Child

 

这里再废话两句:

1、覆盖与隐藏的一个区别在于:被覆盖的方法的访问权限要不大于用于覆盖的方法访问权限,而隐藏则无此限制。

2、  如果example1中要实现打印nameChildexamnple2给出了一个解决方案,另外,还有就是可以覆盖printName方法,当然,还有其他更多方法。

3、  Java中有final修饰符,final对方法和域而言,意味着完全不同的事:对于方法而言,final意味着该方法不能被覆盖(针对实例方法而言)或隐藏(针对静态方法而言);对于域而言,final意味着该域不能被赋值超过一次,所以是可以隐藏域的。

4、  可以将namegetName方法改成static,然后再仔细巩固一下这两个概念。

 

Example3

publicclass A {

 

    publicstaticvoid main(String[] args) {

        System.out.println(X.Y.Z);

    }

}

 

class X{

    staticclass Y{

        static String Z = "Black";

    }

   

    static C Y = new C();

}

 

class C{

    String Z = "White";

}

 

这个程序会打印什么?

这就是遮掩的例子,只要记住遮掩中一个重要准则:一个变量可以遮掩与之有相同名字的一个类型,只要他们都在一个范围内:如果这个名字被用于变量与类型都被许可的范围,那么它将引用到变量上。相似的,一个变量或一个类型可以遮掩一个包。根据该原则,我们就能轻易的得到结果。

 

Good luck!!

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics