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

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]);
        }
        
    }
}

运行结果:

forfan06		27		75.8
dylan		15		13.2
csdn		35		13.2

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

Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
	at 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.

1	3	3	5	5	8	9	10
       上面程序的实现实际上与链表操作类似,只是多增加了一个节点的引用操作。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]);
        }
    }
}

 
 

运行结果:

forfan06		27
Sam		21
csdn		21
dylan		7
maomao		3
gougou		2
zhuzhu		2
  • 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.0
CCC观察到价格更改为:666666.0
BBB观察到价格更改为:666666.0
AAA观察到价格更改为:666666.0
房子价格:666666.0


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值