比较器(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开发中此内容比较有用,因为要维护一个容器不关闭才可以一直定时调度下去。