Java的多态性
Java面向对象的三大特性:封装(Encapsulation), 继承(Inheritance), 多态(Polymorphism) 。
多态:同一个对象 体现出来的多种不同形态(身份) , 将一种行为表现出不同的效果。
实现多态的效果的前提: 需要先有继承关系。
我们先来看看代码:
Animal 类,它默认继承的是Object类:
public class Animal {
public String name = "Animal的name属性";
public void eat(){
System.out.println("animal的吃饭方法");
}
public void sleep(){
System.out.println("animal的睡觉方法");
}
}
继承Animal类的Person类:
public class Person extends Animal{
public String name = "person的name属性";
public void eat(){
System.out.println("person的吃饭方法");
}
public void sleep(){
System.out.println("person的睡觉方法");
}
public void talk(){
System.out.println("person的说话方法");
}
}
继承Animal类的Pig 类:
public class Pig extends Animal{
public String name = "pig的name属性";
public void sleep(){
System.out.println("pig的睡觉方法");
}
}
继承Person类的Student 类:
public class Student extends Person {
public String name = "student的name属性";
public void talk(){
System.out.println("学生遵守礼貌 应该好好说话");
}
public void study(){
System.out.println("好好学习 天天向上");
}
}
继承Person类的Teacher 类:
public class Teacher extends Person{
public String name = "teacher的name属性";
public void eat(){
System.out.println("做老师的通常不按时吃饭");
}
public void teach(){
System.out.println("做老师的独有方法 一般人不会讲课 我会");
}
}
我们先简单梳理一下类关系:
测试:
Object o = new Teacher();//自动向上转型,Teacher-->Object
o.hashCode();
o.toString();
以上代码将父类(Object)类型的引用 指向 子类(Teacher)的对象,虽然o实际上是一个Teacher对象,但是它现在充当的是Object的角色,它只能调用到Object类下的所有方法。但是,执行hashCode()方法时,先要看该对象的真实类(Teacher)是否重写该hashCode()方法,如果重写,调用的就是Teacher类重写的那个方法,如果没有重写,找Teacher类的上一级Person类,如果Person类重写了该方法,就执行Person类重写的那个方法,如果没有没有重写,继续找上一级Animal类,如此不断向上找寻···,直到该方法被成功找到。
要注意的是,之所以可以向上转型,前提是Teacher类是Object类的子类或者是Object类的子类的子类,必须具备直接或间接的继承关系。
Animal a = (Animal)o;//强制向下转型,Object--->Animal
a.hashCode();
a.toString();
System.out.println(a.name);//属性没有重写一说,animal的name属性
a.sleep();//从真实的类对象Teacher出发,Teacher没有重写 但person重写了,执行person的sleep
a.eat();//执行teacher的eat方法
Person p = (Person)o;//强制向下转型,Object--->Person
p.hashCode();
System.out.println(p.name);//person的name属性
p.sleep();//人类的睡觉
p.eat();//老师的吃饭
p.talk();//人类的说话
Teacher t = (Teacher)o;//强制向下转型,Object--->Teacher
System.out.println(t.name);//老师的name
t.eat();//老师的吃饭
t.sleep();//人类睡觉
t.talk();//人类说话
t.teach();//老师的独有方法
以上代码中,老师类和学生类都继承自Person类,它们是平级关系,没有继承关系,那么将teacher对象强制造型(铸型)成student对象会怎样呢?
Object o = new Teacher();
Student s = (Student)o;
这行代码编译好使,但是运行报错,它是运行时异常:java.lang.ClassCastException
cast意为铸造,铸型,它表示的是类铸型异常,不能将Teacher 类对象铸型为Student对象:Teacher cannot be cast to Student。
我们如何有效避免铸型时的异常呢?当我们不确定该类是否可以铸型为另一个类时,我们最好在前面加一个判断:
if(o instanceof Student){//对象是否属于后面类型
System.out.println("类型匹配 可以造型");
Student s = (Student)o;//运行时异常 ClassCastException
s.study();
}else{
System.out.println("对不起 类型不匹配 不帮您造型啦 否则会出问题");
}
instanceof严格意义来讲应该算作是一个运算符,运算的结果是true或false,它用来判断一个对象是否属于某一个类。
总结
多态的5个体现:
- 父类类型的引用 指向子类的对象
Person p = new Teacher(); - 该引用只能调用父类中定义的属性或方法
- 如果子类中将父类的方法重写,那么调取方法后执行的结果是子类重写之后的那个结果
如果父类与子类有同名的属性 执行父类的属性
如果父类与子类有同名的方法(重写) 执行子类重写之后的方法 - 若想要调用子类中独有的成员
(强制类型转化) 造型 铸型 (向上/向下转型) - 造型时(强制向下转型时) 可能会出现一个运行时异常
ClassCastException (造型/铸型 异常)
如果想要避免造型的异常 可以用instanceof关键字来进行判断
instanceof严格意义来讲应该算作是一个运算符,运算的结果是true或false,它用来判断一个对象是否属于某一个类
用法是:对象 instanceof 类
最后总结以下常见的运行时异常:
InputMismatchException 输入类型不匹配
NumberFormateException 数字格式化异常 Integer.parseInt(“abc”)
ArrayIndexOutOfBoundsException 数组索引越界
NegativeArraySizeException 数组长度负数 int [] arr = new int[-2];
NullPointerException 空指针异常 Person p = null; p.hashCode();
ArithmeticException 算数异常 10/0
ClassCastException 造型异常 将对象的类型还原时 与真实类型不匹配
运行时错误:
*Error 栈内存溢出错误