继承
Java的继承具有单继承的特点,每个子类只有一个直接父类。但是可以有无限多个间接父类注意一点:子类能继承过来啥?子类只能从被扩展的父类获得成员变量、方法和内部类(包括内部接口、枚举),不能获得构造器和初始化块。
方法重写:
子类扩展了父类,子类是一个特殊的父类。大部分时候,子类总是以父类为基础,额外增加新的成员变量和方法。
但有一种情况例外:子类需要重写父类的方法(Override)。例如鸟类都包含了飞翔方法,其中鸵鸟是一种特殊的鸟类,因此鸵鸟应该是鸟的子类,因此它也将从鸟类获得飞翔方法,但这个飞翔方法明显不适合鸵鸟,为此,鸵鸟需要重写鸟类的方法。
方法的重写要遵循“两同两小一大”规则,“两同”即方法名相同、形参列表相同;
“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
“一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。尤其需要指出的是,覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
当子类重写了父类的方法,但是该子类中又需要使用父类中的这个方法,怎么办?
首先当子类覆盖了父类方法后,如果需要在子类方法中调用父类中被覆盖的方法,则可以使用super(被覆盖的是 实例方法)或者父类类名(被覆盖的是类方法)作为调用者来调用父类中被覆盖的方法。
如果父类方法具有private访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也就是无法重写该方法。如果子类中定义了一个与父类private方法具有相同的方法名、相同的形参列表、相同的返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。
重载overload和重写voerride比较意义不大:
因为重载主要发生在同一个类的多个同名方法之间,而重写发生在子类和父类的同名方法之间。它们之间的联系很少,除二者都是发生在方法之间,并要求方法名相同之外,没有太大的相似之处。
当然,父类方法和子类方法之间也可能发生重载,因为子类会获得父类方法,如果子类定义了 一个与父类方法有相同的方法名,但参数列表不同的方法,就会形成父类方法和子类方法的重载。
Super:
super是Java提供的一个关键字,super用于限定该对象调用它从父类继承得到的实例变量或方法。正如this不能出现在static修饰的方法中一样,super也不能出现在static修饰的方法中。static修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象,因而super限定也就失去了意义。
如果在构造器中使用super,则super用于限定该构造器初始化的是该对象从父类继承得到的实例变量,而不是该类自己定义的实例变量。
如果子类定义了和父类同名的实例变量,则会发生子类实例变量隐藏父类实例变量的情形。在正常情况下,子类里定义的方法直接访问该实例变量默认会访问到子类中定义的实例变量,无法访问到父类中被隐藏的实例变量。在子类定义的实例方法中可以通过super来访问父类中被隐藏的实例变量。
当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存,也会为它从父类继承得到的所有实例变量分配内存,即使子类定义了与父类中同名的实例变量。也就是说,当系统创建一个Java对象时,如果该Java类有两个父类(一个直接父类A,一个间接父类B),假设A类中定义了2个实例变量,B类中定义了3个 实例变量,当前类中定义了2个实例变量,那么这个Java对象将会保存2+3+2个实例变量。
如果在子类里定义了与父类中已有变量同名的变量,那么子类中定义的变量会隐藏父类中定义的变量。注意不是完全覆盖,因此系统在创建子类对象时,依然会为父类中定义的、被隐藏的变量分配内存空间。
注意:
为了在子类方法中访问父类中定义的、被隐藏的实例变量,或为了在子类方法中调用父类中定义的、被覆盖(Override)的方法,可以通过super作为限定来调用这些实例变量和实例方法。因为子类中定义与父类中同名的实例变量并不会完全覆盖父类中定义的实例变量,它只是简单地隐藏了父类中的实例变量,所以会出现如下特殊的情形。
super最重要的作用是调用父类的构造器:
子类不会获得父类的构造器,但子类构造器里可以调用父类构造器的初始化代码,类似于前面所介绍的一个构造器调用另一个重载的构造器,所以super用法类似于this。在一个构造器中调用另一个重载的构造器使用this调用来完成,必须放在构造器第一行,在子类构造器中调用父类构造器使用super调用来完成,也必须放在第一行。所以this调用和super调用不会同时出现。
不管是否使用super调用来执行父类构造器的初始化代码,子类构造器总会调用父类构造器一次。子类构造器调用父类构造器分如下几种情况。
1.子类构造器执行体的第一行使用super显式调用父类构造器,系统将根据super调用里传入的实参列表调用父类对应的构造器
2.子类构造器执行体的第一行代码使用this显式调用本类中重载的构造器,系统将根据this调用里传入的实参列表调用本类中的另一个构造器。执行本类中另一个构造器时也会先调用父类构造器。
3.子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。
不管上面哪种情况,当调用子类构造器来初始化子类对象时,父类构造器总会在子类构造器之前执行;不仅如此,执行父类构造器时,系统会再次上溯执行其父类构造器,依此类推,创建任何Java对象,最先执行的总是java.lang.Object类的构造器。