学习笔记之JavaSE(18)--面向对象编程9

本文介绍了多态的概念及其在程序设计中的应用。探讨了如何通过父类引用指向子类对象来实现多态,以及这种方法如何提高代码的可扩展性和可维护性。同时,文章还讨论了多态在静态方法和变量上的限制,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天学习的内容是多态


多态可以提高程序的可扩展性和可维护性,是一种非常重要的特性。知识点:

  • 多态的形式:父类引用指向子类对象(向上转型),并可以通过父类引用调用子类对象的内容(注意接口和实现类也存在多态,为了叙述方便下面省略)
  • 方法的返回值类型方法的参数列表也存在多态,目的就是提高程序的可扩展性
  • 父类引用不能调用子类特有的内容(这是因为编译器注重的是引用的类型!!如果这个引用的类型并没有该变量或该方法,编译器是不会让你通过编译的!!
  • 如果子类中有与父类同名的变量,子类变量不会覆盖父类变量,而是共存于子类对象中。之前说过子类引用默认访问同名的子类变量,而使用向上转型的话,父类引用默认访问同名的父类变量多态失效(但是实际开发中子父类不会出现同名变量,容易混淆)
  • 如果子类中有与父类同名的静态方法,父类引用调用的是父类中的静态方法。多态失效(实际上静态方法与静态变量是通过类调用的,压根不具有多态性!!)
  • 如果已经使用了向上转型,而又想访问子类特有的内容,就必须进行向下转型(因为编译器只认引用类型)。并且为了确保父类引用指向的确实是子类对象,最好使用instanceof关键字判断。
  • 如果强行要将父类对象转换为子类对象,会发生ClassCaseException
  • 向下转型的格式:if(父类引用instanceof 子类){子类 引用=(子类)父类引用}
  • 重点:实际上编译器注重的是引用类型,它根本无法得知父类引用到底指向哪个对象,实现多态的关键是动态绑定:除了static方法和final方法(private方法属于final方法),其他所有方法都是在运行时根据对象的类型进行绑定。当父类引用调用方法时,由于动态绑定的存在,JVM才可以根据对象的类型知道到底该调用谁的方法。实际上,多态只是在调用子类覆盖后的非静态方法时才会生效!!!!
  • 补充1:数组的类型检查是在运行时进行的,所以数组的多态可以通过编译器的检查,但是数组的多态是有风险的,如果将不同子类的对象放进数组,会出现ArrayStoreException
  • 补充2:容器类的类型检查是在编译期进行的,所以容器类没有多态
示例程序:
public class DuoTaiTest {
	
	public static void main(String[] args){
		//向上转型
		Animal animal=new Dog();
		//向上转型
		Pet pet=new Dog();
		//!animal.gurad(); 父类引用不能调用子类特有方法
		//!System.out.println(animal.age); 父类引用不能调用子类特有变量
		//!pet.guard(); 接口引用不能调用实现类特有方法
		//!System.out.println(pet.age); 接口引用不能调用实现类特有变量
		System.out.println(animal.size);//父类的0   如果子类和父类有同名变量时,多态失效
		System.out.println(pet.PRICE);//接口的100  如果实现类和接口有同名变量,多态失效
		animal.aa();//父类的静态方法   如果子类和父类有同名静态方法,多态失效

		
		//数组的类型检查是在运行时进行的,所以数组的多态可以通过编译器的检查
		Animal[] arr_dog=new Dog[]{new Dog(),new Dog()};
		Animal[] arr_cat=new Cat[]{new Cat(),new Cat()};
		//但是数组的多态是有风险的,如果将不同子类的对象放进数组,会出现ArrayStoreException
		//arr_dog[0]=new Cat();
		//arr_cat[0]=new Dog();
		//容器类的类型检查是在编译期进行的,所以容器类没有多态
		//!ArrayList<Animal> list=new ArrayList<Dog>();
		
		//多态的好处:提高程序可扩展性
		DuoTaiTest dt=new DuoTaiTest();
		dt.doEat(animal);//编译器调用的是Animal引用作为形参的版本
		dt.doPlay(pet);//编译器调用的是Pet引用作为形参的版本
		dt.doEat(new Dog());//编译器调用的是Dog引用作为形参的版本
		dt.doEat(new Cat());//编译器调用的是Cat引用作为形参的版本
		dt.doPlay(new Dog());//编译器调用的是Dog引用作为形参的版本
		dt.doPlay(new Cat());//编译器调用的是Cat引用作为形参的版本
		
		//向下转型(强制类型转换)
		//如果强行把父类对象转成子类对象,就会发生ClassCastException
		//所以最好先使用instanceof关键字判断父类引用指向的是不是子类对象
		Animal a=dt.fanHuiAnimal();
		if(a instanceof Dog){
			Dog dog=(Dog)a;
			dog.guard();
		}
		Pet p=dt.fanHuiPet();
		if(p instanceof Dog){
			Dog dog=(Dog)p;
			dog.guard();
		}
	}
	
	//如果这样定义方法,不利于程序的可扩展性
	public void doEat(Dog dog){
		dog.eat();
	}
	public void doEat(Cat cat){
		cat.eat();
	}
	public void doPlay(Dog dog){
		dog.play();
	}
	public void doPlay(Cat cat){
		cat.play();
	}
	public Dog fanHuiDog(){
		return new Dog();
	}
	public Cat fanHuiCat(){
		return new Cat();
	}
	//... (如果添加了新的动物,这里又要新添很多方法,不利于系统维护)

	
	//利用多态,可以提高程序的可扩展性
	//将方法的参数列表设置为父类或接口的引用
	public void doEat(Animal animal){//Animal animal=new Dog()/new Cat()...
		animal.eat();
		System.out.println("利用多态");
	}
	public void doPlay(Pet pet){//Pet pet=new Dog()/new Cat()...
		pet.play();
		System.out.println("利用多态");
	}
	//将返回值类型设置为父类或接口
	public Animal fanHuiAnimal(){//Animal animal=new Dog()/new Cat()...
		return new Dog();
		//return new Cat();
		//...
	}
	public Pet fanHuiPet(){//Pet pet=new Dog()/new Cat()...
		return new Dog();
		//return new Cat();
		//...
	}
}

//父类
abstract class Animal{
	
	int size=0;
	
	public  void eat(){
		System.out.println("吃东西");
	}
	
	public static void aa(){
		System.out.println("父类的静态方法");
	}
}

//接口
interface Pet{
	
	int PRICE=100;
	
	void play();
}

//既是子类也是实现类
class Dog extends Animal implements Pet{
	
	//与父类和接口同名变量
	private int size=1;
	private static int PRICE=200;
	//特有变量
	private int age;
	
	public void eat(){
		System.out.println("啃骨头");
	}
	
	public void play(){
		System.out.println("和主人玩");
	}
	
	//特有方法
	public void guard(){
		System.out.println("保护主人");
	}
	
	//覆盖父类的静态方法(实际上这里并没有被覆盖)
	public static void aa(){
		System.out.println("子类的静态方法");
	}
}

//既是子类也是实现类
class Cat extends Animal implements Pet{
	
	public void eat(){
		System.out.println("吃鱼");
	}
	
	public void play(){
		System.out.println("抓老鼠玩");
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值