Java多态,重写,重载

一、多态

  1.多态多态的三个条

        1.继承 
        2.要有方法的重写 
        3.父类引用指向子类对像

            
public class ObjectTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			Father fa=new Son();//条件3.父类引用指向子类对像
			fa.test();
	}
}
class Father{
	void test()
	{
		System.out.println("Father");	
	}
}
class Son extends Father{//条件1.继承
	void test()
	{
		System.out.println("Son");	//条件2.子类重写父类方法
	}
}
输出结果为:Son

  2.方法的重写

       重写的方法,子类方法的参数列表和返回值,必须与父类方法一致,才是重写。

举个不是重写的例子,更容易理解重写:

A.在Father 和Son类基础上定义A和B,A和B中的printl分别接收Father类和Son类参数

class A{
	void printl(Father f){
		
		System.out.println("A Father");
	}
}

class B extends A{
	
	void printl(Son son){
		System.out.println("B son");
	}
}

运行测试的方法:

public static void main(String[] args) {
		// TODO Auto-generated method stub
			Father fa=new Father();
			Son son=new Son();
			B b=new B();
			b.printl(fa);
			b.printl(son);
			
	}

运行结果:

A Father
B son

这个例子中,父类中带参数的方法printl,在子类中,改变了参数,printl参数改变为Father类的子类Son类。

这种情况下,实际上子类B的printl函数并没有重写父类的printl类,而是行了重载

所以:重写,子类方法的参数列表和返回值,必须与父类方法一致(包括参数是父类、和子类的关系也不行,而是同一类才可),才是重写

注:子类中方法 的参数是父类方法的参数的子类,这一情况是重载,而非重写,在理解多态的时候有用到。


3.父类引用指向子类对像

我们经常看到的一种情况。父类引用指向子类对象

Father fa=new Son();

同时,还有一种情况也是父类引用指向子类对象。

Father和Son类是继承关系,前面有写,A类仍然如下:

class A{
void printl(Father f){
		
		System.out.println("A Father");
	}
	
}

测试类:

public class ObjectTest {

	public static void main(String[] args) {
			Son son=new Son();
			A a=new A();
			a.printl(son);
	}
}

其中,A类定义的时候,printl方法的参数是Father类对象,而在测试类中,调用printl方法的时候,传递的是参数Father类的子类Son类的对象,这时候,也是父类引用指向子类对象的情况,即:

形式参数Father f指向实参子类引用Son son---》Father f=son

以上是对多态理解的准备工作

二.多态和重载混搭的情况下,多态的理解(重头戏)

1.多态下的重载

    例1. Father和Son仍是父类子类关系。A,B关系也未变,如下:

class A{
	void printl(Father f){
			System.out.println("A Father");
		}
}

class B extends A{
	void printl(Son son){
		System.out.println("B son");
	}
}

测试类:

public class ObjectTest {

	public static void main(String[] args) {
			Father fa=new Father();
			Son son=new Son();
			A a=new B();
			a.printl(fa);
			a.printl(son);
	}
}

可以看出,A a=new B();有继承,有父类引用指向子类对象,那么,形没形成多态呢?调用printl方法,分别传递父类Father对象,和子类Son对象,输出是什么呢?结果是:全都输出了“A Father”

A Father
A Father

为什么呢,因为不满足子类重写父类方法这个条件,未形成多态。也就是B类中的printl 没有重写A类中的printl,而是重载

B经过在 第一部分多态当中,关于重写的分析,可以知道,A类和B类,printl不是重写而是重载的条件。

重载的形成,等价于类A和类B的内容如下:

class A{
	void printl(Father f){
			System.out.println("A Father");
		}
}

class B {//注意这里去掉了继承A
	void printl(Father f){
		System.out.println("A Father");
	}
	void printl(Son son){
		System.out.println("B son");
	}
}

也就是此种B去掉了继承,和上边B有继承A是等价的。

A类根本就没什么改变,A的printl接收 参数 fa和接收son参数,都是调用了A中同一个方法。

此种情况对应的是,A和B未形成多态的情况(主要是B中未形成重写而是形成了方法的重载)

第二个例子,如果A和B形成了多态了呢?

A和B改造如下,主要是B:

class A{
	void printl(Father f){
			System.out.println("A Father");
		}
}

class B extends A{//满足了继承和chongxie
	
	void printl(Father f){
		System.out.println("B Father");
	}
	
	void printl(Son son){
		System.out.println("B son");
	}
}

测试类不变:

public class ObjectTest {


	public static void main(String[] args) {
			Father fa=new Father();
			Son son=new Son();
			A a=new B();
			a.printl(fa);
			a.printl(son);
	}
}

可以看出来,A a=new B();满足了多态的三个条件,形成了多态,这时候,结果如何呢?如下:

B Father
B Father
输出的第二个B father就容易不好理解了

第一种,先看a.printl(fa);这个好理解一些。

完完全全符合多态的变现,父类引用调用子类方法printl(Father f)

而第二种 a.printl(son);输出的结果,有点难以理解,为什么调用的仍然是printl(Father f)呢?

首先应该明确,B类中是哪个方法形成了多态?答案是只有B中的printl(Father f) 形成多态,而B中printl(Son son)和B中重写的printl(Father f)形成了重载,由于A中根本没有此方法,所以,printl(Son son)完完全全是B自己定义的方法,并没有重写A中的方法,所以,B中的printl(Son son)根本就没有形成多态,也就是A类的对象a,根本调用不了,也找不到B类中的这个B自己的方法 printl(Son son)。(只有形成多态,或者子类中只是简单继承,才能调用

(或者这么理解,假设B中printl(Son son)和B中定义的随便一个别的名字的方法如haha()没有本质区别,都是B自己定义的新方法,对A是不可见的),所以,a.printl(son); 调用的仍然是B中形成多态的printl(Father f)

所以,这里面,需要理解  重写和重载,再加上形成多态的三个条件。

例3.加深一步理解多态:

B类改为如下,都调用一下 形参类里面的方法 test(),其他类不变。

class Father{
	void test()
	{
		System.out.println("Father");	
	}
}
class Son extends Father{//条件1.继承
	void test()
	{
		System.out.println("Son");	//条件2.子类重写父类方法
	}
}
class A{
	void printl(Father f){
			System.out.println("A Father");
		}
}


class B extends A{
	
	void printl(Father f){
		System.out.println("B Father");
		f.test();
	}
	
	void printl(Son son){
		System.out.println("B son");
		son.test();
	}
}
测试类也不变
public class ObjectTest {

	public static void main(String[] args) {
			Father fa=new Father();
			Son son=new Son();
			A a=new B();
			a.printl(fa);
			a.printl(son);
	}
}

根据例2 的分析,都调用B中的printl(Father f)是没什么错的了,就是看B中printl(Father f)的 f.test()的输出内容。

首先,测试类中a.printl(fa) ,调用B中的 printl(Father f)先输出了"B Father",然后,形参f指向的是实参fa,也就是,在

 printl(Father f)中   形参f ----指向---->实参fa,这时候,f.test()调用fa.test()没什么问题,输出“Father”。

其次,测试类中a.printl(son);也是调用B中的 printl(Father f)先输出了"B Father"

然后呢,形参f指向的是实参son,形参f ----指向---->实参son,眼熟,这不是父类引用指向子类对象的吗?再看看Father和Son类,有继承,有重写,有父类引用指向子类对象,三个条件都满足,形成多态 f.test();调用子类方法,输出“Son”

所以,最终输出为

B Father
Father
B Father
Son
所以,识别重载和重写,辨别多态


三.重写

A.重写的定义

子类要重写父类方法,需要满足三个条件,

1.相同的方法名

2.相同的参数列表

3.相同的参数类型(参数类型为父类,子类关系也不行,必须相同,否则为重载了)

则,子类方法覆盖父类方法,

一些注意点:

1.构造函数是不能重写的

2.如果子类不能继承父类的方法,如:父类方法是final或者private,是不能重写的

B.重写@Override的作用,

重写时候,方法加上@Override注解和不加,都是重写,区别在于,加上@Override会检查父类是否有这个方法,是否构成了重写,否则编辑报错,

也就是父类没有方法test(),而在子类的test()加上@Override的注解,会编译报错。

如果没加的话,一不小心写错名字了,就构成重载了

四.重载overload


重载:重载的时候,方法名必须是一样的,可以参数类型,参数个数,参数顺序不一致,就构成了重载

特别要说明的是:返回值不同,无法判断是否构成重载,会报错。

可以想象一下,两个一模一样的函数,一个返回String,一个返回int,调用的时候,怎么判断调用哪个,编译的时候就报错了。

五.总结

1.父类和子类同名方法,参数也是父类和子类的方法,不不构成重写,是重载。

2.实参的子类对象,赋值给形参父类引用,也是父类引用指向子类对象

3.判断多态,一定需要判断一下,是否子类重写了父类方法

4.父类引用,调用不了子类对象特有的方法。只能调用子类对象继承的方法和多态重写的方法

5.构造函数不能重写

6.@Override 在编译阶段判断父类有没有此方法,是不是重写

7.返回值不同,不是重载,因为根本不知道要调用哪个函数,编译报错

8.重载:1.方法名相同 2.参数列表不同(参数类型,个数,顺序)

8.重写:1.方法名相同2.参数列表完全相同(参数类型,个数,顺序)




你可能感兴趣的:(java,面向对象)