重载(overload):同一个类中名字相同但参数列表不同的多个方法之间的关系。关于重载,是我们比较熟悉的了,最常见的就是运用在类的多个构造函数中,看一下 Java 帮助文档,就可以明白这一情况了。而在《Java 解惑》中,作者给出了下面一个谜题:public class Confusing { private Confusing(Object o) { System.out.println("Object"); } private Confusing(double[] dArray) { System.out.println("double array"); } public static void main(String[] args) { new Confusing(null); } } 问此时 main() 中将会输出什么?初初一看,并没有多分析就觉得应该是输出“Object”,虽然Java中的数组实际上也是引用类型,但毕竟Object 是所有类的最终父类,而且目前 JDK 就连参数中的基本数据类型变量也可以被自动想上转型成包装类而成为 Object 的子类。于是我保守一点地就认为参数 null 应该是匹配到 Object 那个重载方法去了。可是这答案是错的,JVM 对于重载方法的解析是这样的:先找出方法名匹配的所有可能的方法;然后根据传进来的形参再次筛选出可能的重载方法;最后才是在这些方法中匹配到一个最精确的一个方法。于是,上面的那个谜题就变成确定哪一个才是最精确这一点子上了。而关于如何判断最精确,有这样的机制:如果某个重载方法能够接收所有传递给另一个重载方法的实参类型,那么对于参数列表来看,显然后者至少是前者的子集,当然也就更精确了。回到谜题上来,Confusing(Object)可以接受任何传递给 Confusing(double[ ])的参数(任何数组引用最终能够都是 Object 对象),因此 main() 中的 null 应该是被 JVM 匹配到 Confusing(double[ ]) 中,也就有了与我所认为的结果相反的输出了。小结:这个谜题表明了我们在写重载方法时,最好是明确地区分出各个方法中的参数列表,不要让彼此之间有互相包含、模糊不清的关系。虽然重载是为了在相同名字的方法中传入实参,由 JVM 动态解析选择合适的方法,但有时也很容易陷入这种方便背后所带来的地雷区当中。其中一种可行的办法就是,提供不同的方法名。但是构造函数的名字一定得相同的啊?郑州建站推广 网站策划 网站建设 网站开发 SEO优化 网站推广 SEO 网站SEO 清软国际 东方清软 国信清软重写(override):父类中的实例方法被其子类重新实现。既然是实例方法,那就是非 static 修饰的了,否则就是 static 静态方法了,那叫做类方法。在我看来,正是重写这一机制的存在,才为多态机制提供了基础。或许 implements (实现)一个 interface (接口)中所声明的方法也能成为重写,因为 interface 的一部分存在原因也是为了多态。对于重写,在《Java 解惑》中有下面这个谜题让我明白:绝对不能在构造函数中调用可能会被子类重写的方法。class Point { protected final int x, y; private final String name; Point(int x, int y) { this.x = x; this.y = y; name = makeName(); } protected String makeName() { return "[" x "," y "]"; } public final String toString() { return name; } } public class ColorPoint extends Point { private final String color; ColorPoint(int x, int y, String color) { super(x, y); this.color = color; } protected String makeName() { return super.makeName() ":" color; } public static void main(String[] args) { System.out.println(new ColorPoint(4, 2, "purple")); } } 此时程序运行结果并不是我们所想的 [4,2]:purple ,而是 [4,2]:null 。为什么会这样?看看下面用流程标号注释过的代码,就能理解了。class Point { protected final int x, y; private final String name; Point(int x, int y) { this.x = x; this.y = y; name = makeName();// 3. 由于被子类重写过的makeName() } protected String makeName() { return "[" x "," y "]"; } public final String toString() { return name; } } public class ColorPoint extends Point { private final String color; ColorPoint(int x, int y, String color) { super(x, y); // 2. 调用Point父类构造函数 this.color = color; // 5. 初始化 color ,可是已经太晚了... } protected String makeName() { // 4. 问题来了:它在子类构造函数之前调用了 // 而此时的 color 是 null 的啊!!! return super.makeName() ":" color; } public static void main(String[] args) { // 1. 调用ColorPoint子类构造函数 System.out.println(new ColorPoint(4, 2, "purple")); } } 思路很清晰了,ColorPoint 子类中的构造函数中的 this.color = color; 还未被执行到就将 null 作为 String color 的值了。正是因为这种来来回回的调用使得程序变得不正常了,在我看来,有那么一点类似于“回调”的意思。郑州建站推广 网站策划 网站建设网站开发 SEO优化 网站推广 SEO 网站SEO 清软国际 东方清软国信清软要去除这种代码结构的不合理,最好还是把 Point 父类构造函数中调用 makeName() 方法一句去掉,然后在 toString 中判断并调用 makeName() 来为 name 初始化,如下:小结:重写对于多态固然重要,但是设计出不正确的代码结构的话,原本想要的多态就会被扭曲甚至造成反效果。于是,绝对不要在构造函数中调用可能会被子类重写的方法。