2022-7-25 第八组 韦柳盈 Java面向对象(6)---多态

发布于:2022-07-25 ⋅ 阅读:(579) ⋅ 点赞:(0)

今日重点:

目录

一、多态

(1)概念

(2)多态形成条件

(3)形式(转型)

1.向上转型

2.向下转型

(4)instanceof关键字

  (5)多态的使用 

(6)多态的优缺点 

二、课上案例

(1)object类----无敌方法

(2)匿名对象 

(2)多态练习案例

(3)在超级数组中运用多态

1.SuperArray类

2.Test类 

三、延申----数组算类吗

四、链表

(1)概念

1.单链表

2.双链表 

(2)课上单链表案例 

1.Node类

2.SuperLinked类

3.测试:Demo类 



一、多态

课上举例子的Animal,Cat,Dog,Person四个类其中隐藏的知识点还没有总结归纳!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1

(1)概念

多态是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。例如买了水果,水果有很多种,苹果、香蕉、梨子......

(2)多态形成条件

1.有继承
2.有重写
3.父类对象指向子类引用

(3)形式(转型)

在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。那么在这个过程中就存在着多态的应用。存在着两种转型方式,分别是:向上转型和向下转型。 

1.向上转型

  • 当子类对象赋值给一个父类引用时,即向上转型(多态本身就是向上转型的过程)
  • 格式:父类类型 变量名 = new 子类类型();---子转父
  • 如:Animal a=new Cat();//Animal 为父类,Cat为子类
  • 可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
  • 说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类中声明过的方法,方法体执行的就是子类重过后的功能。但是此时对象是把自己看做是父类类型的,所以其他资源使用的还是父类型的。
    比如:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
向上转型要点:
自动转型,隐式转换,向上转型就是 父类引用指向子类实例 ,也可以说:子类对象可以赋值
给父类
向上转型是安全的 ,因为子类都重写了父类方法, 父类引用指向那个子类,就调用该子类重
写方法
向上转型,父类引用只能调用子类重写方法,不能调用子类特有的方法,如果想调用,就必
须 向下转型
父类的静态方法是不允许子类重写的

 

2.向下转型

  • 通过强制类型转换格式,将父类引用转为子类格式
  • 格式:子类类型 变量名 = (子类类型)父类类型的变量;---父转子
  • Parent p = new Child();//向上转型,此时,p是Parent类型
    Child c = (Child)p;//此时,把Parent类型的p转成小类型Child
  • 子类的引用的指向子类对象,过程中必须要采取到强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象。其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的。
  • 说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
    比如:花木兰打仗结束,就不需要再看做是她爸了,就可以”对镜贴花黄”了
向下转型要点:
 强制转型 显式转换 ,向下转型就是子类的引用指父类引用,也可以说,父类强制成子类
就可以调用 子类特有的方法,发生向下转型的前提必须先发生向上转型,才能通过强转再转成子类类型 
 转型时容易出现异常 ClassCastExcepiton的发生。
 防止这种异常的发生,需要用java所提供instanceof (对象比较运算符),给引用变量作类型的校验
对象名 instanceof 数据类型
如果对象属于该类型,就返回true
如果对象不属于该类型,就返回false
public class Main {


    public static void main(String[] args) {
        Person person = new Student();

        Student student = new Student(); //向上转型
        Student student1 = (Student)person; //向下转型

        student.sleep();
        student1.sleep();
    }

}


class Person{
    public void sleep() {
        System.out.println("睡觉");
    }
}

class Student extends Person{
    public void sleep() {
        System.out.println("学生睡觉");
    }
}

(4)instanceof关键字

 instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。

instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

import java.util.ArrayList;
import java.util.Vector;
 
public class Main {
 
public static void main(String[] args) {
   Object testObject = new ArrayList();
      displayObjectClass(testObject);
   }
   public static void displayObjectClass(Object o) {
      if (o instanceof Vector)
      System.out.println("对象是 java.util.Vector 类的实例");
      else if (o instanceof ArrayList)
      System.out.println("对象是 java.util.ArrayList 类的实例");
      else
      System.out.println("对象是 " + o.getClass() + " 类的实例");
   }
}

//以上代码运行输出结果为:对象是 java.util.ArrayList 类的实例

(5)多态的使用 

编译看左边,运行看右边

有了对象的多态性以后,我们再编译期,只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

前提:多态对象把自己看做是父类类型

1.成员变量: 使用的是父类的

2.成员方法: 由于存在重写现象,所以使用的是子类的

3.静态成员: 随着类的加载而加载,谁调用就返回谁的

使用场景

(6)多态的优缺点 

优点

  1. 提高了代码的维护性(由继承保证)
  2. 提高代码复用性(由继承保证)
  3. 提高了代码的扩展性:新增加的子类不影响已存在类的多态性、继承性以及其他特性
  4. 安全性:向上转型将子类类型隐藏了起来

缺点

  1. 无法使用子类特有的功能(如要使用就要使用向下转型强制类型转换)

参考的博客链接为:

https://blog.csdn.net/llllllkkkkkooooo/article/details/108154477

https://blog.csdn.net/weixin_43884234/article/details/116593803

 

二、课上案例

(1)object类----无敌方法

这里没有掌握,只是大概了解!!!!!!!!!!!!!!---Ch02

package com.jr.morning;

public class Ch02 {

    // 无敌方法
    public Object show(Object ... obj){

        return true;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Person){
            Person person = (Person) obj;
        }
        return super.equals(obj);
    }

    public static void main(String[] args) {
        Object obj = new Person();
        obj = new Ch02();
        Ch02 ch02 = new Ch02();
        ch02.show("",1,'a',1.5,true,obj);
    }
}

(2)匿名对象 

这里没有掌握,只是大概了解!!!!!!!!!!!!!!---Ch03

package com.jr.morning;

/**
 * 匿名对象
 * 语法:
 * new 类名();
 *
 * 功能:和正常的有名字的对象的功能是相同的。
 *      依然具备了调用属性,方法的功能。
 *
 * 使用场景:多数是用在传参,实参,多数情况下配合构造器使用
 *
 * 好处:节约资源。
 *
 */
public class Ch03 {

    public void show() {

    }

    public static void main(String[] args) {
//        Ch03 ch03 = new Ch03();
//        ch03.show();
//        new Ch03().show();
//        Dog dog = new Dog();
//        dog.setName("旺旺");
//        dog.setAge(2);
//        System.out.println(dog);
        new Dog().setName("黑黑");
        new Dog().setAge(1);
        System.out.println(new Dog());
    }
}

(2)多态练习案例

 需求:开学时,来的人: 老师:去办公楼;学生:去寝室楼
            放假时,走的人:老师:逛街;学生:坐车回家

代码在E:\jiruan\workplace\java\java20220725\Works\src\com\jr\morning\test01

还不能完全掌握理解这个案例!!!!!!!!!!!!!!

(3)在超级数组中运用多态

这里掌握理解一些,但是还差一小部分 !!!!!!!!!!!!!!---Ch04

1.SuperArray类

package com.jsoft.morning.test;

/**
 * 超级数组
 */
public class SuperArray {

    // 维护一个数组,要考虑的是怎么存
    private Object [] array;

    // 超级数组的长度
    private int size;

    // 数组当前的容量
    private int capacity;

    public SuperArray(){
//        array = new Integer[10];
        this(10);
//        capacity = 10;
    }

    public SuperArray(int capacity){
        array = new Object[capacity];
        this.capacity = capacity;
    }

    // 添加数据,默认添加,在数组的尾部添加
    public void add(Object data) {
        // 添加时要确保容量足够,如果不够,就需要扩容
        ensureCapacity(size + 1);
        // 真正的添加数据
        array[size++] = data;

    }
    // 添加数据,传入两个参数
    // 在指定位置添加
    public void add(int index,Object data){
        if(rangeCheck(index)){
            ensureCapacity(size + 1);
            System.arraycopy(array,index,array,index + 1,size - index);
            // 真正的添加数据
            array[index] = data;
            size++;
        }

    }

    // 删除最后一个数据
    public Object remove(){
        if(size > 0){
            return array[--size];
        }
        return null;
    }

    // 删除指定下标位置的元素
    public Object remove(int index){
        if(rangeCheck(index)){
            Object res = array[index];
            System.arraycopy(array,index + 1,array,index,(--size - index));
            return res;
        }
        return null;
    }

    // 修改
    public boolean set(int index,Object data) {
        if(rangeCheck(index)){
            array[index] = data;
            return true;
        }
        return false;
    }

    // 获取超级数组的长度
    public int size(){

        return size;
    }

    // 获取指定下标的元素
    public Object get(int index) {
        // 判断一下index和合法性
        if(rangeCheck(index)){
            return array[index];
        }
        return null;
    }

    private boolean rangeCheck(int index) {
        // index >= 0
        // index <= size - 1
        return (index >=0 && index <= size - 1);
    }

    private void ensureCapacity(int needCapacity) {
        if(needCapacity > capacity){
            capacity = capacity + (capacity >> 1);
            Object [] newArray = new Object[capacity];
            System.arraycopy(array,0,newArray,0,array.length);
            array = newArray;
        }


    }

}

2.Test类 

package com.jsoft.morning.test;


public class Test {

    public static void main(String[] args) {
        // 创建一个超级数组的对象
        SuperArray superArray = new SuperArray(5);
        superArray.add(20);
        superArray.add(10);
        superArray.add(70);
        superArray.add(90);
        superArray.add("你好");
        superArray.add(1,100);
//        superArray.remove(5);
        superArray.set(3,-1);
        for (int i = 0; i < superArray.size(); i++) {
            System.out.println(superArray.get(i));
        }
    }
}

---根据7-23课上超级数组例子基础上修改,链接为:

三、延申----数组算类吗

按照通常的理解来说,Java是一种纯面向对象的语言,所以一切皆对象,对于所有的类型都是对象或者对象的变型。但是数组和其它数据类型却不是一般的对象。

比如一般的对象定义是这样的:

Myclass c1=new Myclass();

一般数组的定义是这样的:

Int[] a =new int[10];

引用类型的数组的定义则为:

Myclass[] c2=new Myclass[5];

从以上我们发现一个问题,数组在声明时是没调用构造函数的(没有小括号!)。java中对象的定义都需要调用构造函数的,而数组却没有,这就表明了数组不是类,或者说不是一个简单的类。分别打印普通类和数组类的实例看一下结果:

package com.jr.morning;

public class y {


    public static void main(String[] args) {
        y t1 = new y();

        y[] arr=new y[9];

        Class class1 = y[].class;

        System.out.println(class1);

        Class class2 = y.class;

        System.out.println(class2);
        
        System.out.println(class1.getSuperclass());
        System.out.println(class2.getSuperclass());

    }

}

输出结果不相同,可以看出数组类和普通类是不一样的:

 

但是数组类又有自己的父类,打印数组类的父类。

 System.out.println(class1.getSuperclass());
 System.out.println(class2.getSuperclass());

结果为:


 

所以数组是不是类呢?答案是:组类可以算作是一种特殊的类。
            1.站在JVM的角度看,是类,在JVM解析数组时,会生成一个数组的类
                解析数组
            2.站在编译角度,不是类,因为没有类名,没有结构。

package com.jsoft.morning;

public class Ch04 {

    public static void main(String[] args) {

        int [] arr = new int[10];//创建数组
       
    }
}

参考博客链接为:https://blog.csdn.net/Crystal7003/article/details/79843067

四、链表

(1)概念

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

 

 

1.单链表

数组和单链表之间的区别:

数组:
(1)数组需要维护下标
(2)数组定义时需要指定数组长度
(3)当在数组的某些位置增加和删除元素时,还要编写代码处理元素的移动
(4)时间性能:查找O(1)、插入和删除O(n)
(5)空间性能:需要预分配存储空间,分大了浪费,小了容易溢出

单链表:
(1)长度可变,扩展性好
(2)内存利用高(可以不连续)
(3)时间性能:查找O(n)、插入和删除O(1)
(4)空间性能:不需要分配存储空间,只要有就可以分配,元素个数不受限制

单链表的逻辑结构如下图所示:

单向链表的缺点分析:

(1)单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。

(2)单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp是待删除节点的前一个节点。

 

2.双链表 

为了能够实现双向查找的和自我删除的功能,引出了双向链表的概念。双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

双链表的逻辑结构如下图所示:

 参考的博客链接为:

https://blog.csdn.net/qq_39240270/article/details/86766945

https://blog.csdn.net/chen_hao_181/article/details/104587170

(2)课上单链表案例 

1.Node类

package com.jr.afternoon.test;

/**
 *  单向链表
 */
public class Node {

    private Integer data;
    private Node next;

    public Integer getData() {
        return data;
    }

    public void setData(Integer data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    public Node() {
    }

    public Node(Integer data, Node next) {
        this.data = data;
        this.next = next;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", next=" + next +
                '}';
    }
}

 

2.SuperLinked类

package com.jr.afternoon.test;


public class SuperLinked {

    // 链表的长度
    private int size;
    // 链表的第一个结点
    private Node first;
    // 链表的最后一个结点
    private Node last;

    // 无参构造器
    public SuperLinked() {
    }

    // 把数组添加到链表的尾部
    public boolean add(Integer data){
        // 把传入的数据构建成一个结点
        Node node = new Node(data,null);
        // 如果现在链表是空的,那我就是第一个结点
        if(first == null) {
            first = node;
        }else {
            // 如果链表不是空,那我就是最后一个结点
            // 我应该是在原来的last结点后面
            // 我是原来last结点的下一个结点
            last.setNext(node);
        }
        last = node;
        size++;
        return true;
    }

    // 在指定位置添加元素
    public boolean add(int index,Integer data) {
        Node node = getNode(index);
        Node newNode = new Node(data,null);
        if(node != null){
//            Node next = node.getNext();
//            newNode.setNext(next);
            newNode.setNext(node.getNext());
            node.setNext(newNode);
        } else {
            // 如果要插入的位置是null,只有一种情况,就是整个链表都是空
            first = newNode;
            last = newNode;
        }
        size++;
        return true;

    }

    // 默认删除头部的数据
    public boolean removeFirst() {
        if(size < 0){
            return false;
        }
        if(first != null){
            first = first.getNext();
            size--;
        }
        return true;
    }

    // 删除尾部的数据
    public boolean removeLast(){
        if(size <= 0){
            return false;
        }
        if(size == 1){
            first = null;
            last = null;
            size--;
            return true;
        }
        if(last != null){
            last = getNode(size - 2);
            last.setNext(null);
            size --;
        }
        return true;
    }

    public boolean remove(int index) {
        if(size < 0){
            return false;
        }
        if(size == 1){
            first = null;
            last = null;
            size--;
            return true;
        }else {
            Node node = getNode(index - 1);
            node.setNext(node.getNext().getNext());
        }
        size--;
        return true;
    }

    // 修改指定下标位置的元素
    public boolean set(int index,Integer data){
        Node node = getNode(index);
        node.setData(data);
        return true;
    }

    // 根据下标获取指定的数据
    public Integer get(int index) {
        return getNode(index).getData();
    }

    // 获取链表的长度
    public int size() {
        return size;
    }

    // 根据下标获取指定的结点
    private Node getNode(int index){
        if(index < 0){
            index = 0;
        }
        if(index >= size - 1){
            index = size - 1;
        }
        // 找到第index个
        Node cursor = first;
        for (int i = 0; i < index; i++) {
            cursor = cursor.getNext();
        }
        return cursor;
    }
}

3.测试:Demo类 

package com.jr.afternoon.test;

public class Demo {
//测试
    public static void main(String[] args) {
        SuperLinked superLinked = new SuperLinked();
        superLinked.add(1);
        superLinked.add(2);
        superLinked.add(3);
        superLinked.add(100);
        superLinked.add(0,-100);
        superLinked.removeFirst();
        superLinked.removeLast();

        superLinked.remove(2);

        for (int i = 0; i < superLinked.size(); i++) {
            System.out.println(superLinked.get(i));
        }

    }
}

今日总结:关于多态基本的概念和知识点都能理解掌握,但是对实际的案例的理解还差一些,自己不能独立完整敲出代码,还得继续努力!

本文含有隐藏内容,请 开通VIP 后查看