2.19 学习记录 综合练习:宠物商店

class Link{//链表类,外部能够看见的只有这一个类
    private class Node{//定义的内部节点类
        private Object data;//要保存的数据
        private Node next;//下一个节点引用
        public Node(Object data){
            this.data = data;
        }
        /**
         * 设置新结点的保存,所有的新结点保存在最后一个结点之后
         * @param newNode 新结点对象
         */
        public void addNode(Node newNode){
            if (this.next == null){//当前的下一个结点为null
                this.next = newNode;//保存节点
            }else {                 //向后继续保存
                this.next.addNode(newNode);
            }
        }
        /**
         * 数据检索操作,判断指定数据是否存在
         * 第一次调用(Link):this = Link.root
         * 第二次调用(Node):this = Link.root.next
         * @param data 要查询的数据
         * @return 如果数据存在返回true,否则返回false
         */
        public boolean containsNode(Object data){
            if (data.equals(this.data)){
                return true;
            }else {
                if (this.next != null){
                    return this.next.containsNode(data);
                }else {
                    return false;
                }
            }
        }
        /**
         * 根据索引取出数据,此时该索引一定存在的
         * @param index 要取得数据的索引编号
         * @return 返回指定索引节点包含的数据
         */
        public Object getNode(int index){
            //使用当前的foot内容与要查询的索引进行比较,随后将foot的内容自增,目的是下次查询方法
            if (Link.this.foot++ == index){//当前为要查询的索引
                return this.data;//返回当前节点数据
            }else {//继续向后查询
                return this.next.getNode(index);//进行下一个节点的判断
            }
        }
        /**
         * 修改指定索引节点包括数据
         * @param index 要修改的索引编号
         * @param data 新数据
         */
        public void setNode(int index,Object data){
            //使用当前的foot内容与要查询的索引进行比较,随后将foot的内容自增,目的是下次查询方便
            if (Link.this.foot++ == index){//当前为要修改的内容
                this.data = data;//进行内容修改
            }else {
                this.next.setNode(index,data);//继续下一个节点的索引判断
            }
        }
        /**
         * 节点的删除操作,匹配每一个结点的数据,如果当前结点数据符合删除数据
         * 则使用"当前结点上一个结点.next = 当前节点.next"方式空出当前结点
         * 第一次调用(Link),previous = Link.root、this = Link.root.next
         * 第二次调用(Node),previous = Link.root、this = Link.root.next.next
         * @param previous 当前节点的上一个结点
         * @param data 要删除的数据
         */
        public void removeNode(Node previous,Object data){
            if (data.equals(this.data)){ //当前结点为要删除的结点
                previous.next = this.next;//空出当前节点
            }else {//继续向后删除
                this.next.removeNode(this,data);//继续判断下一个
            }
        }
        /**
         * 将结点中保存的内容转化为对象数组
         * 第一次调用(Link):this = Link root;
         * 第一次调用 (Node):this = Link.root.next;
         */
        public void toArrayNode(){
            Link.this.retArray[Link.this.foot++] = this.data;//取出数据并保存在数组中
            if (this.next != null){                         //有后续元素
                this.next.toArrayNode();                    //继续下一个数据的取得
            }
        }
    }
    private Node root;          //根节点定义
    private int count = 0;      //保存元素的个数
    private int foot = 0;       //结点索引
    private Object[] retArray;  //返回的数组

    /**
     * 用户向链表增加新的数据,在增加时要将数据封装为Node类,这样才可以匹配结点顺序
     * @param data 要保存的数据
     */
    public void add(Object data){ //假设不允许有null
        if (data == null){  //判断数据是否为空
            return;     //结束方法调用
        }
        Node newNode = new Node(data);//要保存的数据
        if (this.root == null){//当前没有根结点
            this.root = newNode;//保存根结点
        }else {//根结点存在
            this.root.addNode(newNode);//交给Node类处理结点的保存
        }
        this.count++;//数据保存成功后保存个数加一
    }
    /**
     * 取得链表中保存的数据个数
     * @return 保存的个数,通过count属性取得
     */
    public int size(){  //取得保存的数据量
        return this.count;
    }
    /**
     * 判断是否为空链表,表示长度为0,不是null
     * @return 如果链表中没有保存任何数据则返回true,否则返回false
     */
    public boolean isEmpty(){
        return this.count == 0;
    }
    /**
     * 数据查询操作,判断指定数据是否存在,如果链表没有数据直接返回false
     * @param data 要判断的数据
     * @return 数据存在返回true,否则返回false
     */
    public boolean contains(Object data){
        if (data == null || this.root == null){ //现在没有要查询的数据,根节点也不保存数据
            return false;                       //没有查询结果
        }
        return this.root.containsNode(data);    //交由Node类查询
    }
    /**
     * 根据索引取得保存的节点数据
     * @param index 索引数据
     * @return 如果要取得的索引内容不存在或者大于保存个数,返回null,反之返回数据
     */
    public Object get(int index){
        if (index > this.count){
            return null;
        }
        this.foot = 0;
        return this.root.getNode(index);
    }
    /**
     * 根据索引修改数据
     * @param index 要修改数据的索引编号
     * @param data 新的内容数据
     */
    public void set(int index,Object data){
        if (index > this.count){            //判断是否超过了保存范围
            return;                         //结束方法调用
        }
        this.foot = 0;                      //重新设置foot属性的内容,作为索引出现
        this.root.setNode(index,data);      //交给Node类设置数据内容
    }

    /**
     * 链表数据的删除操作,在删除前要先使用contain()判断链表中是否存在指定数据
     * 如果要删除的数据存在,则首先判断根节点的数据是否为要删除数据
     * 如果是,则将根节点的下一个节点作为新的根节点
     * 如果要删除的数据不是根节点数据,则将删除操作交由Node类的removeNode()方法完成
     * @param data 要删除的数据
     */
    public void remove(Object data){
        if (this.contains(data)){
            if (data.equals(this.root.data)){
                this.root = this.root.next;
            }else {
                //此时根元素已经判断过了,从第二个元素开始判断,即第二个元素的上一个元素为根节点
                this.root.removeNode(this.root,data);
            }
            this.count--;       //删除成功后个数要减少
        }
    }
    /**
     * 将链表中的数据转换为对象数组输出
     * @return 如果链表没有数据,返回null,如果有数据,则将数据转变为对象数组后返回
     */
    public Object[] toArray(){
        if (this.root == null){     //判断链表是否有数据
            return null;            //没有数据,返回null
        }
        this.foot = 0;                          //脚标清零操作
        this.retArray = new Object[this.count]; //根据保存内容开辟数组
        this.root.toArrayNode();                //交给Node类处理
        return this.retArray;                   //返回数组对象
    }
    /**
     * 清空链表数据
     */
    public void clear(){
        this.root = null;//清空链表
        this.count = 0;//元素个数为0
    }
}
interface Pet{//定义一个宠物的标准
    /**
     * 取得宠物的名字
     * @return 宠物名字信息
     */
    public String getName();

    /**
     * 取得宠物的年龄
     * @return 宠物年龄信息
     */
    public int getAge();

}

class PetShop{//一个宠物商店要保存多个宠物信息
   private Link pets = new Link();//保存的宠物信息
    /**
     * 新的宠物类型上架操作,向链表中保存宠物信息
     * @param
     */
    public void add(Pet pet){
        this.pets.add(pet);     //向链表中保存数据
    }

    /**
     * 宠物下架操作,通过链表删除保存的信息,需要对象比较equals()方法的支持
     * @param  pet 要删除的宠物信息
     */
    public void delete(Pet pet){
        this.pets.remove(pet);//从链表中删除宠物信息
    }

    //模糊查询一定是返回多个内容,不知道多少个,返回link即可
    /**
     * 宠物数据的模糊查询,首先要取得全部保存的宠物信息
     * 然后采取循环的方式依次取出每一种宠物信息,并且对名称进行判断
     * @param keyWorld 模糊查询关键字
     * @return 宠物信息通过Link类型返回,如果有指定查询关键字的宠物信息则通过Link集合返回,否则返回null
     */
    public Link search(String  keyWorld){
        Link result = new Link();//保存结果
        //将集合变成为对象数组的形式返回,因为集合保存的是Object
        //但是真正要查询的数据在Pet接口对象的getName()方法的返回值
        Object obj[] = this.pets.toArray();
        for (int x = 0;x < obj.length;x++){
            Pet p = (Pet) obj[x];//向下转型找到具体的宠物对象
            if (p.getName().contains(keyWorld)){//查询到了
                result.add(p);//保存满足条件的结果
            }
        }
        return result;
    }
}

class Cat implements Pet{
    private String name;
    private int age;
    public Cat(String name,int age){
        this.name = name;
        this.age = age;
    }

    public boolean equals(Object obj){
        if (this == obj){
            return true;
        }
        if (obj == null){
            return false;
        }
        if (!(obj instanceof Cat)){
            return false;
        }
        Cat c = (Cat) obj;
        if (this.name.equals(c.name) && this.age == c.age){
            return true;
        }
        return false;
    }
    public String getName(){    //覆写接口中的方法
        return this.name;
    }

    public int getAge(){    //覆写接口中的方法
        return this.age;
    }
    public String toString(){
        return "猫的名字:" + this.name + ",年龄:" + this.age;
    }
}

class Dog implements Pet{
    private String name;
    private int age;
    public Dog(String name,int age){
        this.name = name;
        this.age = age;
    }

    public boolean equals(Object obj){//判断对象及属性是否相同的方法
        if (obj == null){//对象为空
            return false;//返回false
        }
        if (this == obj){//如果对象相同
            return true;
        }
        if (!(obj instanceof Dog)){//判断对象是否为Dog的实例
            return false;
        }
        Dog c = (Dog) obj;//如果是Dog的实例,进行转型,如果不转型,用的还是Object类
        //没有Dog类所拥有的属性,要转换为自己需要用的类来取值
        if (this.name.equals(c.name) && this.age == c.age){
            //判断名字属性和年龄属性是否相同
            return true;
        }
        return false;
    }
    public String getName(){    //覆写接口中的方法
        return this.name;
    }

    public int getAge(){    //覆写接口中的方法
        return this.age;
    }
    public String toString(){
        return "狗的名字:" + this.name + ",年龄:" + this.age;
    }
}

public class TestDemo11 {
    public static void main(String[] args) {
        PetShop shop = new PetShop();//实例化宠物商店
        shop.add(new Cat("波斯猫",1));
        shop.add(new Cat("波米拉猫",1));
        shop.add(new Cat("迪罗猫",2));
        shop.add(new Dog("松狮",1));
        shop.add(new Dog("波尔多",2));
        shop.delete(new Cat("波米拉猫",1));
        Link all = shop.search("波");
        Object obj[] = all.toArray();
        for (int x = 0;x < obj.length;x++){
            System.out.println(obj[x]);
        }
    }
}

程序结果
本程序主要的功能就是利用链表操作宠物信息,在增加、删除宠物信息时,接收的参数都是pet接口类型,这样只要是此接口的子类对象都可以进行链表操作。而在进行模糊信息查询时,由于满足条件的宠物名称会存在多个,所以方法返回类型为link。

接收的参数都是pet接口类型,将宠物信息向上转型了,都转成pet类型。
宠物商店的程序看了好久,因为其中蕴含的向下转型的知识没有掌握好,导致看不懂代码。

现在能大致的理解向下转型和向上转型的意思了,当我们的程序包含很多东西,我们要把这些东西放在一个统一的集合里,我们需要向上转型,以统一的标准保存这些东西,当我们需要这些东西的特有的属性时,我们需要把这些东西从集合中取出,向下转型,让他们再次得到自己的私有属性。从这个过程中也可以看出,为什么说向下转型的过程是先向上转型,再向下转型。

发布了48 篇原创文章 · 获赞 9 · 访问量 7906

猜你喜欢

转载自blog.csdn.net/trh_csdn/article/details/104443817