1. 继承的基本语法
java语言中使用extends关键字表示继承。子类继承父类的哪些东西呢?分两种情况:
(1)如果子类和父类位于同一个包时:子类继承父类中public、protected、default访问级别的成员变量和成员方法;
(2)如果父类和子类不在同一个包时:子类继承父类中public、protected访问级别的成员变量和成员方法。
java不支持多继承,一个类只能继承一个类,所有java类都直接或者间接继承java.lang.Object类。假如在定义一个类时,没有使用extends关键字,那么这个类直接继承Object类。
2. 方法重载(Overload)
有时候,类的一种功能有多种实现方式,到底采用哪种实现方式,取决于调用者给定的参数。
对于类的方法(包括从父类继承来的方法),如果有两个方法的方法名相同,但参数不一样,那么一个方法是另一个方法的重载方法。
重载方法必须满足以下条件:
- 方法名相同
- 方法的参数类型、顺序、个数至少有一个不一样
- 方法的返回类型可以不相同
- 方法的修饰符可以不相同
在同一个类中不允许定义两个方法,方法名和参数都完全相同,即使访问权限和方法返回值类型不一致,编译还会出错。因为java虚拟机在运行时无法决定到底执行哪个方法。构造方法、类方法、final方法均可以重载。
class Dog{}
class Cat{}
class Fish{}
class Animal{
public void eat(Cat cat)
{
System.out.println("我是小猫,我在吃鱼");
}
public void eat(Dog dog)
{
System.out.println("我是小狗,我在吃肉");
}
public void eat(Fish fish)
{
System.out.println("我是小鱼,我在吃猫");
}
public static void main(String args[])
{
Animal animal=new Animal();
Dog dog=new Dog();
Cat cat=new Cat();
Fish fish=new Fish();
/*
以下三个都是执行animal对象的eat方法,但具体执行哪个方法,取决于方法的参数。
*/
animal.eat(dog);
animal.eat(cat);
animal.eat(fish);
}
}
3. 方法覆盖(Override)
如果在子类中定义的一个方法,其名称、返回类型、参数刚好与父类中的一个方法完全匹配。那么子类方法覆盖了父类方法。
方法覆盖必须满足以下多种约束。
-
子类方法的名称、返回值类型、参数必须完全与父类方法的名称、返回类型和参数一致,否则编译错误;但是允许子类覆盖父类的方法,然后在子类中再对该方法进行重载。如下代码是合法的。
class Fish extends Animal{ public void eat(Fish fish)//覆盖父类的eat(Fish fish)方法 { System.out.println("我是小鱼儿子,我在吃猫"); } public String eat()//重载子类中的eat(Fish fish)方法 { return null; } } class Animal{ public void eat(Fish fish) { System.out.println("我是小鱼爸爸,我在吃猫"); } }
-
子类方法不能缩小父类方法的访问权限,否则编译错误。
Base base=new Sub(); base.method();
上述代码中定义了一个Base类型的引用变量base,引用Sub类型的实例。base调用method方法时,会优先执行Sub类中的method方法,一旦Sub类中没有该方法,再执行父类中的method方法。如果子类存在method方法,且方法的访问权限为private,可能会导致java虚拟机无法访问该方法。
-
子类方法不能抛出比父类更多的异常。子类方法抛出的异常必须和父类方法抛出的异常相同,或者子类方法抛出的异常类是父类方法抛出的异常类的子类。否则,会编译出错。之所以这样设计,是防止调用子类方法时抛出更多的异常,无法被捕获,导致程序中断。
-
方法覆盖只存在于子类和父类(或者直接父类和间接父类)中,同一个类中的方法只能被重载,不能被覆盖。
-
父类的静态方法不能被子类覆盖为非静态方法。因为静态方法属于类,而不是属于这个类的某个实例对象,所以父类的静态方法,在被子类继承之后,也属于子类,所以子类中不可以再写一个非静态方法覆盖原有静态方法。否则编译出错。
-
父类的非静态方法不能被子类覆盖为静态方法。否则编译出错。
-
子类可以定义和父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。编译时,子类中定义的静态方法也必须满足和方法覆盖一样的约束:参数一致、返回值一致、方法名一致、不能缩小父类方法的访问权限、不能抛出更多的异常。
public class Base{ static int method(int a)throws BaseException{ return 0; } } public class Sub extends Base{ public static int method(int a)throws SubException{ return 1; } }
子类覆盖父类的实例方法和子类隐藏父类的静态方法,两者区别在于:运行时,java虚拟机把静态方法和所属的类绑定,实例方法和所属的实例绑定。
class Test332 extends Animal{ void eat() { System.out.println("子类实例方法"); } static void walk() { System.out.println("子类静态方法"); } public static void main(String args[]) { Animal a=new Test332(); a.eat();//子类实例方法 a.walk();//父类静态方法 Test332 b=new Test332(); b.eat();//子类实例方法 b.walk();//子类静态方法 } } class Animal{ void eat() { System.out.println("父类实例方法"); } static void walk() { System.out.println("父类静态方法"); } }
引用变量a、b都是引用子类的实例,在执行实例方法eat时,都是执行子类定义的eat方法,因为父类的eat方法被覆盖。但a被声明为父类类型的引用变量,所以在执行静态方法walk时,执行的是父类中定义的方法,而b被声明为子类类型的引用变量,所以执行子类静态方法。
-
父类的私有方法不能被子类覆盖。子类方法覆盖父类方法的前提是:子类必须继承父类的方法,而private方法只能被当前类访问。
class Test332 extends Animal{ void eat() { System.out.println("子类实例方法"); } public static void main(String args[]) { Test332 b=new Test332(); b.eat();//子类实例方法 b.walk();//父类实例方法 } } class Animal{ private void eat() { System.out.println("父类实例方法"); } void walk() { eat(); } }
引用变量b调用walk方法时,会执行父类中的eat方法,因为父类eat方法时private的,未被子类覆盖。如果将private去掉,则引用变量b调用walk方法时,会执行子类中的eat方法,因为父类eat方法已经被覆盖。
-
父类的抽象方法可以被子类通过两种途径覆盖。一是子类实现父类的抽象方法,二是子类重新声明父类的抽象方法。且父类的非抽象方法可以被覆盖为抽象方法。
4. 方法覆盖和方法重载的异同
相同点:
- 都要求方法同名
- 都可以用于抽象方法和非抽象方法之间
不同点:
- 方法覆盖要求返回值类型、参数都一致;方法重载对返回值类型没要求,却要求参数一定不能一致
- 方法覆盖只能用于子类覆盖父类的方法;方法重载则用于同一个类的所有方法(包括从父类继承的方法)
- 方法覆盖对访问权限和抛出的异常有特殊要求;方法重载则没有要求
- 父类的一个方法只能被子类覆盖一次,而方法重载可以在一个类中进行多次