Java除了多态理解、多态实现、还有哪些类型呢?

本文详细介绍了Java中的多态性,包括多态的理解、实现方式如重写和向上/向下转型,以及静态绑定和动态绑定的概念。强调了不要在构造方法中调用重写的方法,并探讨了抽象类的使用及其特点。
摘要由CSDN通过智能技术生成

这篇我们会讲到Java中的类型,我先简单介绍一下,主要会从几个方面去解说,重写-向上转型和向下转型,还会将静态绑定和动态绑定,会细讲构造方法中调用重写的方法。希望可以对大家有帮助~

一:对于多态的理解

多态:面向对象方法学中多态是指子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象也可以发送给子类对象。即在类等级的不同层次中可以共享一个方法的名字, 不同层次中的每个类各自按自己的需要来实现这个行为。通俗点理解就是指完成某个行为时,不同对象有着不同的完成方法

比如买票行为,学生和普通人都属于人,但是学生买票可以半价,而普通人是全票

二:Java多态实现

Java多态实现:和C++类似,在Java中要实现多态,必须满足以下条件

  • 继承是多态的前提, 没有继承就没有多态
  • 子类要对父类的方法完成 重写
  • 需要通过 父类的引用 调用重写的方法

(1)重写(override)

重写(override):也即覆盖,是子类对父类的非静态、非private修饰、非final修饰和非构造方法等的实现过程进行重新编写,返回值和形参都不能改变

A:重写规则

重写规则:

  • 子类重写父类时 与父类方法原型保持一致 ,也即 修饰符 返回值类型 方法名(参数列表) 需要完全一致(注意:JDK7以后,被重写的方法返回值类型可以不同,但是必须是 具有父子关系的 ,这称之为 协变 )
  • 访问权限不能比父类中被重写的方法的访问权限更低 :例如,如果父类方法被 public 修饰,那么子类中重写该方法时不可以被声明为 protected
  • 父类被 static 、 private 、 final 所修饰以及构造方法都不能被重写
  • 重写的方法可以使用 @Override 注解来显式指定。 此注解可以帮助我们进行一些合法性检验
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Animal{ 
    String name;
    int age;
    public  Animal(String name,  int age){ 
        this.name = name;
        this.age = age;
    }
    public  void  eating(){ 
        System.out.println(name + "吃饭");
    }
}

class  Dog  extends  Animal{ 
    public  Dog(String name,  int age){ 
        super(name, age);
    }
    @Override //重写父类
    public  void  eating(){ 
        System.out.println(name + "吃鱼");
    }
}

class  Cat  extends  Animal{ 
    public  Cat(String name,  int age){ 
        super(name, age);
    }
    @Override//重写父类
    public  void  eating(){ 
        System.out.println(name + "啃骨头");
    }
}</pre>

B:重写和重载的区别

  • 方法重载 :是类的多态性表现
  • 方法重写 :是子类与父类的一种多态性表现

(2)向上转型和向下转型

A:向上转型

向上转型:使用父类的引用指向子类对象,语法格式为: 父类类型 对象名 = new 子类类型()

  • 向上转型 是合理、安全的 ,因为 大范围可以囊括小范围
  • 优点 :让代码实现更为简单、灵活
  • 缺点 :不能调用子类特有的方法
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Animal{ 
    String name;
    int age;
    public  Animal(String name,  int age){ 
        this.name = name;
        this.age = age;
    }
    public  void  eating(){ 
        System.out.println(name + "吃饭");
    }
}

class  Dog  extends  Animal{ 
    public  Dog(String name,  int age){ 
        super(name, age);
    }
    @Override //重写父类
    public  void  eating(){ 
        System.out.println(name + "啃骨头");
    }
}

class  Cat  extends  Animal{ 
    public  Cat(String name,  int age){ 
        super(name, age);
    }
    @Override//重写父类
    public  void  eating(){ 
        System.out.println(name + "吃小鱼");
    }
}

public class  TestDemo  { 
    public  static  void  main(String[] args)  { 
        Animal animal = new Dog("旺财", 3);//向上转型
    }
}</pre>

向上转型一般会有用于以下场景

  • **直接赋值 **:上面代码已经说过
  • **方法传参 **:形参为父类引用,可以 接受任意子类对象
  • **方法返回 **:返回值为父类引用,此时可以 返回任意子类对象
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Animal{ 
    String name;
    int age;
    public  Animal(String name,  int age){ 
        this.name = name;
        this.age = age;
    }
    public  void  eating(){ 
        System.out.println(name + "吃饭");
    }
}

class  Dog  extends  Animal{ 
    public  Dog(String name,  int age){ 
        super(name, age);
    }
    @Override //重写父类
    public  void  eating(){ 
        System.out.println(name + "啃骨头");
    }
}

class  Cat  extends  Animal{ 
    public  Cat(String name,  int age){ 
        super(name, age);
    }
    @Override//重写父类
    public  void  eating(){ 
        System.out.println(name + "吃小鱼");
    }
}

public class  TestDemo  { 

    //形参为父类引用,可以接受任意子类对象
    public  static  void  eatFood(Animal animal){ 
        animal.eating();
    }
    public  static  void  main(String[] args)  { 
        Cat cat = new Cat("喵喵", 3);
        eatFood(cat);
    }
}</pre>

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Animal{ 
    String name;
    int age;
    public  Animal(String name,  int age){ 
        this.name = name;
        this.age = age;
    }
    public  void  eating(){ 
        System.out.println(name + "吃饭");
    }
}

class  Dog  extends  Animal{ 
    public  Dog(String name,  int age){ 
        super(name, age);
    }
    @Override //重写父类
    public  void  eating(){ 
        System.out.println(name + "啃骨头");
    }
}

class  Cat  extends  Animal{ 
    public  Cat(String name,  int age){ 
        super(name, age);
    }
    @Override//重写父类
    public  void  eating(){ 
        System.out.println(name + "吃小鱼");
    }
}

public class  TestDemo  { 

    //方法返回值为父类引用,此时可以返回任意子类对象
    public  static  Animal  buyAnAnimal(String animal_name){ 
        if(animal_name.equals("狗")) { 
            return new Dog("旺财", 2);
        }
        else if(animal_name.equals("猫")) { 
            return new Cat("喵喵", 3);
        }
        else{ 
            return null;
        }
    }
    public  static  void  main(String[] args)  { 
        System.out.println("进入宠物店:请问你需要什么动物");
        Scanner sc = new Scanner(System.in);
        String animal_name = sc.nextLine();
        Animal animal = buyAnAnimal(animal_name);
        System.out.println("你得到了:" + animal.name);
        sc.close();
    }
}</pre>

B:向下转型(基本不用)

向下转型:使用子类的引用指向父类对象,但是向下转型很不安全,所以基本不会使用

如下是向上转型, Animal animal = new Dog("旺财", 2) ,所以 animal 是无法调用 Dog 的特有方法 dog_method() 的

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Animal{ 
    String name;
    int age;
    public  Animal(String name,  int age){ 
        this.name = name;
        this.age = age;
    }
    public  void  eating(){ 
        System.out.println(name + "吃饭");
    }
}

class  Dog  extends  Animal{ 
    public  Dog(String name,  int age){ 
        super(name, age);
    }
    @Override //重写父类
    public  void  eating(){ 
        System.out.println(name + "啃骨头");
    }

    public  void  dog_method(){ 
        System.out.println(name + "的特有方法");
    }
}

class  Cat  extends  Animal{ 
    public  Cat(String name,  int age){ 
        super(name, age);
    }
    @Override//重写父类
    public  void  eating(){ 
        System.out.println(name + "吃小鱼");
    }
}

public class  TestDemo  { 
    public  static  void  function(Animal animal){ 
        animal.eating();
    }
    public  static  void  main(String[] args)  { 
        Animal animal = new Dog("旺财", 2);
        animal.dog_mehtod();
    }
}</pre>

但是我们可以向下转型,也即 让一个子类的引用指向强转后的父类对象 ,此时便可以调用 Dog 的特有方法 dog_method() 了

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class  TestDemo  { 
    public  static  void  function(Animal animal){ 
        animal.eating();
    }
    public  static  void  main(String[] args)  { 
        Animal animal = new Dog("旺财", 2);
        Dog dog = (Dog)animal;//向下转型
        dog.dog_method();
    }
}</pre>

向下转型很不安全,而且有时会有严重的歧义。 Java中为 了提高向下转型的安全性,引入了 instanceof ,如果该表达式为 true ,则可以安全转换

(3)实现多态

讲到这里,其实已经完成了多态,所以日后我们就可以利用继承和转型来完成 传哪个对象就调用哪个对象的方法的目的

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Animal{ 
    String name;
    int age;
    public  Animal(String name,  int age){ 
        this.name = name;
        this.age = age;
    }
    public  void  eating(){ 
        System.out.println(name + "吃饭");
    }
}

class  Dog  extends  Animal{ 
    public  Dog(String name,  int age){ 
        super(name, age);
    }
    @Override //重写父类
    public  void  eating(){ 
        System.out.println(name + "啃骨头");
    }
}

class  Cat  extends  Animal{ 
    public  Cat(String name,  int age){ 
        super(name, age);
    }
    @Override//重写父类
    public  void  eating(){ 
        System.out.println(name + "吃小鱼");
    }
}

public class  TestDemo  { 
    public  static  void  function(Animal animal){ 
        animal.eating();
    }
    public  static  void  main(String[] args)  { 
        Dog dog = new Dog("旺财", 2);
        Cat cat = new Cat("喵喵", 3);
        function(dog);
        function(cat);
    }
}</pre>

三:静态绑定与动态绑定

  • **静态绑定 **:也称为 早绑定 ,是指 在编译时根据用户所传递实参类型确定所调用的方法 ,最典型的例子就是 方法重载
  • **动态绑定 **:也称为 晚绑定 ,是指 在编译时不能确定该调用的方法,需要在程序运行确定 ,最典型的例子就是 多态 ,在程序运行时才能拿到具体的类型

四:不要在构造方法中调用重写的方法

下面的代码中,B继承自A, func 方法被重写。在构造B对象时会调用A的构造方法,在A的构造方法中调用了 func 方法,但此时是B对象,所以会调用到B的 func ,而B对象而没有完成构造,所以 num 处于未被初始化状态,为0

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  A{ 
    public  A(){ 
        func();
    }
    public  void  func(){ 
        System.out.println("A的func()方法");
    }
}
class  B  extends  A{ 
    private int num = 1;
    @Override
    public  void  func(){ 
        System.out.println("B的func()方法,num="+num);

    }
}
public class  TestDemo{ 
    public  static  void  main(String[] args)  { 
        B b = new B();
        b.func();
    }
}</pre>

五:抽象类

(1)一个例子

如下一个类 Shape ,其中有一个方法 draw ,作用是向屏幕打印某个具体的图形(没有具体实现),图形有很多种,这里给出了 Rectangle 、 Triangle 和 Circle 三个类,分别用于描述矩形、三角形和圆形,它们各自内部也有一个方法 draw ,重写了父类的方法,形成多态

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Shape{ 
    public void draw(){ 
        System.out.println("画一个图形");

    }
}

class  Rectangle  extends  Shape{ 
    @Override
    public void draw(){ 
        System.out.println("画一个矩形");
    }
}

class  Triangle  extends  Shape{ 
    @Override
    public void draw(){ 
        System.out.println("画一个三角形");
    }
}
class  Circle  extends  Shape{ 
    @Override
    public void draw(){ 
        System.out.println("画一个三角形");
    }
}
public class  TestDemo  { 

    public static void drawing(Shape input){ 
        input.draw();
    }

    public static void main(String[] args) { 
        Circle circle = new Circle();
        drawing(circle);
    }
}</pre>

对于父类的方法,它的实现是没有意义的, 因为“画一个图形”这样的话太过抽象 ,无法具体去描述。 所以,我们可以不用实现它,将其改造为抽象方法,需要在方法返回值前加入关键字 abstract ,当某个类拥有抽象方法后它就变成了抽象类,也需要在其前面加入关键字 abstract 进行修饰

  • 注意

    注意:抽象方法就是C++中的纯虚函数

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">abstract class  Shape{ 
    abstract public void draw();//抽象方法
}

class  Rectangle  extends  Shape{ 
    @Override
    public void draw(){ 
        System.out.println("画一个矩形");
    }
}

class  Triangle  extends  Shape{ 
    @Override
    public void draw(){ 
        System.out.println("画一个三角形");
    }
}
class  Circle  extends  Shape{ 
    @Override
    public void draw(){ 
        System.out.println("画一个三角形");
    }
}
public class  TestDemo  { 

    public static void drawing(Shape input){ 
        input.draw();
    }

    public static void main(String[] args) { 
        Circle circle = new Circle();
        drawing(circle);
    }
}</pre>

(2)抽象类概念

抽象类:在面向对象方法学中,所有对象都是需要通过类来描绘的,但这不意味着所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就应该设计为抽象类

  • 例如上例中 Shape 类中也存在 draw 方法,但是 Shape 它并代表某个具体图形,是没有办法来实现的

(3)抽象类语法及特性

抽象类语法:在Java中,如果一个类被 abstract 修饰,就称它为抽象类,抽象类中被 abstract 修饰的方法称之为抽象方法,注意以下几点

①:抽象类无法进行实例化

②:抽象方法 不能是 private 的

③:抽象类存在的意义是为了被继承,它所体现的是接口继承。也就是说我只需要你存在这样一个方法名,你的实现我是不关心的

④:抽象必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也将会是抽象类

⑤:抽象方法 不能被 final 和 static 修饰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值