多态的基本解绍
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态
多态的实现条件
- 必须在继承体系下
- 子类必须要对父类中方法进行重写
- 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法
class Father {
public void say(){
System.out.println("父亲好好工作!");
}
}
class son extends Father{
public void say(){
System.out.println("儿子好好学习!");
}
public void say1(){
System.out.println("儿子找到好工作!");
}
}
class daughter extends Father{
public void say(){
System.out.println("女儿也好好学习!");
}
public void say2(){
System.out.println("女儿找到好工作!");
}
}
public class text{
public static void main(String[] args) {
Father father=new son();
father.say();
Father father1=new daughter();
father1.say();
}
}
定义一个 Father 类 作为父类,定义 son 类 和 daugther 类作为子类继承父类;
Father类 拥有 say() 方法;
son类 和 daugther 类 重写父类的 say() 方法 并且 分别拥有各自的特有的 say1() 方法 和 say2() 方法;
最后在 main 函数中 演示转型。
一个对象的编译类型与运行类型可以不一致 编译类型在定义对象时,就确定了,不能改变,而运行类型是可以变化的 编译类型看定义对象时 =号的左边,运行类型看 = 号的右边
重写
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为
上面代码就发生了重写子类son和daughter重写父类Father的say方法
方法重写的规则
子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
被重写的方法返回值类型可以不同,但是必须是具有父子关系的访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
父类被static、private修饰的方法、构造方法都不能被重写。
重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet),那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写
多态的转型
向上转型
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
Father father=new son();
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
编译类型看左边,运行类型看右边
运行效果看子类的具体实现
向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换
子类类型 引用名 = (子类类型) 父类引用
class Father {
public void say(){
System.out.println("父亲好好工作!");
}
}
class son extends Father{
public void say(){
System.out.println("儿子好好学习!");
}
public void say1(){
System.out.println("儿子找到好工作!");
}
}
class daughter extends Father{
public void say(){
System.out.println("女儿也好好学习!");
}
public void say2(){
System.out.println("女儿找到好工作!");
}
}
public class text{
public static void main(String[] args) {
Father father=new son();
father.say();
son son1=(son)father;
son1.say1();
Father father1=new daughter();
father1.say();
daughter daughter1=(daughter)father1;
daughter1.say2();
}
}
只能强制转换父类的引用,不能强制转换父类的对象
要求父类的引用必须指向的是当前目标类型的对象 当向下转型后,可以调用子类类型中所有的成员
转型类型异常
使用强转的时候可能会出现异常
public class text{
public static void main(String[] args) {
Father father=new son();
father.say();
daughter daughter1=(daughter)father;
daughter1.say2();
}
}
此时可以发现向上转型可以正常运行而向下转型会报出.ClassCastException:
的异常错误,原因是 daughter 类与 son类 没有继承关系,因此所创建的是 类son型对象在运行时不能转换成 daught 类型对象。
public class text{
public static void main(String[] args) {
Father father=new son();
father.say();
daughter daughter1=(daughter)father;
daughter1.say2();
}
}
这里也同样报错ClassCastException原因是因为没有实例化daug对象所以不能进行强制类型转换
instanceof 比较操作符
为了避免上述类型转换异常的问题,我们引出 instanceof 比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型
解释:这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例
public class text {
public static void main(String[] args) {
Father father = new son();
father.say();
if (father instanceof daughter) {
daughter daughter1 = (daughter) father;
daughter1.say2();
} else if (father instanceof son) {
son son1 = (son) father;
son1.say1();
}
}
}
动态绑定
当调用对象方法的时候,该方法会和该对象的运行类型绑定 即运行的时候才知道结果 当调用对象属性时,没有动态绑定机制,即哪里声明,哪里使用
public class text {
public static void main(String[] args) {
Father father = new son();
father.say();
}
}
多态数组
多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
class Father {
public void say(){
System.out.println("父亲好好工作!");
}
}
class son extends Father{
public void say(){
System.out.println("儿子好好学习!");
}
}
class daughter extends Father{
public void say(){
System.out.println("女儿也好好学习!");
}
}
public class text {
public static void main(String[] args) {
Father son1=new son();
Father daughter1=new daughter();
Father [] fathers={son1,daughter1};
int i=0;
for(i=0;i<fathers.length;i++){
fathers[i].say();
}
}
}
多态的优缺点
优点
代码更加灵活:无论右边new的时候换成哪个子类对象,等号左边调用方法都不会变化。
提高程序的拓展性:定义方法的时候,使用父类类型作为参数,将来使用时,使用具体的子类类型操作
多态缺陷:代码的运行效率降低。
注意
- 属性没有多态性当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
- 构造方法没有多态性