多态

Java作为面向对象的语言,可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。

Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。

定义:父类引用变量指向子类对象

多态的前提:必须有子父类关系 或者 类实现接口关系,否则无法完成多态。

在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。

1定义与使用格式

  父类类型  变量名 = new 子类类型();

  变量名.方法名(); 

例:普通类多态:

public class Fu {
	int a=1;
	
	public void method(){
		System.out.println("这是父类方法");
	}
}
public class Zi extends Fu{
	int a=2;
	public void method(){
		System.out.println("这是子类方法");
	}
	public void zi(){
		System.out.println("这是子类方法2");
	}
}
public class Test {
	public static void main(String[] args) {		
		Fu f=new Zi();
		System.out.println(f.a);		
		f.method();		
	}
}

抽象类多态:

public abstract class Animal {
	public abstract void sleep();
}
public class Dog extends Animal{
	public void sleep(){
		System.out.println("狗趴着睡觉");
	};
}

  

public class Test {
	public static void main(String[] args) {
		Animal an=new Dog();
		an.sleep();
	}
}

接口多态:

public interface JiDu {
	public abstract void jidu(); 
}
public class Pig implements JiDu{	
	public void jidu() {
		System.out.println("缉毒猪在缉毒");		
	}	
}

  

public class Test {
	public static void main(String[] args) {		
		JiDu jd=new Pig();
		jd.jidu();		
	}
}

自己理解: 

JiDu jd=new Pig();

正常建对象是 Pig p=new Pig();

这样jd和p都引用了Pig()的地址,也可以说pig()有了两种状态,这就是多态。

多态--成员的特点

2.1多态中成员变量的特点:

当子父类中出现同名的成员变量时,多态调用该变量时:

编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。(看父)

运行时期:调用引用型变量所属的类中的成员变量。(看父)

简单记:编译和运行都参考等号的左边。编译运行看左边

2.2多态中成员方法的特点:

同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。

编译时期:参考引用变量所属的类,如果类中没有调用的方法,编译失败。(看父)

运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。(调用子)。子类独有的方法无法调用。

简单记:编译看左边,运行看右边

3 instanceof关键字

可以通过instanceof关键字来判断某个对象是否属于某种数据类型。

例:

public class Animal{}  //动物类

public class Cat extends Animal{} //猫类

public class Person extends Animal{} //人类

public class Pot{} //锅类
public class Test {
	public static void main(String[] args) {
		Animal an=new Person();
		System.out.println(an instanceof Cat);
		System.out.println(an instanceof Person);		
		System.out.println(an instanceof Object);
	}
}

但是如果

System.out.println(an instanceof Pot); 就编译失败

 

所以只有同体系才能比较,不同的直接编译失败。

4多态转型

4.1向上转型

当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程

格式:

  父类类型  变量名 = new 子类类型();

个人理解:double b=100;(小转大)与这个同理

4.2向下转型

一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。

如果是直接创建父类对象,是无法向下转型的(先向上转了,才能向下转

格式:

  子类类型 变量名 = (子类类型) 父类类型的变量; 

例:

public abstract class Animal{
	public abstract void eat();
}
public class Cat extends Animal{	
	public void eat() {		
		System.out.println("猫吃鱼");
	}
	public void cathMouse(){
		System.out.println("猫抓老鼠");
	}	
}
public class Dog extends Animal{	
	public void eat() {
		System.out.println("狗吃骨头");		
	}
	public void lookhome(){
		System.out.println("狗看家");
	}	
}
public class Test {
	public static void main(String[] args) {
		Animal d=new Dog();
		d.eat();		
		Animal c=new Cat();
		c.eat();			
		
		Dog d2=(Dog)d;
		Cat c2=(Cat)c;
		d2.lookhome();
		c2.cathMouse();		
	}
}

但是如果写成这样:

Cat c2=(Cat)d;

可以编译,但会出现异常:

 

所以,向下转时,必须用instanceof加个判断

public class Test {
	public static void main(String[] args) {
		Animal d=new Dog();
		d.eat();		
		Animal c=new Cat();
		c.eat();
		
		method(d);		
		method(c);	
		
	}
	public static void method(Animal an){
		if(an instanceof Dog){
			Dog d2=(Dog)an;
			d2.lookhome();
		}else if(an instanceof Cat){
			Cat c2=(Cat)an;
			c2.cathMouse();
		}
	}
}

5多态的好处与弊端

好处:隐藏了子类类型,提高了代码的扩展性

弊端:只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。

6 什么时候使用

6.1什么时候使用向上转型:

当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。

6.2什么时候使用向下转型

当要使用子类特有功能时,就需要使用向下转型。

向下转型的好处:可以使用子类特有功能。

向下转型的弊端:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。

7多态举例

7.1传参

  

public interface USB {
	public abstract void openUsb();
	public abstract void closeUsb();
}
public class Notebook{
	public void run(){
		System.out.println("笔记本运行");
	}
	public void shutdown(){
		System.out.println("笔记本关机");
	}
	
	//usb方法,传一个usb对象
	public void useUsb(USB u){
		u.openUsb();
		u.closeUsb();
	}
}
public class Mouse implements USB{	
	public void openUsb() {
		System.out.println("插上鼠标");		
	}	
	public void closeUsb() {
		System.out.println("拔下鼠标");		
	}	
}
public class Keyboard implements USB{	
	public void openUsb() {
		System.out.println("插上键盘");		
	}	
	public void closeUsb() {
		System.out.println("拔下键盘");		
	}
}
public class Test {
	public static void main(String[] args) {
		Notebook n=new Notebook();
		Mouse m=new Mouse();
		Keyboard k=new Keyboard();
		
		n.run();
		n.shutdown();
		n.useUsb(m);
		n.useUsb(k);		
	}
}

分析:这里最关键的就是:public void useUsb(USB u){}

这里参数是传一个USB类型的对象

假设当传值时,传入一个鼠标对象m(Mouse m=new Mouse();)

那么调用方法的同时,相当于实现了一个多态:

USB u=new Mouse();

这是多态最常见的应用。

之前集合ArrayList的方法Add(Object obj),参数是一个Object类型的对象,但是实际用时,是传入一个集合中存放的对象的类型的对象(就是存什么,就传什么),这也是一种多态。

7.2 返回值

如果一个方法的返回值类型是父类,那么可以返回一个子类对象。

例:

public class Father {
	public void test(){
		System.out.println("父类方法");
	}
}
public class Son extends Father{
	public void test(){
		System.out.println("子类方法");
	}
}
public class Test {
	public static void main(String[] args) {		
		Father f=methods();
		f.test();
		System.out.println(f instanceof Son);
        System.out.println(f instanceof Father);
	}
	
	public static Father methods(){ //返回值是父类
		return new Son();	//返回子类对象
	}
}

8 总结:封装、继承、多态的作用

封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式

继承:子类会自动拥有父类所有可继承的属性和方法。(抽象类,接口都体现了继承)

多态:父类引用变量指向子类对象。配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。

扩展:java面试题,可以经常看看,例:

https://blog.csdn.net/linzhiqiang0316/article/details/80473906

原文地址:https://www.cnblogs.com/hzhjxx/p/10054271.html