JAVA类继承过程中其成员的一些问题
一、构造函数的问题:
构造函数不能继承。子类的构造函数可以通过super关键字显式调用父类中的构造函数。如果子类中的构造函数没有显式调用父类中的构造函数,编译器就会自动在子类的构造函数中调用父类中参数为空的构造函数。于是,当父类中没有参数为空的构造函数,而子类中又没有显示调用父类的其他构造函数,编译时就会报错。这一点需要特别注意。当父类中没有定义任何构造函数时,编译器就会为它指定一个参数为空的默认的构造函数;如果父类中定义了构造函数,那么编译器就不会为它指定一个参数为空的默认构造函数了。因此,如果某个类有可能成为其他类的父类,为了避免发生不必要的编译错误,最好为它编写一个参数为空的构造函数。
eg1.父类Sup中没有定义构造函数,编译程序将为它指定一个参数为空的默认构造函数。子类Sub中也没有定义构造函数,编译程序也会为它指定一个参数为空的默认的构造函数,并且会在这个默认的构造函数中调用父类的参数为空的构造函数。
public
class
Sub
extends
Sup
{
//子类中没有定义构造函数

public static void main(String args[])
{
Sub sub=new Sub();
}
}

class
Sup
{
//父类中没有定义构造函数
}
一、构造函数的问题:
构造函数不能继承。子类的构造函数可以通过super关键字显式调用父类中的构造函数。如果子类中的构造函数没有显式调用父类中的构造函数,编译器就会自动在子类的构造函数中调用父类中参数为空的构造函数。于是,当父类中没有参数为空的构造函数,而子类中又没有显示调用父类的其他构造函数,编译时就会报错。这一点需要特别注意。当父类中没有定义任何构造函数时,编译器就会为它指定一个参数为空的默认的构造函数;如果父类中定义了构造函数,那么编译器就不会为它指定一个参数为空的默认构造函数了。因此,如果某个类有可能成为其他类的父类,为了避免发生不必要的编译错误,最好为它编写一个参数为空的构造函数。
eg1.父类Sup中没有定义构造函数,编译程序将为它指定一个参数为空的默认构造函数。子类Sub中也没有定义构造函数,编译程序也会为它指定一个参数为空的默认的构造函数,并且会在这个默认的构造函数中调用父类的参数为空的构造函数。















eg2.父类Sup中没有定义构造函数,编译程序将为它指定一个参数为空的默认构造函数。子类定义了一个带整型参数的构造函数,在这个构造函数中子类没有显式调用父类的构造函数,所以编译器为在它里面调用父类中参数为空的构造函数。





















eg3. 父类中定义了一个带整型参数的构造函数,因此编译器不再为它指定参数为空的默认的构造函数。子类中也定义了一个带整型参数的构造函数。编译时,编译器将试图在子类的构造函数中调用父类的参数为空的构造函数,但是父类中没有定义参数为空的构造函数,所以编译程序将会报错。排错的方法时在子类的构造函数中显示调用父类的构造函数,或者在父类中添加一个带空参数的构造函数。




























排错方法1:



























排错方法2:



































二、类方法和实例方法在继承过程中的问题:
首相要明确一个概念,JAVA中通过什么来区别不同的成员——答案是标记。所谓标记,对于方法而言是指方法的名字、参数的数量和参数的类型;对于变量而言是指变量的名称。注意,标记不包括方法的返回值和变量的类型。
如果子类的实例方法和父类的实例方法具有相同的标记,子类的方法将覆盖父类的方法。但是如果子类的方法和父类的方法具有相同的标记,但是具有不同的返回值类型,编译程序将会报错。排错的方法是修改子类中这个方法的返回值类型,使它父类中相同标记的方法的返回值一样。






















排错:






















同样如果子类的类方法和父类的类方法具有相同的标记,那么子类的类方法将隐藏父类的类方法。注意这里不叫覆盖,而叫隐藏,两者的区别将在后面讨论。同样,如果子类的类方法和父类的类方法具有相同的标记和不同的返回值,编译程序也会报错。解决的问题的方法同实例方法一样。
但是实例方法不能覆盖类方法,类方法也不能隐藏实例方法,否则编译程序就会报错。
实例方法不能覆盖类方法(编译时报错):






















类方法不能隐藏实例方法(编译时报错):






















覆盖和隐藏的区别。对于隐藏的方法,运行时系统调用当前对象引用的编译时类型中定义的方法;对于覆盖的方法,运行时系统调用当前对象引用运行时类型中定义的方法。一个是调用编译时类型中定义的方法,一个是调用运行时类型中定义的方法。通过下面这个例子可以直观的看出两者之间的差别。








































Earth 类覆盖了Planet类的override方法,并隐藏了它的hide方法。在main方法中创建了一个Earth的实例,并将此实例转换为一个 Planet的引用。然后通过Planet引用调用override方法和hide方法。hide方法是类方法,因此根据引用的类来决定调用哪个方法,所以planet.hide()调用的是Planet的类方法hide。override方法是实例方法,因此调用的是运行时的实例中的方法,因为 planet引用的是Earth的实例,因此planet.override()调用的是Earth类中实例方法override。
JAVA正是通过实例方法的这种覆盖关系来实现多态的目的。
三、类的变量在继承过程中的问题:
如果子类中的变量如果和父类中的变量具有相同的标记——即相同的名字——那么子类中的变量一就会隐藏父类中的变量。不过他们的类型是什么,也不管他们是类变量还是实例变量。只要他们具有相同的名字。

















同子类中的类方法隐藏父类中的类方法一样,当子类中的变量覆盖父类中的变量的时候,到底使用哪个变量,要根据引用的类型来确定。
下面是一个类成员继承关系的综合例子:


























































