详解Java面向对象编程之多态

目录
  • Java面向对象编程之多态
    • 一.对于多态的理解:
    • 二.多态的实现方法
  • 总结

Java面向对象编程之多态

一.对于多态的理解:

通俗点理解,多态其实就是一词多义,就是一种方法的多种状态,即不同的类对象,调用同一个方法名,有不同的实现效果,如下面这段代码块:

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("豆豆");
        Cat cat = new Cat("花花");
        dog.eat();
        cat.eat();
    }
}

对象dog和cat看似都调用了eat方法,都没有传参,按理说输出的结果应该一样,但其实不是这样的,让我们来看一下输出的结果:

这就是多态的一种表现,所属不同类的不同对象调用同一个方法名,却有着不同的实现效果。

二.多态的实现方法

Java中通过 方法重写(也叫方法覆写)方法重载接口实现多态(主要依赖于继承机制+方法覆写)

1.方法重载

方法重载十分好理解,就是子类和父类的方法名相同,但是参数个数或类型不一样,返回值不作要求,这里不再赘述

2.方法重写

对于方法重写,通常结合向上转型和向下转型两种形式进行应用,其中向上转型更为常见,向下转型相对使用较少

(1)向上转型:就是子类向父类转,向上转型最大的好处就是可以实现参数统一化,向上转型可以表现在三个地方:

其一:产生对象时:

注意:用这种形式创建的实例化对象dog1,其能调用的方法范围由父类Animal决定,即只能调用Animal类中的方法,而不能调用子类独有的方法,只有当子类有对父类的方法重写时,才调用子类重写后的方法!!!

其二:方法参数的传递:

其三:方法返回值的传递

向上转型的最大好处就是——参数统一化,父类引用可以接收子类所有对象

看下面这个例子:

完整代码为:

public class Animal {
    public String name;
    public Animal(String a){
        name = a;
    }
    public void eat(){
        System.out.println("食物");
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}
public class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
    public void eat(){
        System.out.println("骨头");
    }
}
public class Cat extends Animal{
    public Cat(String name){
        super(name);
    }
    public void eat(){
        System.out.println("鱼");
    }
}
public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("豆豆");
        Cat cat = new Cat("花花");
        dog.fun(dog);
        cat.fun(cat);
    }
}

结果为:

拆开来分析:

fun方法的参数为Animal类的实例化对象

Animal的子类对象,可以直接传入

也就是说,对于Animal类的所有子类实例化对象,均可以直接向fun方法传参,避免了重复性的写诸如 public static void fun(Dod d){}, public static void fun(Cat a){}等方法

(2)向下转型:向上转型是子类向父类转,向下转型则是将转为的父类还原为子类,这里用 还原 这个词是因为能向下转型的前提是:先发生向上转型且我们需要使用子类独有的方法时,才使用向下转型,也很好理解,父类不一定是子类,只有由子类转成的才可以向下转型还原,向下转型的形式如下:

        Animal animal = new Dog("豆豆");
        Dog dog = (Dog)animal;

基本类似与强制类型转换

注意:向下转型是有风险的,可能无法强制转换成功,这里可以引用instanceof类,用if语句判断,避免报错

        if(dog instanceof Dog){

        }

(3)方法重写的几点注意要求:

  • 只能覆写成员方法,不能重写static静态方法,但是方法重载是可以重载static方法的
  • 子类进行方法重写,子类方法的权限修饰符>=父类方法的权限修饰符,同时,子类并不能覆写父类的private方法,对于父类的包访问权限修饰方法,在不同包下的子类也不能覆写
  • 用final修饰的方法也不能覆写哦(JDK中的String类就是一个final类)
  • 返回值必须相同,或是向上转型的,即覆写的方法的返回值可以是父类方法返回类型的子类
  • 注意方法覆写与方法重载的区别,方法覆写:子类与父类的方法名一样,参数、返回值类型均一样,如果只返回值类型不一样编译会报错
  • 可使用@override 检验方法覆写是否一样

(4)最后是一道例题,容易掉坑:

public class A {
    public A(){
        this.func();
    }
    public void func(){
        System.out.println("A");
    }
}
class B extends A{
    private int num;
    public B(int num){
        this.num = num;
    }
    public void func(){
        System.out.println("B的num==" + num);
    }

    public static void main(String[] args) {
        B b = new B(100);
        b.func();
    }
}

分析运行后会输出什么呢?

仔细想想,小心掉坑,答案在文章末尾给出

3.抽象类

对于方法的覆写,一般的继承关系下,子类是可以选择覆写也可以选择不覆写的,但在一些场景下,我们想对子类作出强制性覆写要求,这就引出了抽象类的概念

(1)抽象类用abstract修饰,抽象类是普通类的超集,它只是在普通类的基础上多了抽象方法,抽象方法是没有方法体的,形式如下:

(2)抽象类必须有子类继承

(3)抽象类无法实例化对象,仅能用子类new相应的对象

(4)普通子类继承抽象类,必须覆写所有的抽象方法,当子类仍为抽象类时,可以选择不覆写,依旧保留抽象方法

(5)abstract修饰符不能和final同时使用,也不能和private同时使用

4.接口

上面讲的抽象类虽然能实现方法的覆写,但还是有缺陷的,比如抽象类还是遵循单继承原则,一个类也只能继承一个抽象类,同时,在语义上,只要继承,就是A is B 的意思,有时候并不符合逻辑,故而又引出了接口这个概念

(1)接口的定义与使用

我们用关键字interface来定义接口,子类用关键字 implements来实现接口,同时,通常,在命名接口时,我们会用大写的字母“I”开头命名以示区别,如下面一段代码的接口名为 IMessage ,而对于实现接口的子类命名我们通常用Impl作后缀

(2)接口的特点:

接口中只有全局常量和抽象方法(JDK8之前,JDK8又扩展了default方法,了解即可),如:

public interface IMessage {
     public static final int a = 10;
     public abstract void print();
}

接口中只有public权限,且全部为全局常量和抽象方法,故而,在接口内,public、static、final、abstract可以省略不写,默认即为这些关键字,故上一段代码可以直接写成下面这段:

public interface IMessage {
     int a = 10;
     void print();
}

接口是没有单继承限制的,子类可以implements多个父接口,父接口之间用逗号隔开,如:

public class CImpl implements IB,IMessage{    public void print(){            }    public void printf(){            }}
  • 同时,接口之间也可以多继承,一个接口可以extends多个父接口
  • 接口同抽象类一样,是不能直接实例化对象的,必须通过实现它的子类进行实例化
  • 如果一个子类既有继承的父类,也有实现的接口,则先继承父类再实现父接口

(3)常用的JDK内置的两大接口

a:Comparable接口

当使用Arrays.sort()方法排序时,当排序对象为自定义的类时,sort方法不知道应该按照对象的什么属性进行排序,故而待排序的自定义类需实现该接口,并将抽象方法compareTo覆写,形式如下:

import java.util.Arrays;

public class Person implements Comparable<Person> {
//    两个属性,name和age
    private String name;
    private int age;
//    有参构造
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
//    定义输出
    public String toString(){
        return name + "的年龄是" + age;
    }
//    覆写compareTo方法
    public int compareTo(Person o){
        return (this.age - o.age);
    }

    public static void main(String[] args) {
        Person p1 = new Person("言希",18);
        Person p2 = new Person("温衡",16);
        Person p3 = new Person("思莞",17);
        Person []p = new Person[]{p1,p2,p3};
//        用sort方法排序
        Arrays.sort(p);
        System.out.println(Arrays.toString(p));
    }
}

输出结果(按年龄升序):

b: Cloneable接口

Cloneable接口位于java.lang包中,顾名思义,就是用于克隆,在代码中也就是复制新的对象,新对象的属性方法都是从原对象中拷贝过来的,在实现该接口时,只需要覆写Object类提供的clone方法,如下面示例:

//实现Cloneable接口
public class Animall implements Cloneable{
    private String name;
//    clone方法
    protected Animall clone() throws CloneNotSupportedException {
        return (Animall)super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException{
        Animall a1 = new Animall();
        a1.name = "豆豆";
//        a2由a1克隆而来
        Animall a2 = a1.clone();
//        输出a2,和a1一致
        System.out.println(a2.name);
//        但是a1并不是a2
        System.out.println(a1 == a2);
    }
}

结果如下:

补充

  • Cloneable接口是标记接口,即它本身并没有任何抽象方法,当一个类实现了该接口,就表示该类具备了克隆能力
  • clone方法的源代码为protected native object clone() throws CloneNotSupportedException;,其中native也是一个关键字,表明是本地方法,即调用了C++的同名方法故而,我们还可以发现,native修饰的方法也是没有方法体的
  • 没有方法体的一定是抽象方法 ⅹ错误,因为native方法也是没有方法体的

好了,多态的内容基本就是这么多了,java中的多态主要依赖于继承和方法覆写,而对于那些需要强制性覆写的方法,我们又引出了抽象类,再鉴于抽象类有局限,我们又学习了接口,整体上内容就是这么多了,注意的细节比较多,多敲代码理解理解更棒。

最后,将文中那道例题答案奉上:

答案解析来啦

从main方法开始,执行的第一句为 B b = new B(100);,因为B继承自A,故而执行B 的构造方法要先去执行A的构造方法,public A(){ this.func(); },这里注意,虽然是A的构造,但对象是B的,故这里的this,func()实际是B.func(),public void func(){System.out.println(“B的num==” + num);}

因为这里其实还没给num赋值成功,所以num现在还是默认值0,所以输出了答案的第一句 B的num == 0,然后接着执行B的构造方法,将100赋值给了num,最后执行b.func,也就输出了答案的第二句 B的num ==100

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java面向对象程序设计多态性示例

    本文实例讲述了Java面向对象程序设计多态性.分享给大家供大家参考,具体如下: 多态:具有表现多种形态的能力的特征(同一个实现接口,使用不同的实例而执行不同的操作) 实现多态的优点:为了方便统一调用! 实现多态的三种方式! 1. 子类到父类的转换: 例: Dog dog=new Dog("欧欧","雪纳瑞"); dog.eat(); Pet pet=new Dog("欧欧","雪纳瑞");//子类到父类的转换 pet.eat(

  • 详细理解JAVA面向对象的封装,继承,多态,抽象

    目录 类和对象的使用(面向对象思想落地的实现): 子类对象实例化的全过程 1.从结果上看:(继承性) 2.从过程上来看: 1.封装性 2.继承性 继承性的好处: 3.多态性 虚拟方法调用 4.抽象性 1.抽象类的特点: 2.天生的父类:抽象类 3.抽象方法 总结 创建类的对象 = 类的实例化 = 实例化类 类和对象的使用(面向对象思想落地的实现): 1.创建类,设计类的成员 2.创建类的对象 3.通过"对象.属性"或"对象.方法"调用对象的结构 如果创建了一个类的多

  • Java面向对象之多态

    目录 一.前言 二.什么是多态? 三.多态的实现条件 四.多态的访问特点 1.我们建一个service包放Animal类 2.再servic包下建一个impl包,包下放Cat类 3.我们在建一个controller包,在里面建一个动物测试类 4.弄完之后我们程序一运行 4.1为什么两个有区别呢? 五.多态的优点和缺点? 六.为什么要分开建包 一.前言 前面我们了解和学习了继承的使用,现在我们来学习三大面向对象之一的多态. 多态使java面向对象丰富起来,所以学好多态十分重要. 二.什么是多态?

  • Java面向对象三大特性及多态解析

    大家好,本文将会给大家带来Java多态. 以上就是本次学习的6大任务.我们依次来看. 1 Object类 Object类是所有Java类的根基类. 如果在类的声明中未使用extends关键字指明其基类,则默认基类为Object类. class Person{ } 等价于 class Person extends Object{ } 1.对象的实例化过程 实例化一个类是从最顶级的超类开始实例化的, 是一层一层的包裹结构. "先父类后子类,先静态后成员". ⑴toString方法 toSt

  • java面向对象继承与多态介绍

    目录 一.概述 二.继承 2.1 继承的概述 2.2 继承机制 2.3 类中属性,方法的继承与覆盖 2.4 super 关键字 三. 多态 总结 一.概述 面向对象程序设计的三大原则是封装性,继承性和多态性.继承性是子类自动共享父类的数据和方法的机制,它是由类的派生功能体现的.继承具有传递性,使得一个类可以继承另一个类的属性和方法,这样通过抽象出共同的属性和方法组件新的类,便于代码的重用.而多态是指不同类型的对象接收相同的消息时产生不同的行为,这里的消息主要是对类成员函数的调用,而不同的行为是指

  • 详解Java面向对象编程之多态

    目录 Java面向对象编程之多态 一.对于多态的理解: 二.多态的实现方法 总结 Java面向对象编程之多态 一.对于多态的理解: 通俗点理解,多态其实就是一词多义,就是一种方法的多种状态,即不同的类对象,调用同一个方法名,有不同的实现效果,如下面这段代码块: public class Test { public static void main(String[] args) { Dog dog = new Dog("豆豆"); Cat cat = new Cat("花花&q

  • 详解Java面向对象之多态的原理与实现

    目录 何为多态 代码实现 多态理解 何为多态 定义: 多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式.系统在运行时(而非编译时),能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性. 特点: (1)多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,就是同一种事物表现出的多种形态. (2)多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话. (3)对不同类的对象

  • 详解java封装继承多态

    面向对象编程(Object Oriented Programming)有三大特性:封装.继承.多态.在这里,和大家一起加深对三者的理解. 封装 封装可以拆开理解,装,是把数据和方法放进了类里:封,把装进去的数据和成员方法加上访问权限.对于外界,内部细节是透明的,暴露给外面的是它的访问方法. 继承 继承,是为了重用父类代码.两个类若具有is a的关系就可以用extends.另外,继承也为实现多态做了铺垫. 多态 程序中定义的引用变量(java有两大数据类型,内部数据类型和引用数据类型)所指向的具体

  • 详解Java高并发编程之AtomicReference

    目录 一.AtomicReference 基本使用 1.1.使用 synchronized 保证线程安全性 二.了解 AtomicReference 2.1.使用 AtomicReference 保证线程安全性 2.2.AtomicReference 源码解析 2.2.1.get and set 2.2.2.lazySet 方法 2.2.3.getAndSet 方法 2.2.4.compareAndSet 方法 2.2.5.weakCompareAndSet 方法 一.AtomicReferen

  • Java面向对象编程之继承和多态以及包的解析与使用范例

    目录 1.继承 1.1继承的基本使用 1.2 protected 关键字 1.3 final 关键字 2.多态 2.1向上转型 2.2动态绑定 2.3方法重写 2.4向下转型 2.5super 关键字 2.5.1 super 关键字的基本用法 2.5.2 this和super的区别 3.包的使用 3.1导入包中的类 3.2常见系统包 1.继承 为什么要有继承? 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可. 此处的多个类称为

  • 详解Java 10 var关键字和示例教程

    关键要点 Java 10引入了一个闪亮的新功能:局部变量类型推断.对于局部变量,现在可以使用特殊的保留类型名称"var"代替实际类型. 提供这个特性是为了增强Java语言,并将类型推断扩展到局部变量的声明上.这样可以减少板代码,同时仍然保留Java的编译时类型检查. 由于编译器需要通过检查赋值等式右侧(RHS)来推断var的实际类型,因此在某些情况下,这个特性具有局限性,例如在初始化Array和Stream的时候. 如何使用新的"var"来减少样板代码. 在本文中,

  • 详解Java JDK动态代理

    今天来看看Java的另一种代理方式--JDK动态代理 我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象.动态代理有更强大的拦截请求功能,因为可以获得类的运行时信息,可以根据运行时信息来获得更为强大的执(骚)行(操)力(作). 我们还是以上一个例子为例,这里的IStars接口和Stars类都不需要修改,只需要修改代理类. 创建JDK动态代理需要先实现InvocationHandler接口,并重写其中的invoke方法,具体步骤如下: 1. 创建一个类

  • 详解 Java静态代理

    今天要介绍的是一个Java中一个很重要的概念--代理. 什么是代理?联系生活想想看,代理似乎并不陌生,最形象的代表便是经纪人,明星一般都有经纪人,经纪人作为中间人,负责代理明星的相关事宜,比如说,有人要请明星去唱歌表演,一般不会直接跟明星联系,而是联系他的经纪人,他的经纪人来负责安排行程,而真正唱歌表演的还是明星本人,经纪人仅仅作为一个附加物存在. 在Java中,代理也是这样的概念,来看个栗子: 先来创建一个明星类Stars: public class Stars implements ISta

  • 详解Java 中泛型的实现原理

    泛型是 Java 开发中常用的技术,了解泛型的几种形式和实现泛型的基本原理,有助于写出更优质的代码.本文总结了 Java 泛型的三种形式以及泛型实现原理. 泛型 泛型的本质是对类型进行参数化,在代码逻辑不关注具体的数据类型时使用.例如:实现一个通用的排序算法,此时关注的是算法本身,而非排序的对象的类型. 泛型方法 如下定义了一个泛型方法, 声明了一个类型变量,它可以应用于参数,返回值,和方法内的代码逻辑. class GenericMethod{ public <T> T[] sort(T[]

  • 详解Java Socket通信封装MIna框架

    核心类 IoService :Mina中将服务端和客户端都看成是服务,这里提供统一接口IoService,这个接口的作用就是用来处理套接字机制.也正是IoService来监听消息返回消息这些步骤,可以说IoService就是我们Mina中核心 IoProcessor:这个接口在另一个线程上,负责检查是否有数据在通道上读写,也就是说它也拥有自己的Selector,这是与我们使用JAVA NIO 编码时的一个不同之处,通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分Io

随机推荐