11. JAVA常用类库 Part 4 (对象克隆技术clone、Arrays类、Comparable接口、比较器Comparator、观察者设计模式) ----- 学习笔记

来源:互联网 发布:和人工智能聊天的软件 编辑:程序博客网 时间:2024/04/30 05:57
11.11 对象克隆技术 clone

        在java中支持对象的克隆操作,直接使用Object类中的clone()方法即可。其方法定义如下:

protected Object clone() throws CloneNotSupportedException

        上面的方法是受保护类型,所以在子类中必须覆写此方法,而且覆写后应该扩大该方法的访问权限,这样才能被外部调用

        但是具体的克隆方法的实现还是在Object中,所以在覆写的方法中只需要调用Object类中的clone()方法即可完成操作,而且在对象所在的类中必须实现Cloneable接口才可以完成对象的克隆操作

        JDK文档中,Cloneable接口中没有定义任何的方法,此接口在设计上称为一种标识接口,表示对象可以被克隆!!!!

范例:对象的克隆操作

/* * 对象克隆操作*/package org.forfan06.clonedemo;class Person implements Cloneable{         //必须实现Cloneable接口    private String name = null;    public Person(String name){        this.name = name;    }    public String getName(){        return name;    }    public void setName(String name){        this.name = name;    }    public String toString(){  //覆写toString方法        return "姓名:" + this.getName();    }    public Object clone() throws CloneNotSupportedException {        return super.clone();    }}public class CloneDemo01{    public static void main(String args[]){        Person per1 = new Person("forfan06");        Person per2 = null;        try {        per2 = (Person) per1.clone();   //一定要进行异常的捕获处理        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        System.out.println("原对象:" + per1);        System.out.println("克隆对象:" + per2);                per2.setName("CSDN");        System.out.println("修改属性后的克隆对象:" + per2);    }}

运行结果:

原对象:姓名:forfan06克隆对象:姓名:forfan06修改属性后的克隆对象:姓名:CSDN


11.12 Arrays类

   Arrays类是数组的操作类,定义在java.util包中,主要功能是实现数组元素的查找、数组内容的填充、排序等等。其常用方法如下:

<span style="font-size:12px;">public static boolean equals(int[] a, int[] a2)           //判断两个数组是否相等,此方法被重载多次,可以判断各种数据类型的数组</span>
<span style="font-size:12px;">public static void fill(int[] a, int val)             //将指定内容填充到数组之中,此方法被重载多次,可以填充各种数据类型的数组</span>
<span style="font-size:12px;">public static void sort(int[] a)                      //数组排序,此方法被重载多次,可以对各种类型的数组进行排序</span>
<span style="font-size:12px;">public static int binarySearch(int[] a, int key)      //对排序后的数组进行检索,此方法被重载多次,可以对各种类型的数组进行搜索</span>
<span style="font-size:12px;">public static String toString(int[] a)           //此方法被重载多次,可以输出各种数据类型的数组</span>

范例:操作Arrays类

/* * 操作Arrays类*/package org.forfan06.arraydemo;import java.util.Arrays;public class ArrayDemo01{    public static void main(String args[]){        int temp[] = {3, 5, 9, 7, 1, 15, 27, 38, 4, 2, 98};        System.out.println("原始数组:");        System.out.println(Arrays.toString(temp));                Arrays.sort(temp);        System.out.println("排序后的数组:");        System.out.println(Arrays.toString(temp));                int point1 = Arrays.binarySearch(temp, 4);        System.out.println("元素‘4’的位置在:" + point1);                int point2 = Arrays.binarySearch(temp, 0);        System.out.println("元素‘4’的位置在:" + point2);                Arrays.fill(temp, 4, 5, 77);        System.out.println("数组填充:");        System.out.println(Arrays.toString(temp));    }}

运行结果:

原始数组:[3, 5, 9, 7, 1, 15, 27, 38, 4, 2, 98]排序后的数组:[1, 2, 3, 4, 5, 7, 9, 15, 27, 38, 98]元素‘4’的位置在:3元素‘4’的位置在:-1数组填充:[1, 2, 3, 4, 77, 7, 9, 15, 27, 38, 98]


11.13 Comparable接口

          11.13.1 比较器的基本应用

          使用java.util.Arrays类进行数组的排序操作时,Arrays类中的sort()方法被重载多次,可以对任意类型的数组排序,排列时会根据数值的大小进行排序。同样此类也可以对Object数组进行排序,但是要使用此种方法排序需要对象所在的类必须实现Comparable接口,此接口就是用于指定对象排序规则的

       Comparable接口的定义如下:

public interface Comparable<T>{   public int compareTo(T o);}

     在Comparable接口中使用了Java的泛型技术。该接口中只有一个compareTo方法,此方法返回一个int类型的数据。其返回值int 的值只能是以下3种:

  • 1:  表示大于
  • -1: 表示小于
  • 0:  表示等于

范例:设计一个学生类,该类包含姓名、年龄、成绩 ,并产生一个对象数组,要求按照成绩由高到低排序,如果成绩相等,则按年龄由低到高排序。 使用Arrays类中的sort()方法进行排序操作。

package org.forfan06.comparabledemo;<span style="color:#ff0000;"><strong>class Student implements Comparable<Student></strong></span>{     //指定泛型类型为Student类型    private String name;    private int age;    private float score;    public Student(String name, int age, float score){        this.setName(name);        this.setAge(age);        this.setScore(score);    }    public void setName(String name){        this.name = name;    }    public void setAge(int age){        this.age = age;    }    public void setScore(float score){        this.score = score;    }    public String getName(){        return name;    }    public int getAge(){        return age;    }    public float getScore(){        return score;    }        public String toString(){        return this.getName() + "\t\t" + this.getAge() + "\t\t" + this.getScore();    }        public int compareTo(Student stu){       //覆写compareTo()方法,实现排序规则应用        if(this.getScore() > stu.getScore()){            return -1;        }else if(this.getScore() < stu.getScore()){            return 1;        }else{            if(this.getAge() > stu.getAge()){                return 1;            }else if(this.getAge() < stu.getAge()){                return -1;            }else{                return 0;            }        }    }}public class ComparableDemo01{    public static void main(String args[]){        Student stu[] = {new Student("forfan06", 27, 75.8f), new Student("dylan", 15, 13.2f), new Student("csdn", 35, 13.2f)};        java.util.Arrays.sort(stu);           //调用排序方法进行排序        for (int i = 0; i < stu.length; i++){      //输出            System.out.println(stu[i]);        }            }}

运行结果:

forfan062775.8dylan1513.2csdn3513.2

  如果此时Student类没有实现Comparable接口,则会发生运行时异常!!!!!!!!此异常是类型转换异常,因为在排序时,所有的对象都将向Comparable类型进行转换,所以,一旦没有实现该接口就会出现以下错误

Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparableat java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)at java.util.Arrays.sort(Arrays.java:472)at ComparableDemo01.main(Unknown Source)执行错误

          11.13.2 分析比较器的排序原理

          实际上,上面的程序ComparableDemo01的排序过程就是数据结构中的二叉树排序方法,通过二叉树进行排序,然后利用中序遍历的方式把内容一次读取出来。

       二叉树排序的基本原理就是:将第1个内容作为根节点保存,如果后面的值比根节点的值小,则放在根节点的左子树;如果后面的值比根节点的值大,则放在根节点的右子树。

     按照这样的思路,如果给出了数字8、 3、 10、 9、 1、 5,则保存的树结构如下图所示:

         

然后根据中序遍历的原理(左子树 -->  根节点    -->   右子树的方式)即可将数字排序读取出来,排序后的结果为1、 3、 5、 8、 9、 10。

按照上面的思路编写一个简单的二叉树排序操作。(为了简化代码,直接使用Integer类,因为Integer类本身就已经实现了Comparable接口)

(1)范例:Integer为Comparable接口实例化

/* * Integer为Comparable接口实例化*/package org.forfan06.comparabledemo;public class ComparableDemo02{    public static void main(String args[]){        Comparable comp = null;             //声明一个Comparable接口对象        comp = 30;                         //通过Integerl类为Comparable接口实例化        System.out.println("内容是:" + comp);      //实际上调用的是toString()方法    }}

运行结果:

内容是:30

(2)范例:基于Comparable接口实现的二叉树操作

package org.forfan06.comparabledemo;class BinaryTree{    class Node{                  //声明一个节点类        private Comparable data;              //保存具体内容        private Node left;                    //保存左、右子树        private Node right;        public void addNode(Node newNode){            //要确定是放在左子树,还是右子树。比当前节点小的放在左子树;比当前节点大或相等的放在右子树            if (newNode.data.compareTo(this.data) < 0){                if(this.left == null){      //放在左子树                    this.left = newNode;                }else{                    this.left.addNode(newNode);                }            }            if (newNode.data.compareTo(this.data) >= 0){                if(this.right == null){       //放在右子树                    this.right = newNode;                }else{                    this.right.addNode(newNode);                }            }        }                public void printNode(){           //输出采用中序遍历            if(this.left != null){         //先输出左子树                 this.left.printNode();            }            System.out.print(this.data + "\t");   //再输出根节点            if(this.right != null){               //最后输出右子树                 this.right.printNode();            }        }    }    private Node root;        //根元素    public void add(Comparable data){        Node newNode = new Node();      //每传入一个新的内容就声明一个根节点        newNode.data = data;        if (root == null){            root = newNode;        }else{            root.addNode(newNode);        }    }    public void print(){        this.root.printNode();    }}public class ComparableDemo03{    public static void main(String args[]){        BinaryTree bt = new BinaryTree();        bt.add(8);        bt.add(3);        bt.add(3);        bt.add(10);        bt.add(9);        bt.add(1);        bt.add(5);        bt.add(5);        System.out.println("");        bt.print();    }}

运行结果:

ComparableDemo03.java uses unchecked or unsafe operations.Note: Recompile with -Xlint:unchecked for details.133558910
       上面程序的实现实际上与链表操作类似,只是多增加了一个节点的引用操作。Java中对各个常见的数据结构算法提供了很好的支持,详细操作在Java类集部分讲解!!!


PS: 尝试修改上面的程序,来实现二叉树的另外两种遍历方式!!!!!


11.14 另一种比较器Comparator

        如果一个类已经开发完成,但是在此类建立的初期并没有实现Comparable接口,此时肯定是无法进行对象排序操作的。此时就可以考虑到用java中的另一个比较器的操作接口  ---->>>Comparator

       Comparator接口定义在java.util包中,定义格式如下:

public interface Comparator<T>{     public int compare(T o1, T o2);                   //Compares its two arguments for order     boolean equals(Object obj);                       // Indicates whether some other object is "equal to" this comparator}
  • Comparable<T> interface in java.lang
  • Comparator<T>  interface in java.util

   在Comparator接口中也有一个比较方法compare(T o1, T o2), 此方法要接收2个对象, 其返回值依然是0、 -1、 1

   Comparator接口需要单独指定好一个比较器的比较规则类才可以完成数组排序。下面定义一个学生类,其中有姓名、年龄属性,并按照年龄排序

范例:(1)定义学生类

package org.forfan06.comparatordemo;public class Student{    private String name;    private int age;    public Student(String name, int age){        this.setName(name);        this.setAge(age);    }    public void setName(String name){        this.name = name;    }    public String getName(){        return this.name;    }    public void setAge(int age){        this.age = age;    }    public int getAge(){        return this.age;    }    //@override equals() method    public boolean equals(Object obj){        if (this == obj){            return true;        }        if(!(obj instanceof Student)){            return false;        }        Student stu = (Student) obj;        if(stu.name.equals(this.name) && stu.age == this.age){            return true;        }else{            return false;        }    }    //@override toString()    public String toString(){        return this.getName() + "\t\t" + this.getAge();    }}

(2)定义比较规则

//package org.forfan06.comparatordemo;import java.util.Comparator;public class StudentComparator implements Comparator<Student>{   //指定泛型类型为Student    public int compare(Student s1, Student s2){        if(s1.equals(s2)){            return 0;        }else if(s1.getAge() < s2.getAge()){            return 1;              //倒序排列        }else{            return -1;        }    }}

(3)为对象数组排序

<pre class="java" name="code"><pre class="java" name="code">package org.forfan06.comparatordemo;public class ComparatorDemo{    public static void main(String args[]){        Student stu[] = {new Student("forfan06", 27), new Student("csdn", 21), new Student("dylan", 7), new Student("zhuzhu", 2), new Student("maomao", 3), new Student("gougou", 2), new Student("Sam", 21)};        java.util.Arrays.sort(stu, new StudentComparator());    //排序,指定排序的规则        for(int i = 0; i < stu.length; i++){            System.out.println(stu[i]);        }    }}

运行结果:

forfan0627Sam21csdn21dylan7maomao3gougou2zhuzhu2
  • Comparator接口和Comparable接口都可以实现相同的排序功能,但是与Comparable接口相比,Comparator接口只是一种补救的措施。建议还是使用Comparable接口进行排序比较方便!!


11.15 观察者设计模式

          11.15.1 什么叫观察者

             观察者设计模式: 现在有很多的购房者都在关注着房子的价格变化,每当房子价格变化时,所有的购房者都可以观察到; 实际上以上的购房者都属于观察者,他们都在关注着房子的价格。   这就叫观察者设计模式!!如下图所示:

          

     在java中可以直接依靠Observable类和Observer接口轻松地实现以上功能。

          11.15.2 观察者模式实现

          在java.util包中提供了Observable类和Observer接口,使用它们即可完成观察者模式。

(1)需要被观察的类必须继承Observable类,Observable类的常用方法如下所示:

public void addObserver(Observer o)                              //添加一个观察者
public void deleteObserver(Observer o)                                      //删除一个观察者
protected void setChanged()                           //被观察者状态发生改变
public void notifyObservers(Object arg)                                 //通知所有观察者状态改变

(2)然后每一个观察者类都需要实现Observer接口。Observer接口定义如下:

public interface Observer{    void update(Observable o, Object arg);}

    该接口中只定义了一个update方法,第1个参数表示被观察者实例,第2个参数表示修改的内容

范例:观察着模式的实现

package org.forfan06.observerdemo;import java.util.Observable;import java.util.Observer;class House extends Observable{    private float price;    public House(float price){        this.price = price;    }    public float getPrice(){        return price;    }        public void setPrice(float price){        super.setChanged();                   //设置变化点        super.notifyObservers(price);         //通知所有观察者价格变化        this.price = price;    }        public String toString(){        return "房子价格:" + this.price;    }}class HousePriceObserver implements Observer{    private String name;    public HousePriceObserver(String name){        this.name = name;    }        public void update(Observable obj, Object arg){        if(arg instanceof Float){          //判断参数类型            System.out.print(this.name + "观察到价格更改为:");            System.out.println(((Float) arg).floatValue());        }    }}public class ObserverDemo01{    public static void main(String args[]){        House h = new House(1000000);        HousePriceObserver hpo1 = new HousePriceObserver("AAA");        HousePriceObserver hpo2 = new HousePriceObserver("BBB");        HousePriceObserver hpo3 = new HousePriceObserver("CCC");                h.addObserver(hpo1);        h.addObserver(hpo2);        h.addObserver(hpo3);                System.out.println(h);    //输出房子价格        h.setPrice(666666);        System.out.println(h);    }}

运行结果:

房子价格:1000000.0CCC观察到价格更改为:666666.0BBB观察到价格更改为:666666.0AAA观察到价格更改为:666666.0房子价格:666666.0


0 0
原创粉丝点击