多态中向上转型与向下转型

谈谈类型转换符

  类型转换运算符是小括号,类型转换符的用法是:(type)variable,这种用法可以将variable变量转换成一个type类型的变量。前面在介绍基本类型的强制类型转换时,已经看到了使用这种类型转换符的用法,类型转换运算符可以将一个基本类型变量转换成另一个类型。
  除此之外,这个数据类型转换运算符还可以将一个引用类型变量转换成其子类类型。这种强制类型转换不是万能的,当进行强制类型转换时需要注意:

  • 基本类型之间的转换只能在数值类型之间进行,这里所说的数组类型包括整数型、字符型和浮点型。但数值类型和布尔类型之间不能进行类型转换。
  • 引用类型之间的转换只能在具有继承关系的两个类型之间进行,如果是两个没有任何继承关系的类型,则无法进行类型转换,否则编译时就会出现错误。如果试图把一个父类实例转换成子类类型,则这个对象必须实际上是子类实例才行(对这句话的理解看向下转型代码演示),否则在运行时引发ClassCastException异常。

向上转型与向下转型

1.向上转型
假设有一个Person类,Person类中有一个eat()方法以及name和id属性,代码如下:

class Person{
	 String name;
	 int id;
	public void eat(){
		System.out.println("人得吃饭");
	}
}

有一个Student类继承自Person类,该类重写了父类的eat()方法,有自己的特有属性grade和特有的study()方法。代码如下:

class Student extends Person{
	int grade;
	public void eat(){
		System.out.println("学生要学习,得多吃饭");
	}
	
	public void study(){
		System.out.println("学生要学习");
	}
}

实例化Student对象,并声明一个Person类的引用变量引用该Student实例,调用实例中的eat()方法以及给id属性赋值:

		Person st=new Student();
		st.id=1;
		st.eat();

在这里插入图片描述
我们调用st.grade以及st.study()方法结果报错

st.grade;
st.study();

在这里插入图片描述
在这里插入图片描述
分析:这里用到了向上转型,换句话说就是用父类的引用变量引用了子类对象(其实这就是多态,以后直接叫做多态就行)。当向上转型之后,我们在编译期,只能调用父类中声明的方法,但在运行期,执行的是子类重写的方法。(向上转型)多态是方法的多态,不是属性的多态。所以属性只能引用变量所声明的类中的,也就是只能引用Person类中的,而不能调用Student类中的,否则编译报错(此问题详细看三大特性中的多态)。
2.向下转型
有了对象的多态性(向上类型转换)以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。那么如何才能调用子类特有的属性和方法呢就要用到(向下类型转换向下类型转换其实就是上面提到的引用类型的强制类型转换

		Person st=new Student();
		Student st1=(Student)st;
		st1.grade=2;
		st1.study();

上述代码是允许的,因为st引用的对象原本就是Student对象向上转型得到的,在对st向下转型后得到的还是Student类的对象,能够被Student类的引用变量所引用。并且可以调用Student类特有的属性和方法了。
3.转型好处

  • 传参,当你不知道要传什么类型的参数时,可以直接将参数定义成Object类型的,这样就可以接收任意类型的参数(JAVA中所有的类都继承自java.lang.Object),但使用时要使用强转类型转换回来。
class Animal{
	
	public void eat(){
		System.out.println("动物:进食");
	}
	
	public void shout(){
		System.out.println("动物:叫");
	}
}

class Cat extends Animal{
	public void eat(){
		System.out.println("猫吃鱼");
	}
	
	public void shout(){
		System.out.println("喵喵喵");
	}
}

class Dog extends Animal{
	public void eat(){
		System.out.println("狗吃骨头");
	}
	
	public void shout(){
		System.out.println("汪!汪!汪!");
	}
	
	public void watchDoor(){
		System.out.println("看门");
	}
}

public class PolymorphismExample {
	
	
	public void func(Animal animal){//Animal animal = new Dog();
		animal.eat();
		animal.shout();
		
//		if(animal instanceof Dog){
//			Dog d = (Dog)animal;
//			d.watchDoor();
//		}
	}
	
	//使用多态(向上转型)的话,就不用写这些重载的方法了
	//	public void func(Dog dog){
	//	dog.eat();
	//	dog.shout();
	//}
	//public void func(Cat cat){
	//	cat.eat();
	//	cat.shout();
	//}
	
	public static void main(String[] args) {
		
		PolymorphismExample pe=new PolymorphismExample();
		pe.func(new Dog());
		pe.func(new Cat());
	}
}

在这里插入图片描述
这样调用func()方法时就不用写重载的方法了。

2.instanceof运算符
并不是所有的对象都可以向下转型的,如果试图把一个父类实例转换成子类类型,则这个对象必须实际上是子类实例才行,否则在运行时引发ClassCastException异常(代码演示如下)

class Person{
	String name;
	int age;
	
	public void eat(){
		System.out.println("人需要吃饭");
	}
	public void sleep(){
		System.out.println("人要睡觉");
	}
}

class Man extends Person{
	
	public void eat(){
		System.out.println("男人干活养家,多吃饭饭");
	}
	
	public void earnMoney(){
		System.out.println("男人要多挣钱");
	}
	
}

class Woman extends Person{
	public void eat(){
		System.out.println("女人要漂亮,少吃饭");
	}
	
	public void spendMoney(){
		System.out.println("女人花钱买包包");
	}
}
public class ConvsersionTest {
	
	public static void main(String[] args) {
		
		Person p =new Man();
		Man m=(Man)p;
		//p引用的子类对象是man,而下面把man型的对象强转为Woman报错
		//ClassCastException
		Woman wm=(Woman)p;
	}
	
}

instanceof的引入

  • 使用强转时,可能出现ClassCastException的异常。
  • 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。

instanceof的使用

  • a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
  • 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。
  • 要求a所属的类与类A必须是子类和父类的关系,否则编译错误。
class Person {

	String name;
	int age;
	
	int id=1001;
	public void eat(){
		System.out.println("人:吃饭");
	}
	
	public void walk(){
		System.out.println("人:走路");
	}
}


 class Man extends Person {

	boolean isSmoking;
	int id=1002;
	
	public void earnMoney(){
		System.out.println("男人负责挣钱养家");
	}
	
	public void eat(){
		System.out.println("男人多吃肉,长肌肉");
	}
	
	public void walk(){
		System.out.println("男人霸气的走路");
	}
}


 class Woman extends Person {
	
	boolean isBeauty;
	
	public void goShopping(){
		System.out.println("女人喜欢购物");
	}
	
	public void eat(){
		System.out.println("女人少吃,为了减肥");
	}
	
	public void walk(){
		System.out.println("女人窈窕的走路");
	}
}

public class PersonTest {

		public static void main(String[] args) {
		
			Person p2=new Man();
			if(p2 instanceof Woman){//不会进入
				Woman w1 = (Woman)p2;
				w1.goShopping();
				System.out.println("******Woman******");
			}
			
			if(p2 instanceof Man){//会进入
				Man m2 = (Man)p2;
				m2.earnMoney();
				System.out.println("******Man******");
			}
			
			if(p2 instanceof Person){//会进入
				System.out.println("******Person******");
			}
			if(p2 instanceof Object){//会进入
				System.out.println("******Object******");
			}
			
//			if(p2 instanceof String){
//			
//			}
		}
}

练习
1.

			Person p4=new Woman();
			Man m3=(Man)p4;

编译时通过(只要语法符合就行),运行时不通过 man 和 woman没啥关系
2.

			Person p4=new Person();
			Man m4=(Man)p4;
			m4.earnMoney();

编译通过,运行不通过,编译通过符合语法
如果运行也通过,我去调用Man专有的方法m4.earnMoney()是不是运行也通过?但是你有这个功能吗?没有的,只有子类继承父类后会加载父类的属性方法,哪有声明一个父类的对象把它强制转成子类对象,从而去调用子类对象的方法或属性呢?
3.

			Object obj=new Woman();
			Person p=(Person)obj;

编译通过,运行时也通过(woman的父类是person person的父类是object)
4.

			Man m5=new Woman();
			String str=new Date();

编译不通过"="是赋值运算符,只有右边对象和引用是同一类型或者右边对象是左边的子类才可以。不过这种情况可以骗过编译器的,但是运行还是不能通过。如下的举例

	Object o=new Date();
	String str1=(String)o;
/*
 * 建立InstanceTest 类,在类中定义方法method(Person e);
		在method中:
		(1)根据e的类型调用相应类的getInfo()方法。
		(2)根据e的类型执行:
		如果e为Person类的对象,输出:
		“a person”;
		如果e为Student类的对象,输出:
		“a student”
		“a person ” 
		如果e为Graduate类的对象,输出: 
		“a graduated student”
		“a student”
		“a person” 
 * 
 */
public class InstanceTest {
	
	public static void main(String[] args) {
		
		InstanceTest test = new InstanceTest();
		test.method(new Student());
	}
	
	
	public void method(Person e){
		
		//虚拟方法调用
		String info = e.getInfo();
		System.out.println(info);
		
		//if-else中范围小的声明在上边,否则范围小的就没法执行了
		//方式一
//		if(e instanceof Graduate){
//			System.out.println("a graduated student");
//			System.out.println("a student");
//			System.out.println("a person");
//		}else if(e instanceof Student){
//			System.out.println("a student");
//			System.out.println("a person");
//		}else{
//			System.out.println("a person");
//		}
		
		//方式二
		if(e instanceof Graduate){
			System.out.println("a graduated student");
		}
		
		if(e instanceof Student){
			System.out.println("a student");
		}
		
		if(e instanceof Person){
			System.out.println("a person");
		}
		
		
	}
}

class Person {
	protected String name = "person";
	protected int age = 50;

	public String getInfo() {
		return "Name: " + name + "\n" + "age: " + age;
	}
}

class Student extends Person {
	protected String school = "pku";

	public String getInfo() {
		return "Name: " + name + "\nage: " + age + "\nschool: " + school;
	}
}

class Graduate extends Student {
	public String major = "IT";

	public String getInfo() {
		return "Name: " + name + "\nage: " + age + "\nschool: " + school + "\nmajor:" + major;
	}
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值