在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