Java基础——继承、多态

1. 面向对象的特征

我们都知道,java是面向对象的语言,那么面向对象的有哪些特征呢?

(1)抽象:将客观事物的共性抽象出来,并将这些属性归为一个类。

        包括两个方面:过程抽象数据抽象

(2)继承:一个新类可以从现有的类中派生。

(3)封装:将客观事物抽象成类,每个类对自身的数据和方法实现保护。

(4)多态:主要有两种表现方式:方法的重载方法的覆盖

抽象和封装比较简单,我们重点讲解继承和多态。

2.什么是继承?

通过继承,子类可以使用父类中的一些成员变量与方法,提供代码的复用性。

使用格式:class 子类名 extends 父类名

特性:(1)不支持多重继承,子类至多只能有一个父类,但可以通过实现多个接口来达到多重继承的目的(接口支持多重继承)

(2)子类只能继承父类的非私有(public 和 protected)成员变量和方法

(3)当子类中定义的成员变量和父类定义的成员变量同名时,子类的成员变量会覆盖父类的成员变量,而不会继承。

(4)当子类中的方法与父类中的方法有相同的函数签名(相同方法名,相同参数个数和类型)时,子类将会覆盖父类的方法,不会继承。

3. 什么是多态? 

它表示当同一个操作作用在不同对象时,会有不同的语义,从而产生不同的结果。

(1)方法的重载(overload):重载是指同一个类中有多个同名的方法,但是这些方法有不同的参数,因此在编译时就可以确定到底调用哪个方法,它是一种编译时多态

(2)方法的覆盖 (override):子类可以覆盖父类的方法,因此同样的方法会在父类与子类中有着不同的表现形式。因为父类引用不仅能指向父类对象,也能指向子类对象。所以,在执行期间(非编译期间)需要判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。因此,也方法的覆盖 (override)被称为运行时多态

运行时多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。

class Base{
	//父类的构造方法
	public Base()
	{
		g();
	}
	public void f()
	{
		System.out.println("Base f()");
	}
	public void g()
	{
		System.out.println("Base g()");
	}
}

class Derived extends Base{
	//覆盖了父类的f()方法
	public void f()
	{
		System.out.println("Derived f()");
	}
	//覆盖了父类的g()方法
	public void g()
	{
		System.out.println("Derived g()");
	}
}

public class Test{
	public static void main(String[] args) {
		Base b = new Derived(); //父类的引用指向了子类的实例化对象
		b.f();
		b.g();
	}
}

父类提供了无参数的构造函数,因此编译器默认调用父类的无参数构造函数。在子类中覆盖了父类的 f()方法和 g()方法,因此运行时实例化对象调用子类中相应的方法.

运行结果:

Derived g()

Derived f()

Derived g()

只有类中的方法才有多态的概念,类中的成员变量没有多态的概念。 

成员变量的值并不取决于创建对象的类型,而是取决于所定义变量的类型

class Base{
	public int i =1;
}

class Derived extends Base{
	public int i = 2;
}

public class Test {
    public static void main(String[] args) {
		Base b = new Derived();   //变量类型为Base
		System.out.println(b.i);
	}
}

输出结果:1

i 的值并不取决于创建对象的类型,而取决于定义变量的类型。变量b的类型为Base ,所以输出为1,这是在编译时就能确定的,也不具备多态性。

Base b = new Derived(); 改为Derived b = new Derived();则输出为2

总结:

重载的注意点:

(1)重载是通过不同的方法参数来区分的,例如不同的参数个数、不同的参数类型、不同的参数顺序。不能通过方法的访问权限、返回值类型和抛出的异常类型来进行重载。

(2)对于继承来说,如果父类方法的访问权限为private,那么不能在子类中对其重载。如果在子类中也定义了一个同名的函数,这只是一个新的方法,不会达到重载的效果。(继承只能继承父类的非私有成员)

覆盖的注意点:

(1)子类中的覆盖方法必须和父类中被覆盖方法有相同的函数名、参数、返回值。

(2)子类的覆盖方法所抛出的异常必须和父类被覆盖的方法所抛出的异常一致。

(3)父类中被覆盖的方法不能为private,否则其子类只是定义了一个方法,并没有对其覆盖。

4.重载和覆盖的区别

 (1)覆盖是子类和父类之间的关系,是垂直的关系;重载是同一个类中方法之间的关系,是水平关系。

(2)覆盖要求参数列表相同;重载要求参数列表不同

(3)覆盖关系中,调用方法体是根据对象的类型来决定的,而重载关系是根据调用时的实参数表与形参数表来选择方法体的。

如下代码的运行结果是什么? 

答案:编译报错。因为函数是不能以返回值来区分的,虽然父类与子类中的函数有着不同的返回值,但它们有着相同的方法签名,因此,编译器无法区分。


class Super{
	public int f()
	{
		return 1;
	}
}

public class SubClass extends Super{
	public float f()                //这里会报错
	{
		return 2f;
	}
	public static void main(String[] args) {
		Super s = new SubClass();
	    System.out.println(s.f());
	}
}

猜你喜欢

转载自blog.csdn.net/zhm1563550235/article/details/85212000