论文导读:用形如Student()的构造方法,可是从代码中明显看到并没有定义该方法,这就回到了上面提到的默认构造方法的问题。然而我们确切地知道子类必须要调用父类的构造方法(实际上,子类对象都包含了一个父类的对象,该对象必须有父类的构造方法实施创建),所以此时必须有一个父类的构造方法站出来,可惜失败了,所以错误产生了。对于这个推
源于:****职称论文http://www.dfholiday.com
得到了完美的体现。不足的是我们创建的实例是没有个性的,其名称为null,年龄为0,这是因为使用的是默认构造方法。形如public Person(){},这种结果可能会令人失望,但是后面的探讨将告诉我们它是多么重要。为了说明这个问题,开始我们的第一次修改,给Person类增加一个形如默认构造方法的构造方法,即第2个版本,按照引子中的约定,相同部分不再显示: /*@ 2 @*/ public Person(){ System.out.println("public Person()");}/*#public Person() 我是null,今年0岁。#*/ 结果告诉我们这样一个事实,父类(Person)的构造方法被调用了,这是怎么回事?其实子类(Student)并没有“显式的”构造方法,所以系统会默默地提供一个形如public Student(){}的构造方法,而这个构造方法会作出一个举动:默默地调用父类的构造方法。这个结论也许并不是什么新奇的结论,但是我开始并没有意识到或者认识的不够彻底,对后续的研究和探讨带了一定的羁绊。与此同时,正是对这个看似很简单的细节认识清楚以后,才有了本文的诞生,由此可见,关注细节是至关重要的。 探讨的序幕拉开了,一个很关键的问题来了:对于父类的构造方法的调用真的如此简单么? 如果将第2个版本的构造方法修改如下,注意是修改,而不是重载: /*@ 3 @*/ public Person(String name, int age){ this.name = name;this.age = age; System.out.println("public Person()");} 这会产生什么结果呢?结果是没有结果,因为等待我们的是编译错误。引用简化后的错误提示——无法将类 Person中的构造器(就是构造方法)Person应用到给定类型,需要: String,int找到: 没有参数。这是JDK给我们的帮助信息,善于利用调试工具,才是我们学习计算机语言的正确之路。错误提示说明了一个事实:如果你给出了任何一个构造方法,系统将不再提供默认的构造方法(这个结论是我经过细致入微的编码和思考得到的,虽然在一些书籍里面都有提及,但是”看到别人的“和”自己想到的“有很大的不同,多一些”自己想到的“是学习道路上最迷人的风景),因为你提供的方法在功能上要优于系统的(系统的是空的方法)。此时Student类面临创建对象的任务,调用自己默认的构造方法,而默认的构造方法势必调用父类的构造方法,这种机制是自动执行的,凡是自动执行的无外乎引起两种结果:一种是方便,另一种是隐蔽。抛开其便利性不说,追究其隐蔽(也正是本文的重要目的之一),我们从如何调用父类构造方法入手。 回头再次审视JDK给我们的错误提示不难发现编译器需要的是形如Student(String name, int age)的构造方法,但是却找到了形如Student()的构造方法,这是从编译器的提示中得到的解释,看似没有问题。但是在这里,我想再次提到一个细节:按照一般的思路,当我们遇到这种错误的时候,会从主函数即从new Student().introduction()这句话入手,当我们看到这句话的时候,首先想到的是调用形如Student()的构造方法,可是从代码中明显看到并没有定义该方法,这就回到了上面提到的默认构造方法的问题。然而我们确切地知道子类必须要调用父类的构造方法(实际上,子类对象都包含了一个父类的对象,该对象必须有父类的构造方法实施创建),所以此时必须有一个父类的构造方法站出来,可惜失败了,所以错误产生了。对于这个推理,大家觉得如何?初步看来,有些道理,但是经不起更为周详的推敲啊! 首先,我们可以做一个最为简单的事情——将new Student().introduction()这句话注释掉,即: 这是本文程序的第4个版本,令人惊奇的是,注释掉这句话,还是会产生同样的错误?!上面的推理正是从这句话开始的,这不由得让我们开始怀疑,到底是怎么了?其实这个涉及到更为深层的议题——类的加载问题,该文不打算深入探讨。这就是计算机语言的迷人之处,我们如果经常做这样的推理和思考,不仅可以深入地研究问题,将相关的知识做一个深入的认知,同时也能发现其中的乐趣,对于这种乐趣,个人觉得是了不起的东西。 其次,如上所述,根据JDK的提示,我们可以轻易得出两个假设:一是系统确实给Student类提供了默认的构造方法,二是系统不需要这个默认的方法,而是需要形如Student(String name, int age){}的方法。这又是一个推理,这个推理如何呢?能够经得住推敲么?为此我们产生了第5个版本: /*@ 5 @*/ class Student extends Person { Student(){} }// Student定义完