|
Java 库的建立方法及其实例(5) 接下来,我们来看看Pattern类的内容。这里有两种方法,一种是直接阅读源代码,另外一种是先用 工具分析一下Pattern类的内容。这里,我采用第二种方法,用javap来看类的内容。
[games]$javap -classpath .. -private jregex.Pattern
Compiled from jregex/Pattern.java
public class jregex.Pattern extends java.lang.Object implements java.io.Serializable, jregex.REFlags {
java.lang.String stringRepr;
jregex.Term root;
jregex.Term root0;
int memregs;
int counters;
int lookaheads;
java.util.Hashtable namedGroupMap;
private jregex.Pattern() throws jregex.PatternSyntaxException;
public jregex.Pattern(java.lang.String) throws jregex.PatternSyntaxException;
public jregex.Pattern(java.lang.String,java.lang.String) throws jregex.PatternSyntaxException;
public jregex.Pattern(java.lang.String,int) throws jregex.PatternSyntaxException;
private void compile(java.lang.String, int) throws jregex.PatternSyntaxException;
public int groupCount();
public java.lang.Integer groupId(java.lang.String);
public jregex.Matcher matcher();
public jregex.Matcher matcher(java.lang.String);
public jregex.Matcher matcher(char[], int, int);
public jregex.Matcher matcher(jregex.MatchResult, int);
public jregex.Matcher matcher(jregex.MatchResult, java.lang.String);
public jregex.Matcher matcher(java.io.Reader, int) throws java.io.IOException;
public jregex.Replacer replacer(java.lang.String);
public jregex.Replacer replacer(jregex.Substitution);
public jregex.RETokenizer tokenizer(java.lang.String);
public jregex.RETokenizer tokenizer(char[], int, int);
public jregex.RETokenizer tokenizer(java.io.Reader, int) throws java.io.IOException;
public java.lang.String toString();
public java.lang.String toString_d();
static int parseFlags(java.lang.String) throws jregex.PatternSyntaxException;
static int parseFlags(char[], int, int) throws jregex.PatternSyntaxException;
private static int getFlag(char) throws jregex.PatternSyntaxException;
}
其中,要关心private和protected成员,因为在使用类的时候,我们只要关心public成员就行了,但 是,要阅读源代码,明白类的构成,就必须注意private和protected成员。
private Pattern() throws PatternSyntaxException{}
public Pattern(String regex) throws PatternSyntaxException{
this(regex,DEFAULT);
}
public Pattern(String regex,String flags) throws PatternSyntaxException{
stringRepr=regex;
compile(regex,parseFlags(flags));
}
public Pattern(String regex, int flags) throws PatternSyntaxException{
stringRepr=regex;
compile(regex,flags);
}
可以看出,构造函数中,有一个缺省的构造函数是private。而第二个调用了最后一个构造函数,用 this()。第三个和最后一个都是用了一个函数compile来完成构造正则表达式的任务。在上面javap的 输出我们也可以看到,compile是一个private函数。
来看看它的说明:
private void compile(String regex,int flags) throws PatternSyntaxException{
Term.makeTree(regex,flags,this);
}
具体到Term类,超出了本文的范围,它采用了hashtable等方法,构造了一个正则表达式,并返回。 而我们关心的是库的结构。从这里我们可以明白一点:构造函数往往需要调用另外一个构造函数来 完成,而不需要把同样的代码在各个构造函数中都实现。同时,也可以采用另外一个private函数来 完成构造函数的功能,而只要在构造函数中调用它就行了。
从Matcher API说明文档中,我们可以看到,有两种通过Pattern实例构造Matcher实例的方法,而在 javap的输出中可以看到,有几种不同的matcher函数。
public Matcher matcher(){
return new Matcher(this);
}
public Matcher matcher(String s){
Matcher m=new Matcher(this);
m.setTarget(s);
return m;
}
public Matcher matcher(char[] data,int start,int end){
Matcher m=new Matcher(this);
m.setTarget(data,start,end);
return m;
}
public Matcher matcher(MatchResult res,int groupId){
Matcher m=new Matcher(this);
if(res instanceof Matcher){
m.setTarget((Matcher)res,groupId);
}
else{
m.setTarget(res.targetChars(),res.start(groupId)+res.targetStart(),res.length(groupId));
}
return m;
}
public Matcher matcher(Reader text,int length)throws IOException{
Matcher m=new Matcher(this);
m.setTarget(text,length);
return m;
}
以上都是用来实现第一种返回matcher的方法。可以看到,这几种实现都是通过new Matcher实例, 以this(即Pattern实例)为参数,然后根据参数不同调用Matcher.setTarget()方法。
public Matcher matcher(MatchResult res,String groupName){
Integer id=res.pattern().groupId(groupName);
if(id==null) throw new IllegalArgumentException("group not found:"+groupName);
int group=id.intValue();
return matcher(res,group);
}
这是第二种返回matcher的方法。
从这里我们可以发现一种比较好的方法:当你需要两个互相关联的类,一个类的实例需要构造另一 个类的实例,可以在第一个类中用一个public方法,方法实现为调用第二个类的构造函数,并可以 用构造出来的实例调用其他方法,根据第一个类的实例的数据来设置第二个类的实例。
同样的,也有Replacer, 和Tokenizer的构造方法。
public Replacer replacer(String expr){
return new Replacer(this,expr);
}
/**
* Returns a replacer will substitute all occurences of a pattern
* through applying a user-defined substitution model.
* @param model a Substitution object which is in charge for match substitution
* @see Replacer
*/
public Replacer replacer(Substitution model){
return new Replacer(this,model);
}
public RETokenizer tokenizer(String text){
return new RETokenizer(this,text);
}
/**
* Tokenizes a specified region by an occurences of the pattern.
* Note that a series of adjacent matches are regarded as a single separator.
* The same as new RETokenizer(Pattern,char[],int,int);
* @see RETokenizer
* @see RETokenizer#RETokenizer(jregex.Pattern,char[],int,int)
*/
public RETokenizer tokenizer(char[] data,int off,int len){
return new RETokenizer(this,data,off,len);
}
/**
* Tokenizes a specified region by an occurences of the pattern.
* Note that a series of adjacent matches are regarded as a single separator.
* The same as new RETokenizer(Pattern,Reader,int);
* @see RETokenizer
* @see RETokenizer#RETokenizer(jregex.Pattern,java.io.Reader,int)
*/
public RETokenizer tokenizer(Reader in,int length) throws IOException{
return new RETokenizer(this,in,length);
}
回忆一下,我在本文的开头,曾经提到过:"一个好的库必须是一个紧凑的关系紧密的整体,而不 是一个分散的关系松散的对象的集合。"从API说明文档所显示的这个库的树形结构,并不能看出这 些类之间的联系。而从源代码的角度,我们则可以清楚地看到这一点。在这一部分的讨论中,我们 也明白了两点:
1、如何编写重载构造函数
2、在一个类的实例中返回另外一个类的实例 (未完待续)
|
