名称重用有时会导致编译错误,但有时却不会,导致发生难以发现的问题,所以一定要注意。
下面将所有的名称重用情况列出,并附带例子,在日常的开发中一定还会遇到类似的问题,希望能有所帮助。
这部分涉及到的静态绑定和动态绑定问题,理解后对于泛型能有更好的理解,相关笔记请看文末。
-
重写(Overriding)
子类可以重写其父类中可访问到的具有相同签名的所有成员方法[JLS 8.4.8.1]。
JVM将会根据实例的运行期类型(对象)来选择要调用的重写方法[JLS 15.12.4.4]。
静态方法是无法重写的,因为重写的要求是要动态绑定(对象),而静态方法是静态绑定(类)。final方法也是无法重写的,因为使用final的目的就是防止其被重写。注意,这里说的搜是方法。
在 JDK 1.5 及以上中使用 @Override 注解,防止将需要重写的方法错写为重载。class Base { public void f() {} } class Derived extends Base { @Override public void f() {} // overrrides Base.f() }
-
重载(Overloading)
在一个类中,可以以相同的名称和不同的签名来构成重载,重载方法的调用是在编译阶段选定的(类)。
签名指的是方法名及参数列表,返回类型不是签名的一部分,所以返回类型不同但是签名相同的重载是无法编译通过的。
尽量要确保重载的方法在相同的签名上有相同的行为。class CircuitBreaker { public void f(int i) { } // int overloading public void f(String s) { } // String overloading }
-
隐藏(Hiding)
一个成员变量、静态方法或成员类型可分别隐藏其父类中可访问到的具有相同名称(方法而言是方法签名)的所有成员变量、静态方法或成员类型,隐藏的成员不会被继承。
不要去重用 java.lang 包下的类型名。
补充一下,内部成员类的参数类型(泛型)可以隐藏外部类的参数类型(这个问题在IDEA中已经有温馨提示)。class Base { public static String fStr = "base"; public static void f() {System.out.println(fStr); } static class BaseType { public static String type = "base"; } } class Derived extends Base { private static String fStr = "derived"; //hides Base.fStr, even can change the access! public static void f() { System.out.println(fStr); } // hides Base.f() //hides Base.BaseType static class BaseType { public static String type = "derived"; } }
-
遮蔽(Shadowing)
一个变量、方法或类型可以分别遮蔽在类中具有相同名称的所有成员变量、成员方法或成员类型。如果一个实体被遮蔽了,根据实体的不同,有时候根本无法引用到。[JLS 6.3.1]class WhoKnows { static String sentence = "I don’t know."; public static void main(String[] args) { String sentence = "I know!"; // shadows static field System.out.println(sentence); // prints local variable } } //最常见的遮蔽,不是没有风险,但为惯用法。 class Belt { private final int size; public Belt(int size) { // Parameter shadows Belt.size this.size = size; } }
-
遮掩(Obscuring)
一个变量名可以遮掩具有相同名称的一个类型(类),只要他们属于同一个作用域。类似的,一个变量或一个引用可以遮掩一个包。遮掩是惟一一种两个名称位于不同的名称空间的名称重用形式,包括:变量、方法、包或类型。
按照命名规范来进行命名可以有效防止遮掩的发生。public class Obscure { static String System; // Obscures type java.lang.System public static void main(String[] args) { // Next line won’t compile: System refers to static field System.out.println("hello, obscure world!"); } }