比较器(Comparable、Comparator)

Comparable接口

可以直接使用java.util.Arrays类进行数组的排序操作,但对象所在的类必须实现Comparable接口,用于指定排序接口。

Comparable接口定义如下:

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

从接口的定义格式上来看,可以发现如果想实现对象属性的排序功能,要实现Comparable接口,并且覆写compareTo(T o)方法。此方法返回的是一个int类型的数据,但是此int类型的数据只能是一下三种:

·1:表示大于

·-1:表示小于

·0:表示等于

看如下的实例:一个学生类,里面有三个属性姓名、年龄、成绩三个属性,要求按成绩由高到底排序,如果成绩相等,则按照年龄由底到高排序。

class Student implements Comparable<Student>	//指定类型为Student
{
	private String name;
	private int age;
	private float score;
	public Student(String name,int age,float score){
		this.name=name;
		this.age=age;
		this.score=score;
	}
	public String toString(){
		return name+"\t\t"+age+"\t\t"+score+"\t\t";
	}
	public int compareTo(Student stu){	//覆写compareTo方法,实现排序规则的应用
		if(this.score<stu.score){
			return -1;
		}
		if(this.score>stu.score){
			return 1;
		}
		else{
			if(this.age<stu.age){
				return -1;
			}
			if(this.age>stu.age){
				return 1;
			}
			else{
				return 0;
			}
		}
	}
}
public class ComparableDemo01
{
	public static void main(String args[]){
		Student stu[]={
			new Student("张三",20,89.1f),
			new Student("李四",26,53.5f),
			new Student("王五",30,79.0f),
			new Student("赵六",20,66.0f),
			new Student("孙七",25,66.0f)
		};
		java.util.Arrays.sort(stu);	//进行排序操作
		for(Student x:stu){	//循环输出
			System.out.println(x);
		}
	}
}

注意:如果在此时Student类没有实现Comparable接口,则在执行的时候会出现一下异常:

Exception in thread "main" java.lang.ClassCastException: Student cannot be cast
to java.lang.Comparable

另一种比较器Comparator

此比较器其实跟Comparable是非常类似的。如果现在已经完成了一个类的开发,但是并没有实现Comparable接口,此时肯定是无法进行对象排序操作的,所以为了解决这种问题,java又定义了另一个比较器的操作接口---Comparator。此接口在java.util包中,接口如下定义:

pubic interface Comparator<T>{
  public int compare(T o1,T o2);
  boolena equals(Object obj);
}

代码如下:

import java.util.*;
class Student 	 {
	private String name;
	private int age;
	private float score;
	public Student(String name,int age,float score){
		this.name=name;
		this.age=age;
		this.score=score;
	}
	public void setName(String name){
		this.name=name;
	}
	public String getName(){
		return name;
	}
	public void setAge(int age){
		this.age=age;
	}
	public int getAge(){
		return age;
	}
	public String toString(){
		return name+"\t\t"+age+"\t\t"+score+"\t\t";
	}
}
class StudentComparator implements Comparator<Student>
{
	//因为Object类中已经覆写过equals方法
	public int compare(Student s1,Student s2){
		if(s1.equals(s2)){
			return 0;
		}	
		else if(s1.getAge()<s2.getAge()){	//按照年龄进行比较
			return 1;
		}
		else{
			return -1;
		}
	}
}
public class ComparableDemo02
{
	public static void main(String args[]){
		Student stu[]={
			new Student("张三",20,89.1f),
			new Student("李四",26,53.5f),
			new Student("王五",30,79.0f),
			new Student("赵六",20,66.0f),
			new Student("孙七",25,66.0f)
		};
		java.util.Arrays.sort(stu,new StudentComparator());	//进行排序操作
		for(Student x:stu){	//循环输出
			System.out.println(x);
		}
	}
}

总结:在开发中尽量还是使用Comparable在需要排序的类上实现好此接口,而Comparator需要单独建立一个排序的类,这样如果有很多的话,则排序的规则类也就会很多,操作起来比较麻烦些。注意:在java中只要是对象排序,永远都是以Comparable接口为准的。

观察者设计模式

具体内容

所谓观察者设计模式,就是某一样东西被多个观察者观察,当这样东西发生变化,则会及时反馈到每个观察者的身上。如果要想实现观察者设计模式,则必须依靠java.util包中的Observable类和Observer接口。

例子:现在很多的购房者都在关注着房子的变化,每当房子价格变化的时候,所有的观察者都能够立即观察到

import java.util.* ;
class House extends Observable{	// 表示房子可以被观察
	private float price ;// 价钱
	public House(float price){
		this.price = price ;
	}
	public float getPrice(){
		return this.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 o,Object arg){
		if(arg instanceof Float){
			System.out.print(this.name + "观察到价格更改为:") ;
			System.out.println(((Float)arg).floatValue()) ;
		}
	}
};
public class ObserDemo01{
	public static void main(String args[]){
		House h = new House(1000000) ;
		HousePriceObserver hpo1 = new HousePriceObserver("购房者A") ;
		HousePriceObserver hpo2 = new HousePriceObserver("购房者B") ;
		HousePriceObserver hpo3 = new HousePriceObserver("购房者C") ;
		h.addObserver(hpo1) ;
		h.addObserver(hpo2) ;
		h.addObserver(hpo3) ;
		System.out.println(h) ;	// 输出房子价格
		h.setPrice(666666) ;	// 修改房子价格
		System.out.println(h) ;	// 输出房子价格
	}
};

正则表达式

正则表达式可以方便的对数据进行匹配,可以执行更加复杂的字符串验证、拆分、替换等功能。

例如:现在要求判断一个字符串是否由数字组成,有以下两种方式:

·不使用正则完成

·使用正则完成

第一种方式代码:

public class RegexDemo01{
	public static void main(String args[]){
		String str = "1234567890" ;		// 此字符串由数字组成
		boolean flag = true ;			// 定义一个标记变量
		// 要先将字符串拆分成字符数组,之后依次判断
		char c[] = str.toCharArray() ;	// 将字符串变为字符数组
		for(int i=0;i<c.length;i++){	// 循环依次判断
			if(c[i]<'0'||c[i]>'9'){		// 如果满足条件,则表示不是数字
				flag = false ;			// 做个标记
				break ;					// 程序不再向下继续执行
			}
		}
		if(flag){
			System.out.println("是由数字组成!") ;
		}else{
			System.out.println("不是由数字组成!") ;
		}
	}
};

第二中方式代码:

import java.util.regex.Pattern ;
public class RegexDemo02{
	public static void main(String args[]){
		String str = "1234567890" ;		// 此字符串由数字组成
		if(Pattern.compile("[0-9]+").matcher(str).matches()){	// 使用正则
			System.out.println("是由数字组成!") ;
		}else{
			System.out.println("不是由数字组成!") ;
		}
	}
};

Pattern、Matcher类

这两个类为正则的核心操作类,都是定义在java.util.regex包中。Pattern类主要是进行正则规范(如之前的操作“[0-9]“就属于正则规范)的编写,而Matcher类主要是执行规范,验证一个字符串是否符合规范。

常用正则规则:

·\d:表示数字, [0-9]

·\D:表示非数字,[^0-9]

·\w:表示字母、数字、下划线,[a-zA-Z0-9]

·\W:[^a-zA-Z0-9]

常用正则规范2:

以上的正则,如果要想驱动起来,则必须依靠Pattern和Matcher类。

Pattern主要是表示一个规则的意思,即:正则表达式的规则需要在Pattern类中使用。

Matcher类主要表示使用Pattern指定好的规则验证。

Pattern类的常用方法:

方法

描述

public static Pattern compile(String regex)

指定正则表达式规则

public Matcher matcher(CharSequence input)

返回Mather类的实例

public String[] split(CharSequence input)

字符串拆分

在Patter类中如果要想取得Pattern类的实例,则必须调用compile()方法。

本类中没有明确的构造方法可以使用,那么此类的构造方法肯定被私有化了,如果是这样的类,都可以通过本类中的一个静态方法获得该类的实例。

Matcher类中的常用方法:

方法

描述

Public Boolean matches()

执行验证

Public String replaceAll(String replacement)

字符串替换

实例操作:(验证日期是否合法)

import java.util.regex.Pattern ;
import java.util.regex.Matcher ;
public class RegexDemo03{
	public static void main(String args[]){
		String str = "1983-07-27" ;		// 指定好一个日期格式的字符串
		String pat = "\\d{4}-\\d{2}-\\d{2}" ;	// 指定好正则表达式
		Pattern p = Pattern.compile(pat) ;	// 实例化Pattern类
		Matcher m = p.matcher(str) ;	// 实例化Matcher类
		if(m.matches()){		// 进行验证的匹配,使用正则
			System.out.println("日期格式合法!") ;
		}else{
			System.out.println("日期格式不合法!") ;
		}
	}
};

在Pattern类中也可以使用正则进行字符的拆分功能:

import java.util.regex.Pattern ;
import java.util.regex.Matcher ;
public class RegexDemo04{
	public static void main(String args[]){
		// 要求将里面的字符取出,也就是说按照数字拆分
		String str = "A1B22C333D4444E55555F" ;	// 指定好一个字符串
		String pat = "\\d+" ;	// 指定好正则表达式
		Pattern p = Pattern.compile(pat) ;	// 实例化Pattern类
		String s[] = p.split(str) ;	// 执行拆分操作
		for(int x=0;x<s.length;x++){
			System.out.print(s[x] + "\t") ;
		}
	}
};

还可以使用Matcher类中的字符串替换功能:

import java.util.regex.Pattern ;
import java.util.regex.Matcher ;
public class RegexDemo05{
	public static void main(String args[]){
		// 要求将里面的字符取出,也就是说按照数字拆分
		String str = "A1B22C333D4444E55555F" ;	// 指定好一个字符串
		String pat = "\\d+" ;	// 指定好正则表达式
		Pattern p = Pattern.compile(pat) ;	// 实例化Pattern类
		Matcher m = p.matcher(str) ;	// 实例化Matcher类的对象
		String newString = m.replaceAll("_") ;
		System.out.println(newString) ;
	}
};

String类对正则的支持

在String类中有以下三个方法是支持正则操作的:

方法

描述

public boolean matches(String regex)

字符串匹配

public String replaceAll(String regex,String replacement)

字符串替换

public String[] split(String regex)

字符串拆分

代码如下:

import java.util.regex.Pattern ;
import java.util.regex.Matcher ;
public class RegexDemo06{
	public static void main(String args[]){
		String str1 = "A1B22C333D4444E55555F".replaceAll("\\d+","_") ;
		boolean temp = "1983-07-27".matches("\\d{4}-\\d{2}-\\d{2}") ;
		String s[] = "A1B22C333D4444E55555F".split("\\d+") ;
		System.out.println("字符串替换操作:" + str1) ;
		System.out.println("字符串验证:" + temp) ;
		System.out.print("字符串的拆分:") ;
		for(int x=0;x<s.length;x++){
			System.out.print(s[x] + "\t") ;
		}
	}
};

但是,使用正则需要注意一点,看如下的代码:

import java.util.regex.Pattern ;
import java.util.regex.Matcher ;
public class RegexDemo07{
	public static void main(String args[]){
		String info = "LXH:98|MLDN:90|LI:100" ;			// 定义一个字符串
		// 拆分的形式:
		/*
			LXH	-->	98
			MLDN	-->	90
			LI	-->	100
		*/
		String s[] = info.split("\\|") ;
		System.out.println("字符串的拆分:") ;
		for(int x=0;x<s.length;x++){
			String s2[] = s[x].split(":") ;
			System.out.println(s2[0] + "\t" + s2[1]) ;
		}
	}
};

如果有时候发现一个字符串无法按照指定的字符拆分的话,则需要使用“\”转义,转义的时候连个“\”表示一个“\”。

总结:

1、  使用正则可以方便完成字符串的验证、拆分、替换等功能。

2、  在开发中一般会使用String类提供好的正则支持,很少使用Pattern或者是Matcher类。

3、  在使用一些正则的时候,对于一些敏感的字符要进行转义。

定时调度

定时调度:每隔一段时间,程序会自动执行,成为定时调度。

如果要使用定时调度,则必须保证程序运行这才可以,也就说是相当于定时调度在程序之外又启动了一个新的线程。使用Timer和TimerTask两个类完成定时调度。

Timer类

Timer类是一种线程设施,可以用来实现在某一个时间或某一段时间后,安排某一个任务执行一次,或定期重复执行。该功能要与TimerTask类配合使用。TimerTask类用来实现由Timer安排的一次或重复执行的某一个任务。

Timer类的常用方法:

方法

类型

描述

public Timer()

构造

用来创建一个计时器并启动

 

public void cancel()

普通

 

用来终止该计时器,并放弃所有已安排的任务,对当前正在执行的任务没有影响。

public int purge()

普通

将所有已经取消的任务移除,一般用来释放内存空间

public void schedule(TimerTask task,Date time)

普通

 

安排一个任务在指定的时间执行,如果已经超过该时间,则立即执行。

public void scheduleAtFixedRate(TimerTask task,Date firstTime,long period)

普通

安排一个任务在指定的时间执行,之后以近似固定的频率(单位:毫秒)重复执行

Schedule()与scheduleAtFixedRate()方法的区别:

两者的区别在于重复执行任务时,对于时间间隔出现延迟的情况处理:

·Schedule()方法的执行时间间隔永远是固定的,如果之前出现了延迟的情况,之后也会按照设定好的间隔时间执行。

·scheduleAtFixedRate()方法可以根据出现的延迟时间自动调整下一次间隔的执行时间。

TimerTask类

要想执行具体的任务,则必须使用TimerTask类。TimerTask是一个抽象类,如果要使用该类,需要建立一个子类继承该类,并实现其中的抽象方法。

方法

描述

 

public void cancle()

 

用来终止此任务,如果该任务只执行一次并且还没有执行,则永远不会再执行,如果为重复执行任务,则之后不会再执行(如果该任务正在执行,则执行完后不会再执行)

public void run()

该任务所要执行的具体操作,该方法为引入接口Runable中的方法,子类需要覆写。

 

public long scheduledExecutionTime()

 

返回最近一次执行任务的时间(如果正在运行,则返回该任务的执行安排时间),一般在run()方法中调用,用来判断当前是否有足够的时间来执行完该任务。

代码示例:定时调度的程序,每隔2秒打印一次。


//完成具体的任务操作
import java.util.*;
import java.text.SimpleDateFormat;
class MyTask extends TimerTask	//任务调度类都要继承TimerTask
{
	public void run(){
		SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		System.out.println("当前系统时间为:"+sd.format(new Date()));
	}
}

完成的是一个具体的任务操作类,以后定时调度就是调度此类的操作,方法的主体就是run()方法。注意要继承TimerTask类,下面建立测试类,并执行任务调度:

import java.util.*;
public class TestTask
{
	public static void main(String args[]){
		Timer t=new Timer();
		MyTask mytask=new MyTask();
		t.schedule(mytask,1000,2000);
	}
}

总结:一般在web开发中此内容比较有用,因为要维护一个容器不关闭才可以一直定时调度下去。