Java多态

Java多态的一些认识

一.什么是多态 ?

  1. 一些相关介绍:面向对象的编程有三大特性:封装,继承,多态。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。在外界看来,暴露的只是他的访问方式。继承是为了重用父类的代码。同时继承也为实现多态做了铺垫。
  2. 多态的定义: 多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。关于多态我们一般有一下两种类型。
  3. 编译时多态: 编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法,通过编译之后会变成两个不同的方法。
  4. 运行时多态:也叫做动态绑定,一般是指在执行期间(非编译期间)判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。主要用于继承父类和实现接口时,父类引用指向子类对象。

 二.实现多态的条件?

1.继承 

2.重写

         1)区别重写与重载

                重载发生在本类中,重写发生在父类与子类之间。

                重载的方法名必须相同,重写的方法名且返回值类型必须相同。

                重载的参数列表不同,重写的参数列表必须相同

                但是对于重载必须发生在本类中也有争议。子类在某种情况下也可以重载父类的方法。因为子类会继承父类所有的公有方法(构造器方法除外),然后在子类中定义与继承方法同名不同参数列表的方法,这也叫重载。

        2) 为什么需要有重写呢?因为子类会继承父类的方法(除构造方法,对于父类的私有方法,子类没有权限调用),为了满足运行时子类的方法在父类上有所拓展,我们选择重写从父类继承的方法。

  3. 向上转型:

                1) JAVA中的一种调用方式。向上转型是对A的对象的方法的扩充,即A的对象可访问B           从A中继承来的和B“重写”A的方法。通俗来讲就是将子类对象转换为父类对象。

               2)比如说我们会在运行时将zi这个对象转换为Fu类对象。

再看另一个例子:

向上转型时会原本是Dog类型的obj转换为Animal类型。在Java中,引用变量可以是多态的,即它可以存放不同类对象的地址,只要这些类是它声明的类的派生类

                3)向上转型的一些知识点.

                        向上转型是不用强制转型的

                        向上转型时,父类指向子类引用对象会遗失除与父类对象共有的其他方法,也就是在转型过程中,子类的新有的方法都会遗失掉,但是子类重写的方法仍可以调用,只不过此时调用的是子类重写后的方法。向上转型只能使用父类的功能,不能使用子类的一些特定功能。 

运行结果

                4)动态绑定:父类引用指向的或者调用的方法是子类的方法。

  1. Eg: Fu obj = new Zi();//向上转型
  2. obj.show();//动态绑定

 4. 向下转型:父类引用的对象转换为子类类型。

                1)一般向下转型存在风险,我们将向下转型分为三种情况

第一种如果父类引用的对象如果引用的是指向的子类对象,那么在向下转型的过程中是安全的。也就是编译是不会出错误的。按照上面Animal和Dog的例子,也就是如果引用本来就指向的是Dog,那么向下转型的时候就是安全的。

第二种如果父类引用的对象是父类本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。如果引用本来是父类即Animal的一个实例对象,那么向下转型时是不安全的。

第三种情况:

  1. Fu obj1 = new Fu();
  2. Zi obj2 = (Zi)obj1;//失效
  1. 为了避免向下转型的风险,我们一般会提前使用instanceof关键字判断实例的类型,然后再进行转型。
  2. 向下转型是强制转换。
  3. 向下转型可以使用子类的特定功能,这一点是向上转型没有的。

5.抽象类

1) 应该注意的是多态可以从不同角度来看待。上面说的转型和重写是一种理解。抽象类和重写又是另一种理解。

2)举一个较易理解的例子,就是图形。图形是一个大类,图形有三角形,矩形,圆等等,因此图形下面有很多具体的图形分类。此时图形就相当于基类,而具体的图形就是派生出来的类,可以明显看到图形这个基类可以有多种形态,这就是多态。同时我们也注意到图形在没有确定具体形状之前,这个基类的方法(比如求面积)大多不能具体确定。

  1. //创建基类——图形,注意类头加上abstract标识符
  2. public abstract class GeometricObject{
  3.     private boolean filled;
  4.     private java.util.Date dateCreated;
  5. //抽象类的构造方法定义为protected,因为它只被子类使用。创建一个具体的实例时,其父类的构造方法被调用以初始化父类中定义的数据域
  6.     protected GeometricObject(){
  7.         dateCreated = new java.util.Date();
  8. }
  9. //创建时间
  10.     public java.util.Date getDateCreated(){
  11.         return dateCreated;
  12. }
  13.     public abstract double getArea();//抽象方法,获取面积
  14.     public abstract double getPerimeter();//抽象方法,获取周长
  15. }
  16. 创建派生类——
  17. public class Circle extends GeometricObject {
  18.   private double radius;
  19.   public Circle() {
  20.   }
  21.   public Circle(double radius) {
  22.     this.radius = radius;
  23.   }
  24.   /** Return radius */
  25.   public double getRadius() {
  26.     return radius;
  27.   }
  28.   /** Set a new radius */
  29.   public void setRadius(double radius) {
  30.     this.radius = radius;
  31.   }
  32.   @Override /** Return area */
  33. //在子类中实现基类中的抽象方法
  34.   public double getArea() {
  35.     return radius * radius * Math.PI;
  36.   }
  37.   @Override /** Return perimeter */
  38. //在子类中实现基类中的抽象方法
  39.   public double getPerimeter() {
  40.     return 2 * radius * Math.PI;
  41.   }
  42. }
  43. 创建派生类——矩形
  44. public class Rectangle extends GeometricObject {
  45.   private double width;
  46.   private double height;
  47.   public Rectangle() {
  48.   }
  49.   public Rectangle(double width, double height) {
  50.     this.width = width;
  51.     this.height = height;
  52.   }
  53.   @Override /** Return area */
  54. //在子类中实现基类中的抽象方法
  55.   public double getArea() {
  56.     return width * height;
  57.   }
  58.   @Override /** Return perimeter */
  59. //在子类中实现基类中的抽象方法
  60.   public double getPerimeter() {
  61.     return 2 * (width + height);
  62.   }
  63. }
  64. 下面是Demo
  65. public class Test{
  66.     public static void main(String[] args){
  67.         GeometricObject obj1 = new Circle(5);
  68.         GeometricObject obj2 = new Rectangle(5 3);
  69.         
  70.         System.out.println("面积是:“ + obj1.getArea() + " 周长是:” + 
  71.         obj1.getPerimeter());
  72.         System.out.println("面积是:“ + obj2.getArea() + " 周长是:” + 
  73.         obj2.getPerimeter());
  74. }

        3)关于抽象类的一些注意事项

        抽象类的构造方法要用protected标识符。抽象类的构造方法在子类调用构造方法时会被调用。

        不能用new创建关于抽象类的实例(如果是正常的父类,我们可以用new去创建一个父类的实例)。抽象类只能为其他类的基类。在创建子类的时候一般是 父类类型 变量名 = new 子类类型()。

        若父类是抽象类,如果子类不想成为抽象类,那么子类必须将父类中的抽象方法重写为带方法体的普通方法,否则子类仍然是抽象类(例如上面的多边形类)

        抽象类可以作为一种数据类型。我们可以创建一个抽象类类型的数组,然后再创建具体实例。

  1. //创建一个GeometricObject类型的数组,并将引用赋给arr.
  2. GeometricObject[] arr = new GeometricObject[10];
  3. //创建GeometricObject的子类实例,并将引用赋给数组
  4. arr[0] = new Circle();
  5. arr[1] = new Rectangle();

三.多态

1. 抽象类多态定义的使用格式

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

Eg:

2.多态的相关语法

  1. 在实现多态时其实就是因为子类继承父类的方法,然后子类继承父类的方法(除了构造函数和私有方法不能个访问),对于这些方法子类会进行重写。对于父类来说可以有很多拓展的子类,因此对于父类的方法,会有重写的很多不同方法。
  2. 关于子类重写父类的方法和属性,我们要区分两个概念覆盖和隐藏。
  1. 隐藏:child隐藏了parent的变量和方法,那么,child不能访问parent被隐藏的变量或者方法,但是,将child转换成parent中,可以访问parent被隐藏的变量或者方法
  2. 覆盖:child覆盖了parent的变量或者方法,那么,child不能访问parent被覆盖的变量或者方法,将child转换成parent后同样不能访问parent被覆盖的变量或者方法
  3. 总结:变量只会隐藏不会被覆盖,无论是实例变量还是静态变量。即子类的变量可以隐藏父类的变量。对于方法而言,同名的实例方法被覆盖,同名的静态方法被隐藏。

运行结果

3.应用场景

1)使用循环,使基类访问不同的派生类

  1. Eg: GradedActivity[] tests = new GradedActivity[3];//共有三个派生类
  2. //第一次采用五级计分制,考了75
  3. test[0] = new GradedActivity();
  4. test[0].setScore(75);
  5. //第二次考试采用二级积分制(P或者F)总共二十题,每题分值相同,
  6. //共二十道题,考生错了五道题。通过的最低分为60
  7. test[1] = new PassFailExan(205 ,60);
  8. //第三次考试是期末考试,也采用五级计分制,总共50题,每    题分值相同,考生答错了7
  9. test[2] = new FinalExam(507);
  10. //显示每次考试的分数和等级
  11. for(int i = 0; i<tests.length; i++){
  12.       System.out.println("Score:" +
  13.  tests[i].getScore() + "\t" + "Grade:" + exam.getGrade());
  14. }

2)实参是 派生类,形参是基类

  1. Eg: public static void main(String[] args) {
  2. GradedActivity[] tests = new GradedActivity[3];
  3. // 第一次考试采用五级计分制,考了75
  4. tests[0] = new GradedActivity();
  5. tests[0].setScore(75);
  6. // 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
  7. // 通过的最低分数线是60
  8. tests[1] = new PassFailExam(20560);
  9. // 第三次是期末考试也采用五级计分制总共50题,每题分值相同,考试答错7
  10. tests[2] = new FinalExam(507);
  11. // 显示每次考试的分数和等级
  12. for(int i=0; i<tests.length; i++){
  13.    showValue(tests[i]);//子类作为参数,传实参
  14. }
  15. }
  16. public static void showValue(GradedActivity exam){
  17. //父类作为形参
  18. System.out.println("Score: " + exam.getScore()+ "\t" + 
  19. "Grade: " + exam.getGrade());
  20. }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值