JavaSE学习笔记 5

        接上一回的内容,接下来主要讲一下多态的相关内容。

        (一)多态

                ①概念

                多态是三个特性中的最后一个特性了,这块内容也很饱满,多态体现的是同一段代码,在引用不同对象下,会发生不一样的现象,这就是多态。拿猫和羊举例,假如我们定义了一个吃饭的成员方法,用来描述猫和羊,虽然都叫吃饭,但是猫吃鱼,羊吃草,在现象上是不一样的,这也是多态。但是我们要搞清楚多态是一种思想,而不是一种具体的语法,我们需要的是我们用多态的思想去指导我们用代码去实现这样一种多态的表现。

                那么代码层面上我们如何实现所谓的多态呢?

                ② 多态的条件

                在Java内,多态是围绕着Java的重写功能去实现的,除了重现,还需要类处于继承关系或者实现接口的前提下,最后还需要我们通过父类引用、接口引用去调用对应的方法。要注意一点,Java的多态体现在方法上,参数的不同可以直接通过继承关系解决,比方动物有名字这一成员变量,对应子类在继承这个变量后,都能按各自要求去进行赋值;但是假如动物有一个吃的方法,子类继承以后,如果不对父类方法加以重写,那么猫和羊的子类在调用这个方法时,最后都只会显示吃饭,而不会分别显示吃鱼和吃草;同时我们前面也提到过,当子类、父类方法名一致,并且满足重写条件时,假如在子类内调用这个方法,则永远只会调用子类的方法,而不会调用父类的方法和其他相同继承关系子类的方法,所以需要我们通过父类引用调用重写的方法。

                ③重写

                重写的是方法,重写要求类之间是继承或者接口关系,父类、接口类内先有一个方法,然后子类内按相同的返回值、方法名、形参列表去写一个方法,这个方法体被重新编写,这样的一个过程就叫重写。(返回值如果是继承关系,那么允许不同)

                这里重写还有一些特性,首先重写的成员方法,其权限要大于等于父类的方法;其次重写的父类方法不能是构造方法,不能是static和private修饰的方法;最后就是为了让我们的代码更可靠,我们可以在对应重写的方法上面写上@Override注解,来让编译器帮我们检查我们重写的代码是否符合要求。

                那么程序是如何知道具体要调用哪个类重写的方法呢,这里主要涉及到了静态绑定和动态绑定两个方面的知识,静态绑定就是在编译阶段已经确定了会调用哪个方法,而重写则主要是运用了动态绑定的功能,动态绑定时在程序运行时才确定下来调用哪个类的方法。

                ④造型/转型(cast)

                所谓造型也就是父类引用调用子类对象,而重写,需要我们实现向上造型,也就是将子类对象的引用传给父类引用,一般来说可以通过赋值、通过方法传参、方法返回值三种方式去实现向上造型,例子如下:

class Person {
    public void print() {
        System.out.println("人类的打印");
    }
}

class Student extends Person {
    String name = null;

    public Student(String name1) {
        super();
        name = name1;
    }

    public Student() {
        super();
        name = "111";
    }

    @Override
    public void print() {
        System.out.println("学生的打印");
    }
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person();
        Person person2 = null;

        person1 = new Student();// 直接赋值的向上造型;
        person1.print();// 输出结果:学生的打印
        person2 = backP(new Student());// 通过方法返回值进行向上造型
        person2.print();// 输出结果:学生的打印
        Print(new Student()); // 通过形参传参进行向上造型,输出结果:学生的打印
    }

    private static Person backP(Student student) {
        return student;
    }

    private static void Print(Person person) {
        person.print();
    }
}

                通过向上造型、继承关系、重写,三个条件,我们实现了多态的实现。

                不过,有向上造型,自然也有向下造型,以上面的代码为例,需要转型才可以实现对应的向下造型,代码如下:

Person person = new Person();
Student student = (Student)person;

                 可以看到Person向下转型为了Student,并且需要使用类似于强制类型转换符来实现这一过程,如果没有转换符,那么这个代码编译时不会通过的,这里需要说明的是这个强制类型转换符并不代表强制类型转换,只是对类型的说法,因为如果是基本数据类型,我们将值为1.2的double强制类型变量转化为int类型的变量,这个值是会变为1的,相当于数据是会发生改变的。

                而对引用使用强制类型转换符,是不会对这个数据产生影响的,只是相当于对这个数据进行了一个声明,以上面的例子来说,我们加了一个(Student),表示我们声明这个person变量内的数据可以看成Student来看,但person内的数据或者说接受了值的student内,其数据并没有发生任何变化。

                向下转型是不安全的,如果从数据角度来看,person是包含在student内的,所以person内必然会没有student内的数据,当student试图去访问专属于student的成员时,就会报错,因为数据内并没有储存相关内容,所以一般来说我们不会进行向下造型。但如果person引用内本来就储存的时Student类对象,我们则可以安全地向下造型,为了解决这个问题,我们引入了关键字“instanceof”,用这个关键字说明两个引用存在继承关系。

                ⑤多态地优缺点

                多态的优点自然是提高了我们代码的复用率,有效减少了代码量;同时增加了可扩展性,说人话,就是后期想加东西方便了,代码可以少改一点。

                缺点主要就是属性这些东西没有多态咯,我们通过父类引用实现多态,调用不同的类方法,但是没法通过父类引用去调用不同的类属性。

                最后提醒一下,就是不要在构造方法内调用重写的方法,那样会容易产生意想不到的结果。

        (二)抽象类

                ①概念

                有些时候,我们通过类描述一个对象时,没有必要将一个类实例化,这个类的意义仅在于把自己当前的成员方法、成员属性传给子类,这时候,我们就会将这个类设定为抽象类。打个比方就是我们有一个文具类,文具类底下有毛笔、铅笔、红笔等等与笔相关的类,他们都重写了文具类的“写字”方法,我们实例化毛笔、红笔、铅笔等类时,调用重写的方法,可以体现为用红笔写字、用毛笔写字、用铅笔写字等,但是如果我们直接调用文具类内的“写字”方法,就会变成“用文具写字”,这有一点点问题,首先用文具写字,很怪,因为文具不仅仅包含笔,也包含橡皮等等,其次用文具写字,这与我们常规的语言习惯也不同,所以直接实例化一个文具类调用“写字”方法,是没有什么意义的,写字方法只有在具体子类内才具有对应的意义,这时候我们可以将文具类变为抽象类,某种意义上来说,抽象类就是用来被继承和重写方法的。

                ②定义抽象类的语法

                我们定义一个抽象类,只需要在关键字class前面加上一个关键字“abstract”即可,有了abstract关键字以后,程序就会允许我们不给出抽象方法的具体实现,因为在抽象类里实现这个抽象方法是没有意义的,好比前面例子里的写字,写字方法对于文具来说没意义,只有对特定的子类才有意义。所以对于抽象方法,子类都是必须要进行重写的。

                抽象类除了多了抽象方法以外,其他方面都跟普通类一样,成员方法、成员变量、构造方法等。

                ③抽象类特性

                抽象类不能被实例化,因为实例化没有意义;

                抽象类必须被继承,如果有抽象方法必须要被重写;

                抽象类内可以有构造方法,用于初始化父类变量;

                抽象类可以没有抽象方法,但是有抽象方法的一定是抽象类;

                抽象方法因为一定要被重写,所以不能是被private、static、final修饰的;

                ④抽象类的作用

                抽象类在概念的时候能有一定了解,但是我个人角度来说,对其真正的好处大概理解还不够深入吧,因为普通类也能够重写,普通类也能被继承,但是隐隐约约能感觉到一点抽象类的好处。从老师所说的角度来分析,则确实会有不少的意义。

                抽象类的作用更多的是用来作为一层保险,用于帮助我们检验代码,好比有些事情必须要子类去做,父类做是没意义的,如果我们用普通类去做,程序是不会报错的,但如果是抽象类,当我们试图直接实例化一个抽象类对象时,这时就会直接报错,相当于多了一层校验措施。

        (三)接口

                ①概念

                所谓接口,其实就是一种行为规范,打个通俗点的比方,假如有那么一个班级,我们约定好,能进这个班级的人都必须得会钓鱼,好的,我们就定义好了一个接口,即我们大家约定好了都要遵守的一个公共行为规则——钓鱼。

                从Java角度来说,接口是一种引用数据类型,他定义了一个大家都需要遵守的公共行为规范集合。

                ②接口的语法

                对于接口,Java有专门的一个关键字——interface,定义方式与定义一个类大同小异,代码如下:

interface 接口名 {抽象成员方法}

                实际上接口的定义,跟class的定义没差别,也就是把class换成interface而已,一般来说,我们定义一个接口名,其名字开头会加上一个大写的I,并且名字会用一些形容性的词来表达。

                接口内只允许定义公共抽象成员方法,即默认是public  abstract的;接口内不允许定义普通的成员变量,只允许定义一个常量,并且得是公共的、静态的,即必须得是public static final的成员变量。

                ③接口的使用

                接口使用也很简单,与继承的对应,将extends改为implements即可,但要注意的是,继承只允许类继承一个类,但接口允许一个类实现多个接口。例子如下:

// 基本格式
// class 类名 implements 接口名{}

class animals {
    String name;

    public animals(String name) {
        this.name = name;
    }
}

interface IEat {
    void eat();// 默认public abstract void eat();
}

interface ISleep {
    void sleep();// 默认public abstract void sleep();
}

class Duck extends animals implements IEat,ISleep {
    public Duck(String name) {
        super(name);
    }

    @Override
    public void eat() {
        System.out.println("鸭子在吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("鸭子在睡觉");
    }
}

                上面的例子我们可以看到,我们定义了一个父类animals,同时定义了IEat和ISleep两个接口,最后定义了一个子类Duck,让Duck继承了父类animals,同时让它实现了IEat和ISleep两个接口。

                ④接口的特性

                接口同抽象类一样,不能被实例化;

                接口内的成员变量默认为public static final,成员方法默认是public abstract;

                接口内的抽象方法是不实现的,均需要由实现了这个接口的子类去实现;

                接口不能有构造方法和静态代码块;

                接口虽然不是类,但是生成的字节码文件也是class后缀;

                ⑤接口间的继承

                接口与接口之间也可以有继承关系,使用的关键字也是extends,但这里的extends代表的是拓展而不是继承。当接口A拓展了接口B的抽象方法后,实现了A接口的类,也同时要实现接口B的所有抽象方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值