(基础)java中的多态

多态性使得Java语言有很强的灵活性

很多设计模式都是基于多态的特点发展出来的

 

多态

父类(抽象的)引用指向子类(具体的)对象

Animal a = new Person();

 

编译时,编译器检查左边(Animal a)

a.getAge();   //编译通过,Animal中有getAge()

a.speakLanguage();   //编译失败,Animal中没有子类的特有方法speakLanguage()

多态编译是否通过,就看父类中是否存在被调用的方法

      
运行时,JVM按右边的对象执行(new Person)

a.getAge();  //运行时,如果子类有覆盖,则调用子类的getAge(),否则调用父类的getAge()

a.speakLanguage();   //无法运行,编译没通过;只能通过对象向下转型才能调用特有方法

Person p = (Person)a;
p.speakLanguage();


 

多态的好处

1.可以提高代码的扩展性,父类引用可以接收多种子类对象

 向上转型--->限定功能使用,提高扩展性,实现统一操作

  Animal a = new Person();

  getAnimalAge(a); //凡是Animal对象都可以使用getAnimalAge()方法,实现统一操作

 

 

多态的弊端

1.由于子类对象类型的向上提升,导致父类引用只能调用父类中定义好的方法/规则,会丢失对子类特有方法的调用;

 调用子类对象特有方法的解决方案:

向下转型--->使用对象的特有方法

Person person = (Person)Amimal;

person.speakLanguage();

风险:类型转换异常

通过类型判断: A instanceof AType ?

 

 

多态使用的前提

1.必须有继承或者实现关系--->父类通过引用来接收子类对象

2.要想在运行时父类引用调用到子类的方法,这些方法必须是覆盖父类的方法,子类特有方法在多态下无法调用!

 

 

 

 多态中的成员变量

编译和运行都参考左边的父类;

成员变量没有覆盖的功能,子类与父类可以存在相同类型相同名称的变量;

记住:多态时,类型发生了提升,所有调用都要看父类中的定义,

   对于成员变量而言,是不存在覆盖的,所以始终都是使用的父类的成员变量,但是子类可以 改变这个变量值!

   对于方法而言,如果子类有覆盖,则调用子类的,否则调用父类的;

 

 示例1

public class Animal {
	int num;
	
	public Animal() {
		num = 1;
	}
	
	private void say() {
		System.out.println("Animal.say()");
	}
	
	public void hello() {
		say();
		System.out.println("Animal.hello():"+num);
	}
	
}

 

public class Person extends Animal{
	
	//子类可以定义与父类完全相同的成员变量,这样,子类与父类各自持有自己的一份成员变量
	//注意:成员变量没有覆盖的功能
	//int num;
	
	public Person() {
		//如果子类自己也定义了与父类相同的成员变量,则这里就是对子类的成员变量进行赋值
		//否则,就是对继承自父类的成员变量进行赋值;
		num = 3;
	}

}

 

public class Test {
	public static void main(String[] args) {
		Animal p = new Person();
		p.hello();
	}
	
}

 运行结果说明:

Animal.say()
Animal.hello():3

子类没有覆盖父类中的方法,将调用父类的hello(),所以父类中的私有方法也被调用到,但是,子类改变了父类的成员变量,所以num从1变为了3。

 

 

示例2

子类定义了与父类完全相同的成员变量

public class Animal {
	int num;
	
	public Animal() {
		num = 1;
	}
	
	private void say() {
		System.out.println("Animal.say()");
	}
	
	public void hello() {
		say();
		System.out.println("Animal.hello():"+num);
	}
	
}

 

public class Person extends Animal{
	
	//子类定义了一个与父类相同的成员变量num
	int num;
	
	public Person() {
		//子类中有成员变量num,则这里就是对子类自己的成员变量num进行赋值
		num = 3;
	}

}

 

public class Test {
	public static void main(String[] args) {
		Animal p = new Person();
		p.hello();
	}
	
}

 运行结果:

Animal.say()
Animal.hello():1

子类没有覆盖父类的hello(),所以,调用父类的hello();

由于子类定义了一个与父类完全相同的成员变量num,并在构造函数中初始化,但是这是对子类自己的num成员变量进行赋值,并不会修改父类的成员变量num,所以,执行结果中的num=1;

 

 

示例3

子类定义了与父类完全相同的成员变量,并覆盖父类的方法

public class Animal {
	int num;
	
	public Animal() {
		num = 1;
	}
	
	private void say() {
		System.out.println("Animal.say()");
	}
	
	public void hello() {
		say();
		System.out.println("Animal.hello():"+num);
	}
	
}

 

public class Person extends Animal{
	
	//子类定义了一个与父类相同的成员变量num
	int num;
	
	public Person() {
		//子类中有成员变量num,则这里就是对子类自己的成员变量num进行赋值
		num = 3;
	}

	public void hello() {
		System.out.println("Person.hello():"+num);
	}
}

 

public class Test {
	public static void main(String[] args) {
		Animal p = new Person();
		p.hello();
	}
	
}

 运行结果:

Person.hello():3

子类定义了与父类完全相同的成员变量,则子类构造函数中赋值操作是针对子类自己的成员变量num进行的;

子类对父类方法进行了覆盖,调用时,将执行子类的方法;

由于执行的是子类的方法,方法中使用的变量就是子类中定义的变量

所以,num=3。

 

 

 多态中的成员函数---成员函数绑定到对象上执行

编译时参考左边父类中是否存在被调用的方法
(核心)运行时参考右边子类对象中的方法,若没有,则通过super调用父类中的方法;

非静态方法的执行都是绑定到对象上进行的

如果子类有覆盖父类的方法,则子类对象直接调用自己的这个覆盖方法;

如果子类没有覆盖父类的方法,则子类会通过super调用父类的方法;

 

Animal a = new Person();

       a.getType();//子类没有覆盖,就调用父类的;有覆盖,就调用子类重写的;
 
  多态中的静态函数
编译和运行都参照左边;
没有多态性,静态函数始终绑定到类上,与对象多态无关!
 
 
 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值