??xml version="1.0" encoding="utf-8" standalone="yes"?> /** /** /** /**
很多朋友在深入的接触
JAVA
语言后就会发现这样两个词Q反?/span>
(Reflection)
和内?/span>
(Introspector)
Q经常搞不清楚这到底是怎么回事Q在什么场合下应用以及如何使用Q今天把q二者放在一起介l,因ؓ它们二者是相辅相成的?/span>
反射
相对而言Q反比内省更容易理解一炏V用一句比较白的话来概括,反射是让你可以通过名称来得到对?/span>
(
c,属性,Ҏ
)
的技术。例如我们可以通过cd来生成一个类的实例;知道了方法名Q就可以调用q个ҎQ知道了属性名可以访问这个属性的倹{?/span>
q是写两个例子让大家更直观的了解反射的用方法:
//
通过cd来构造一个类的实?br />
Class cls_str = Class.forName(
"java.lang.String"
);
//
通过Ҏ名来调用一个方?br />
String methodName =
"length"
;
上面的两个例子是比较常用Ҏ。看C面的例子有发问了:Z么要q么ȝ呢?本来一条语句就完成的事情干吗要整这么复杂?没错Q在上面的例子中实没有必要q么ȝ。不q你惛_q样一个应用程序,它支持动态的功能扩展Q也是说程序不重新启动但是可以自动加蝲新的功能Q这个功能用一个具体类来表C。首先我们必Mؓq些功能定义一个接口类Q然后我们要求所有扩展的功能cdd现我指定的接口,q个规定了应用程序和可扩展功能之间的接口规则Q但是怎么动态加载呢Q我们必让应用E序知道要扩展的功能cȝcdQ比如是
test.Func1
Q当我们把这个类?/span>
(
字符?/span>
)
告诉应用E序后,它就可以使用我们W一个例子的Ҏ来加载ƈ启用新的功能。这是cȝ反射Q请问你有别的选择吗?
关于Ҏ的反徏议大家看我的另外一文章?/span>
利用
Turbine
的事件映来扩展
Struts
的功?/span>
》,地址是:
http://www.javayou.com/article/CSDN/extend_struts.html
。这文章详l介l了如果通过反射来扩?/span>
Struts
框架的功能?/span>
内省
内省?/span>
Java
语言?/span>
Bean
cd性、事件的一U缺省处理方法。例如类
A
中有属?/span>
name,
那我们可以通过
getName,setName
来得到其值或者设|新的倹{通过
getName/setName
来访?/span>
name
属性,q就是默认的规则?/span>
Java
中提供了一?/span>
API
用来讉K某个属性的
getter/setter
ҎQ通过q些
API
可以使你不需要了解这个规则(但你最好还是要搞清楚)Q这?/span>
API
存放于包
java.beans
中?/span>
一般的做法是通过c?/span>
Introspector
来获取某个对象的
BeanInfo
信息Q然后通过
BeanInfo
来获取属性的描述器(
PropertyDescriptor
Q,通过q个属性描q器可以获取某个属性对应的
getter/setter
ҎQ然后我们就可以通过反射机制来调用这些方法。下面我们来看一个例子,q个例子把某个对象的所有属性名U和值都打印出来Q?/span>
/*
package
demo;
import
java.beans.BeanInfo;
/**
public
class
IntrospectorDemo {
//
如果不想把父cȝ属性也列出来的话,
}
public
String getName() {
public
void
setName(String name) {
Web
开发框?/span>
Struts
中的
FormBean
是通过内省机制来将表单中的数据映射到类的属性上Q因此要?/span>
FormBean
的每个属性要?/span>
getter/setter
Ҏ。但也ƈ不Lq样Q什么意思呢Q就是说对一?/span>
Bean
cLԌ我可以没有属性,但是只要?/span>
getter/setter
Ҏ中的其中一个,那么
Java
的内省机制就会认为存在一个属性,比如cM有方?/span>
setMobile
Q那么就认ؓ存在一?/span>
mobile
的属性,q样可以方便我们?/span>
Bean
c通过一个接口来定义而不用去兛_具体实现Q不用去兛_
Bean
中数据的存储。比如我们可以把所有的
getter/setter
Ҏ攑ֈ接口里定义,但是真正数据的存取则是在具体cMd玎ͼq样可提高系l的扩展性?/span>
ȝ
?/span>
Java
的反以及内省应用到E序设计中去可以大大的提供程序的化和可扩展性。有很多目都是采取q两U技术来实现其核心功能,例如我们前面提到?/span>
Struts
Q还有用于处?/span>
XML
文g?/span>
Digester
目Q其实应该说几乎所有的目都或多或的采用q两U技术。在实际应用q程中二者要怺l合方能发挥真正的智能化以及高度可扩展性?/span>
在字W串匚w问题中,我们期待察看串T中是否含有串P?br />其中串T被称为目标串Q串S被称为模式串?/p>
q行字符串匹配,最单的一个想法是Q?/p>
可以看见Q这个算法(假定m>>nQ的复杂度是O(mn)Q其中m是T的长度,n是P的长度。这U算法的~陷是匹配过E中带有回溯——准地说是T串存在回溯,也就是当匚w不成功的时候,之前q行的匹配完全变为无用功Q所有的比较需要重新开始?/p>
KMP法是D.E.Knuth、J.H.Morris和V.R.Pratt提出的无回溯的字W串匚w法Q算法的核心思想是设法在匹配失败的时候,量利用之前的匹配结果,消除T串的回溯问题。那么如何消除回溯呢Q请看下面的例子Q?/p>
假设P=abacdQ如果T=abax...Q当从头开始匹配到字符cӞ若c=xQ显Ӟ匚wq程l箋Q当c≠xӞ按照朴素的匹配算法,T串会发生回溯Q之后T串会从第2个字Wb开始重新匹配,而不是从匚wp|的字Wx开始l。但是显Ӟ对于上述的匹配过E,T串不需要从b开始重新匹配,它只需要从x开始和P的b字符l箋匚w卛_。如下: 现在的问题是Q按照KMP法Q匹配失败的时候,P串需要重新调整位|,但是调整的依据是什么?Knuth{h发现QP调整位置的依据和P的构造有养I和T无关。具体来_定义失效函数Qf(j)=kQ其?<=k<=jQ且k是得p0p1...pk-1 = pj-k+1pj-k+2...pj成立的最大整数。徏立失效函数的法如下Q?br />public void Build() { 匚wq程如下Q?br />public int Match(String target, int start) { KMP法虽然能够q行字符串匹配,但是Q在实践中字W串匚w往往q要支持通配W,MSpȝ中最常见的通配W是??。其中,?可以代表一个字W(不能没有Q,*可以代表L多个字符Q可以ؓI)。经典的KMP法针对通配W是无能为力的,但是l过单的攚w,KMP法也可以识别通配W?/p>
首先?Q根?的功能,?表示L字符Q也是说在匚wq程中,?永远匚w成功。因此对匚w函数的修改十分简单: 本质上,?q没有修改算法,而仅仅修改了匚w规则——遇?则一定匹配。然?与此不同Q?的作用是匚wL多个字符Q显然我们不能简单的修改匚wq程而满求。如果我们重新思?的作用,我们会发?的另一个作用就是分割PԌ卛_果P=P1*P2Q那么与其说*代表匚wL多个字符Q不如说P的匹配条件是在匹配P1子串后再匚wP2子串?/p>
现在回顾失效函数的作用,如果当匹配到P的j+1位时匚wp|Q那么重新开始匹配的时候,P串的位置调整到f(j)位,直到P串的位置调整?Q则匚w重新开始。但当P=P1*P2Q假如P1已经匚w成功Q而在P2中发生匹配失败,那么P串要需要调整位|,但P串无论如何调_此时也不应该调整?Q最多调整到P2的开始处Q因为P1已经匚wQ只需匚wP2卛_。假如P=abcab*abcabQ失效函数应该是Q注意之前提?的作用)Q?br />a b c a b * a b c a b 因此Q要惌KMP支持*Q那么关键是要重新设计失效函数的建立法Q如下: 法中begin表示每段字符串的开始位|。此外,匚wq程也应该进行相应的修改Q因为字W?对于匚w没有M帮助Q它属于占位W,因此需要蟩q,匚w法如下Q?br />public int Match(String target, int start) { 一个数字逻辑的问题:设计一个识?1011的电路,解这个问题的关键是设计个电路的DFAQ如下: 仔细看看q个状态机Q是不是和KMP的算法有几分cM呢?qƈ不是巧合Q因为KMP法中的失效函数d以等L转化Z个DFA。当然KMP的DFAq比识别11011的DFA要复杂,原因在于KMP接受的输入是全体字符集合Q识?1011的DFA只接??q两个输入。我们知道,一个正则语a和一个DFA是等LQ而KMP计算失效函数的算法,实际上等价于求DFA的过E,f(j)的值实际上表明状态j+1接受C正确的字W时应该回溯到的状态(注意此时输入ƈ没有前进Q。普通的字符串都能看成是一个正则语aQ含有通配W??的字W串也可以等L转换Z个正则表辑ּ。但是,正则语言的集合远比KMP法所能支持的模式集合的更大,期间原因q是刚才提过的输入问题。试想P=p1p2...pnQ当匚w到pj的时候,如果下一个输入字W正是pjQ那么状态机q入下一个状态,如果不是pjQ那么状态机按照实效函数的指C{Ud状态f(j-1)Q也是说KMP状态机的每个状态只能根据输入是否ؓpj来进行{UR而正则表辑ּ所对应的状态机则有所不同Q如果正则语aL=l1l2...lnQ假设这些都是字母,当匹配到lj位的时候,如果下一个输入字W正是ljQ那么状态机q入下一个状态,否则它还可以Ҏ输入的D行{U,例如lj=c1时{换到状态xQlj=c2时状态{换到y{等?/p>
字符串匹配问题是老问题了Qƈ没有太多新意可言Q只不过虽然KMP法十分单,但它的内在含义还是十分深ȝ。横向比较KMP、DFA和正则语a、正则表辑ּ我们会发玎ͼ它们之间存在很多的关联,而这U比较也有利于我们更好的理解q些法Q或者改q这些算法。最后说一句,试图利用目前的框架得KMP法支持全部U类的通配W(对应于正则表辑ּ是x?、x*、x+、{m,n}{等Q是不可能,而我们也不需要这么做Q因为我们还有正则表辑ּ嘛?/p>
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;
public class FileOperate {
private String message;
public FileOperate() {
}
* d文本文g内容
* @param filePathAndName 带有完整l对路径的文件名
* @param encoding 文本文g打开的编码方?br /> * @return q回文本文g的内?br /> */
public String readTxt(String filePathAndName,String encoding) throws IOException{
encoding = encoding.trim();
StringBuffer str = new StringBuffer("");
String st = "";
try{
FileInputStream fs = new FileInputStream(filePathAndName);
InputStreamReader isr;
if(encoding.equals("")){
isr = new InputStreamReader(fs);
}else{
isr = new InputStreamReader(fs,encoding);
}
BufferedReader br = new BufferedReader(isr);
try{
String data = "";
while((data = br.readLine())!=null){
str.append(data+" ");
}
}catch(Exception e){
str.append(e.toString());
}
st = str.toString();
}catch(IOException es){
st = "";
}
return st;
}
* 新徏目录
* @param folderPath 目录
* @return q回目录创徏后的路径
*/
public String createFolder(String folderPath) {
String txt = folderPath;
try {
java.io.File myFilePath = new java.io.File(txt);
txt = folderPath;
if (!myFilePath.exists()) {
myFilePath.mkdir();
}
}
catch (Exception e) {
message = "创徏目录操作出错";
}
return txt;
}
/**
* 多目录创徏
* @param folderPath 准备要在本目录下创建新目录的目录\?例如 c:myf
* @param paths 无限U目录参敎ͼ各目录以单数线区分 例如 a|b|c
* @return q回创徏文g后的路径 例如 c:myfac
*/
public String createFolders(String folderPath, String paths){
String txts = folderPath;
try{
String txt;
txts = folderPath;
StringTokenizer st = new StringTokenizer(paths,"|");
for(int i=0; st.hasMoreTokens(); i++){
txt = st.nextToken().trim();
if(txts.lastIndexOf("/")!=-1){
txts = createFolder(txts+txt);
}else{
txts = createFolder(txts+txt+"/");
}
}
}catch(Exception e){
message = "创徏目录操作出错Q?;
}
return txts;
}
/**
* 新徏文g
* @param filePathAndName 文本文g完整l对路径及文件名
* @param fileContent 文本文g内容
* @return
*/
public void createFile(String filePathAndName, String fileContent) {
try {
String filePath = filePathAndName;
filePath = filePath.toString();
File myFilePath = new File(filePath);
if (!myFilePath.exists()) {
myFilePath.createNewFile();
}
FileWriter resultFile = new FileWriter(myFilePath);
PrintWriter myFile = new PrintWriter(resultFile);
String strContent = fileContent;
myFile.println(strContent);
myFile.close();
resultFile.close();
}
catch (Exception e) {
message = "创徏文g操作出错";
}
}
/**
* 有编码方式的文g创徏
* @param filePathAndName 文本文g完整l对路径及文件名
* @param fileContent 文本文g内容
* @param encoding ~码方式 例如 GBK 或?UTF-8
* @return
*/
public void createFile(String filePathAndName, String fileContent, String encoding) {
try {
String filePath = filePathAndName;
filePath = filePath.toString();
File myFilePath = new File(filePath);
if (!myFilePath.exists()) {
myFilePath.createNewFile();
}
PrintWriter myFile = new PrintWriter(myFilePath,encoding);
String strContent = fileContent;
myFile.println(strContent);
myFile.close();
}
catch (Exception e) {
message = "创徏文g操作出错";
}
}
/**
* 删除文g
* @param filePathAndName 文本文g完整l对路径及文件名
* @return Boolean 成功删除q回true遭遇异常q回false
*/
public boolean delFile(String filePathAndName) {
boolean bea = false;
try {
String filePath = filePathAndName;
File myDelFile = new File(filePath);
if(myDelFile.exists()){
myDelFile.delete();
bea = true;
}else{
bea = false;
message = (filePathAndName+"
删除文g操作出错");
}
}
catch (Exception e) {
message = e.toString();
}
return bea;
}
/**
* 删除文g?br /> * @param folderPath 文g夹完整绝对\?br /> * @return
*/
public void delFolder(String folderPath) {
try {
delAllFile(folderPath); //删除完里面所有内?br /> String filePath = folderPath;
filePath = filePath.toString();
java.io.File myFilePath = new java.io.File(filePath);
myFilePath.delete(); //删除I文件夹
}
catch (Exception e) {
message = ("删除文gҎ作出?);
}
}
/**
* 删除指定文g夹下所有文?br /> * @param path 文g夹完整绝对\?br /> * @return
* @return
*/
public boolean delAllFile(String path) {
boolean bea = false;
File file = new File(path);
if (!file.exists()) {
return bea;
}
if (!file.isDirectory()) {
return bea;
}
String[] tempList = file.list();
File temp = null;
for (int i = 0; i < tempList.length; i++) {
if (path.endsWith(File.separator)) {
temp = new File(path + tempList[i]);
}else{
temp = new File(path + File.separator + tempList[i]);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
delAllFile(path+"/"+ tempList[i]);//先删除文件夹里面的文?br /> delFolder(path+"/"+ tempList[i]);//再删除空文g?br /> bea = true;
}
}
return bea;
}
/**
* 复制单个文g
* @param oldPathFile 准备复制的文件源
* @param newPathFile 拯到新l对路径带文件名
* @return
*/
public void copyFile(String oldPathFile, String newPathFile) {
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPathFile);
if (oldfile.exists()) { //文g存在?br /> InputStream inStream = new FileInputStream(oldPathFile); //d原文?br /> FileOutputStream fs = new FileOutputStream(newPathFile);
byte[] buffer = new byte[1444];
while((byteread = inStream.read(buffer)) != -1){
bytesum += byteread; //字节?文g大小
System.out.println(bytesum);
fs.write(buffer, 0, byteread);
}
inStream.close();
}
}catch (Exception e) {
message = ("复制单个文g操作出错");
}
}
* 复制整个文g夹的内容
* @param oldPath 准备拯的目?br /> * @param newPath 指定l对路径的新目录
* @return
*/
public void copyFolder(String oldPath, String newPath) {
try {
new File(newPath).mkdirs(); //如果文g夹不存在 则徏立新文g?br /> File a=new File(oldPath);
String[] file=a.list();
File temp=null;
for (int i = 0; i < file.length; i++) {
if(oldPath.endsWith(File.separator)){
temp=new File(oldPath+file[i]);
}else{
temp=new File(oldPath+File.separator+file[i]);
}
if(temp.isFile()){
FileInputStream input = new FileInputStream(temp);
FileOutputStream output = new FileOutputStream(newPath + "/" +
(temp.getName()).toString());
byte[] b = new byte[1024 * 5];
int len;
while ((len = input.read(b)) != -1) {
output.write(b, 0, len);
}
output.flush();
output.close();
input.close();
}
if(temp.isDirectory()){//如果是子文g?br /> copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);
}
}
}catch (Exception e) {
message = "复制整个文g夹内Ҏ作出?;
}
}
/**
* Ud文g
* @param oldPath
* @param newPath
* @return
*/
public void moveFile(String oldPath, String newPath) {
copyFile(oldPath, newPath);
delFile(oldPath);
}
* Ud目录
* @param oldPath
* @param newPath
* @return
*/
public void moveFolder(String oldPath, String newPath) {
copyFolder(oldPath, newPath);
delFolder(oldPath);
}
public String getMessage(){
return this.message;
}
}
]]>
//
上面q句很眼熟,因ؓ使用q?/span>
JDBC
讉K数据库的人都用过
J
Object str = cls_str.newInstance();
//
相当?/span>
String str = new String();
Method m = cls_str.getMethod(methodName,
null
);
System.out.println(
"length is "
+ m.invoke(str,
null
));
//
相当?/span>
System.out.println(str.length());
* Created on 2004-6-29
*/
import
java.beans.Introspector;
import
java.beans.PropertyDescriptor;
*
内省演示例子
*
@author
liudong
*/
String name;
public
static
void
main(String[] args)
throws
Exception{
IntrospectorDemo demo =
new
IntrospectorDemo();
demo.setName(
"Winter Lau"
);
//
?/span>
getBeanInfo
的第二个参数填写父类的信?br />
BeanInfo bi = Introspector.getBeanInfo(demo.getClass(),
Object.
class
);
PropertyDescriptor[] props = bi.getPropertyDescriptors();
for
(
int
i=0;i<props.length;i++){
System.out.println(props[i].getName()+
"="
+
props[i].getReadMethod().invoke(demo,
null
));
}
return
name;
}
this
.name = name;
}
}
]]>2 朴素匚w法
public
class
SimpleMatch
{
public
int
StringMatch(String target,String patten)
{
int
tl
=
target.length();
int
pl
=
patten.length();
int
i
=
0
;
int
j
=
0
;
while
(i
<
tl
-
pl
&&
j
<
pl)
{
if
(patten.charAt(j)
==
target.charAt(i
+
j))
j
++
;
else
{
j
=
0
;
i
++
;
}
}
if
(j
==
pl)
return
i;
return
-
1
;
}
public
static
void
main(String[] args)
{
String t
=
"
123456789
"
;
String p
=
"
456
"
;
SimpleMatch sm
=
new
SimpleMatch();
System.out.println(sm.StringMatch(t, p));
}
}
3 KMP法
匚wq程Q?br />P=abacd
T=abax....
^----比较到此处时发生匚wp|
朴素匚w法Q?br />P= abacd
T=abax...
^----回溯到bQ重新开始和P的匹?br />KMP法Q?br />P= abacd
T=abax...
^----T串不回溯Q从x处l匹?/p>
if(pattern == null)
throw new Exception("KMP Exception : null pattern");
array = new int[pattern.Length];
int i = 0, s = pattern.Length;
if(s > 1)
array[0] = 0;
for(i = 1; i < s; i++) {
if(pattern[i] == pattern[array[i - 1]])
array[i] = array[i - 1] + 1;
else
array[i] = 0;
}
}
if(array == null || pattern == null || target == null)
return -1;
int target_index = start;
int pattern_index = 0;
int token_length = target.Length;
int pattern_length = pattern.Length;
while(target_index < token_length && pattern_index < pattern_length) {
if(target[target_index] == pattern[pattern_index]) {
target_index++;
pattern_index++;
} else {
if(pattern_index == begin)
target_index++;
else
pattern_index = array[pattern_index - 1];
}
}
if(pattern_index == pattern_length)
return target_index - pattern_length;
return -1;
}4 支持通配W??的KMP法
...
while(target_index < token_length && pattern_index < pattern_length) {
if(target[target_index] == pattern[pattern_index]|| pattern[pattern_index] == '?') {
target_index++;
pattern_index++;
} else {
...
建立失效函数的过E和匚wq程cMQ修改如下:
...
for(i = 1; i < s; i++) {
if(pattern[i] == pattern[array[i - 1]]|| pattern[i] == '?' || pattern[array[i - 1]] == '?')
array[i] = array[i - 1] + 1;
...
0 0 0 1 2 - 6 6 6 7 8
public void Build() {
if(pattern == null)
throw new Exception("KMP Exception : null pattern");
array = new int[pattern.Length];
int i = 0, s = pattern.Length;
if(s > 1)
array[0] = 0;
int begin = 0;
for(i = 1; i < s; i++) {
if(pattern[i] == '*') {
array[i] = i;
begin = i + 1;
} else if(pattern[i] == pattern[array[i - 1]] || pattern[i] == '?' || pattern[array[i - 1]] == '?')
array[i] = array[i - 1] + 1;
else
array[i] = begin;
}
}
if(array == null || pattern == null || target == null)
return -1;
int target_index = start;
int pattern_index = 0;
int token_length = target.Length;
int pattern_length = pattern.Length;
int begin = 0;
while(target_index < token_length && pattern_index < pattern_length) {
if(pattern[pattern_index] == '*') {
begin = pattern_index + 1;
pattern_index++;
} else if(target[target_index] == pattern[pattern_index] || pattern[pattern_index] == '?') {
target_index++;
pattern_index++;
} else {
if(pattern_index == begin)
target_index++;
else
pattern_index = array[pattern_index - 1];
}
}
if(pattern_index == pattern_length)
return target_index - pattern_length + begin;
return -1;
}5 正则语言和确定状态自动机
6 l语
版权声明Q本文可以自p{载,转蝲时请务必以超链接形式标明文章原始出处和作者信息及本声?
作?langm
原文:http://www.matrix.org.cn/resource/article/44/44056_NoClassDefDoundErr.html
关键?NoClassDefDoundErr ClassNotFoundException
在读q篇文章之前Q你最好了解一下Java的Exception机制?/p>
也许你在开发的q程中经常地见到ClassNotFoundException和NoClassDefFoundErrq两个异常,每每看到之后Q都会一概而论的是cL有找刎ͼ但有些时候见C们的时候又有些疑惑Q至我是这PQؓ什么Java要用两个异常来表C类定义没有扑ֈ那?他们之间有什么区别那Q?/p>
正y今天我又到了这个问题,Z的仔l研I了一下这两个异常的区别?
首先Q?
ClassNotFoundException直接l承与ExceptionQ它是一个checked的异常?
NoClassDefFoundErr l承自Error->LinkageError Q它是一个unchecked的异常?/p>
下面让我们看一下两个异常在API文中的说明
ClassNotFoundExceptionQ?
当应用尝试用字符串名U通过下面的方法装载一个类时这个类的定义却没有扑ֈ时会抛出的异常?
Class.forName
ClassLoader.findSystemClass
ClassLoader.loadClass
NoClassDefFoundErrQ?
当JVM或者ClassLoader实例试装蝲一个类的定义(q通常是一个方法调用或者new表达式创Z个实例过E的一部分Q而这个类定义q没有找时所抛出的错误?
当编译的时候可以找到这个类的定义,但是以后q个cM再存在?/p>
q比较显而易见了吧,d文是很重要的事情。这里我p一下我对这两个cȝ区别的理解?/p>
ClassNotFoundException异常只出现在你的应用E序d的装载类的过E中Q这个异常很多时候出现在我们的应用框架在初始化或者运行中动态装载已配置的类的过E中。这U情况下我们应该首先查我们的配置或者参数是否错误,是否企图装蝲一个ƈ不存在的c,如果配置没有错误Q我们就应该查看Classpath是否配置错误而导致ClassLoader无法扑ֈq个c,也应该检查要装蝲的类是否在一个jar包中而我们在引入q个jar包的q程中是否有遗漏或错误(q里jar包的版本也是一个需要格外注意的问题Q很多时候qjar包版本会造成太多的麻烦)?
NoClassDefFoundErr异常一般出现在我们~译环境和运行环境不一致的情况下,是说我们有可能在编译过后更改了Classpath或者jar包所以导致在q行的过E中JVM或者ClassLoader无法扑ֈq个cȝ定义Q我曄在编译后作了一ơjar包的清理Q然后应用就送给了我一个这LC物Q?/p>
我们l常用SDK开发应用,开发的q程中要引入很多jar包,有些SDK也会讑֮自己的Classpath。编译过E结束后在运行的q程中就要将已开发的应用和所有引入的jar包拷贝到应用服务器的相应目录下才可以q行Q而应用服务器使用的Classpath也很有可能与SDK的不同,在这个过E中有很大的几率造成双方环境不一致。所以很多开发者就会遇到在SDK中可以编译,q行也没有问题,但是同样的程序放到应用服务器上就出现NoClassDefFoundErrq个异常q种情况Q这是让初学者很挠头的一个问题?/p>
以上是我对q两个异常的一点个人理解,希望对各位开发者有所帮助Q可以让各位开发者在以后的开发过E中能够更快的找到问题所在。祝开发顺?/p>
捕获错误最理想的是在编译期Q最好在试图q行E序以前。然而,q所有错误都能在~译期间侦测到。有些问题必dq行期间解决。让错误的缔l者通过一定的Ҏ预先向接收者传递一些适当的信息,使其知道可能发生什么样的错误以及该如何处理遇到的问题,q就是Java的异常控制机制?br />“异常”QExceptionQ这个词表达的是一U正常情况之外的“异常”。在问题发生的时候,我们可能不知具体该如何解冻I但肯定知道已不能不顾一切地l箋下去。此Ӟ必须坚决地停下来Qƈ由某人、某地指出发生了什么事情,以及该采取何U对{。异常机制的另一好处就是能够简化错误控制代码。我们再也不用检查一个特定的错误Q然后在E序的多处地方对其进行控制。此外,也不需要在Ҏ调用的时候检查错误(因ؓ保证有h能捕莯里的错误Q。我们只需要在一个地方处理问题:“异常控制模块”或?#8220;异常控制?#8221;。这样可有效减少代码量,q将那些用于描述具体操作的代码与专门U正错误的代码分隔开。一般情况下Q用于读取、写入以及调试的代码会变得更富有条理?br />若某个方法生一个异常,必须保证该异常能被捕Pq获得正对待。Java的异常控制机制的一个好处就是允许我们在一个地方将_֊集中在要解决的问题上Q然后在另一个地方对待来自那个代码内部的错误。那个可能发生异常的地方叫做“警戒?#8221;Q它是一个语句块Q我们有必要zN警探日夜监视着。生成的异常必须在某个地方被捕获和进行处理,p警察抓到嫌疑犯后要带到警|去询问。这个地方便是异常控制模块?br />“警戒?#8221;是一个try关键字开头后面用花括hh的语句块Q我们把它叫?#8220;try?#8221;。当try块中有语句发生异常时掷出某U异常类的一个对象。异常被异常控制器捕获和处理Q异常控制器紧接在try块后面,且用catch关键字标讎ͼ因此叫做“catch?#8221;。catch块可以有多个Q每一个用来处理一个相应的异常Q因为在“警戒?#8221;内可能发生的异常U类不止一个。所以,异常处理语句的一般格式是Q?br />try {
// 可能产生异常的代?br /> }
catch (异常对象 e) {
//异常 e的处理语?br /> }catch (异常对象 e1) {
//异常 e的处理语?br /> }catch (异常对象 e2) {
//异常 e的处理语?br /> }
即不用try-catchl构Q发生异常时Java的异常控制机制也会捕莯异常Q输出异常的名称q从异常发生的位|打C个堆栈跟t。然后立即终止程序的q行。下面的例子发生了一?#8220;雉”异常Q后面的hello没有被打印?/font>
? 没有作异常控制的E序?/font>
///
public
class
Exception1 {
public
static
void
main(String args[]) {
int
b = 0;
int
a = 3 / b;
System.out.println(
"Hello!"
);
}
}
///
输出l果Q?br />java.lang.ArithmeticException: / by zero
at Exception1.main(Exception1.java:5)
Exception in thread "main" Exit code: 1
There were errors
但是如果使用了try-catch来处理异常,那么在打印出异常信息后,E序q将l箋q行下去。下面是处理了的代码?/font>
///
// Exception2.java
public
class
Exception2 {
public
static
void
main(String args[]) {
try {
int
b = 0;
int
a = 3 / b;
}
catch(ArithmeticException e) {
e.printStackTrace
}
System.out.println(
"Hello!"
);
}
}
///
输出l果Q?br />Exception:
java.lang.ArithmeticException: / by zero
at Exception2.main(Exception1.java:5)
Hello!
与前例不同的是,Hello!被输Z。这是try-catchl构的用处,它异常发生和处理后E序得以“恢复”而不?#8220;中断”?/font>
Z使异常控制机制更地发挥它的功效,Java设计者几乎所以可能发生的异常Q预制了各色各样的异常类和错误类。它们都是从“可掷?#8221;cThrowablel承而来的,它派生出两个cError和Exception。由Errorz的子cd名ؓXXXErrorQ其中词XXX是描q错误类型的词。由Exceptionz的子cd名ؓXXXExceptionQ其中词XXX是描q异常类型的词。Errorcd理的是运行ɾpȝ发生的内部错误,是不可恢复的Q唯一的办法只要终止运行运行程序。因此,客户E序员只要掌握和处理好Exceptioncd可以了?br />ExceptioncL一切异常的栏V现成的异常c非怹多,我们不可能也没有必要全部掌握它。好在异常类的命名规则大致描q出了该cȝ用途,而异常类的方法基本是一L。下面给出lang包中声明的部分异常类?/font>
RuntimeException q行时异?br />NullPointerException 数据没有初始化就使用
IndexOutOfBoundsException 数组或字W串索引界
NoSuchFieldException 文g找不?br />NoSuchMethodException Ҏ没有定义
ArithmeticException 非法术q行
在其他包中也有相关的异常c,例如io包中有IOEceptioncR利用异常的命名规则Q你可以使用下面的DOS命o在包所在的目录查看有什么异常类可用Q?br /> DIR *Eception.class
对于q行时异常RuntimeExceptionQ我们没必要专门为它写一个异常控制器Q因为它们是׃~程不严谨而造成的逻辑错误。只要让出现l止Q它会自动得到处理。需要程序员q行异常处理的是那些非运行期异常?br />Throwable有三个基本方法:
String getMessage() 获得详细的消息?
String toString() q回Ҏcȝ一D늮要说明,其中包括详细的消息(如果有的话)?
void printStackTrace() ?#160; void printStackTrace(PrintStream)
打印用堆栈\径。调用堆栈显C出我们带到异常发生地点的Ҏ调用的顺序?
因ؓExceptioncL一切异常的根,所以对M一个现有的异常c都可以使用上述Ҏ?
异常规范 ?/strong> throws
java库程序员Z使客L序员准确地知道要~写什么代码来捕获所有潜在的异常Q采用一U叫做throws的语法结构。它用来通知那些要调用方法的客户E序员,他们可能从自qҎ?#8220;?#8221;Z么样的异常。这便是所谓的“异常规范”Q它属于Ҏ声明的一部分Q即在自变量Q参敎ͼ列表的后面加上throws 异常cd表。例?br /> void f() throws tooBig, tooSmall, divZero { Ҏ体}
若用下qC码:
void f() [ // ...
它意味着不会从方法里“?#8221;出异常(除类型ؓRuntimeException的异总外,它可能从M地方掷出Q?br />如果一个方法用了异常规范Q我们在调用它时必须使用try-catchl构来捕获和处理异常规范所指示的异常,否则~译E序会报错而不能通过~译。这正是Java的异常控制机制的杰出贡献Q它对可能发生的意外及早预防从而加Z代码的健壮性?br />在用了异常规范的方法声明中Q库E序员用throw语句来掷Z个异常。throw语句的格式ؓQ?br /> thrownew XXXException();
由此可见Qthrow语句掷出的是XXXcd的异常的对象Q隐式的句柄Q。而catch控制器捕获对象时要给Z个句?catch(XXXException e)?br />我们也可以采?#8220;ƺ骗手段”Q用throw语句“?#8221;Z个ƈ没有发生的异常。编译器能理解我们的要求Qƈ使用q个Ҏ的用户当作真的生了那个异常处理。在实际应用中,可将其作为那个异常的一?#8220;占位W?#8221;使用。这样一来,以后可以方便C生实际的异常Q毋需修改现有的代码。下面我们用“ƺ骗手段”l出一个捕获异常的CZE序?/font>
? 本例E演C异常类的常用方法?/font>
///
public
class ExceptionMethods {
publicstaticvoid main(String[] args) {
try {
thrownew Exception("Here's my Exception");
} catch(Exception e) {
System.out.println("Caught Exception");
System.out.println(
"e.getMessage(): " + e.getMessage());
System.out.println(
"e.toString(): " + e.toString());
System.out.println("e.printStackTrace():");
e.printStackTrace();
}
}
}
///
该程序输出如下:
Caught Exception
e.getMessage(): Here's my Exception
e.toString(): java.lang.Exception: Here's my Exception
e.printStackTrace():
java.lang.Exception: Here's my Exception
at ExceptionMethods.main
在一个tryZ潜在的异常可能是多种cd的,那时我们需要用多个catch块来捕获和处理这些异常。但异常发生时掷Z某类异常对象QJava依次逐个查这些异常控制器Q发C掷出的异常类型匹配时执行那以段处理代码Q而其余的不会被执行。ؓ了防止可能遗漏了某一cd常控制器Q可以放|一个捕获Exceptioncȝ控制器。Exception是可以从McL法中“?#8221;出的基本cd。但是它必须攑֜最后一个位|,因ؓ它能够截获Q何异常,从而后面具体的异常控制器不v作用。下面的CZ说明了这一炏V?/font>
? 本例E演C多个异常控制器的排列次序的作用?/font>
///
public
class
MutilCatch {
private staticvoid test(int
i) {
try
{
int
x = i;
if
(x>0)
throw
new
ArithmeticException (
"this is a Arithmetic Exception!"
);
else
if
(x<0)
throw
new
NullPointerException (
"this is a NullPointer Exception!"
);
else
throw
new
Exception(
"this is a Exception!"
);
}
catch
(ArithmeticException e) {
System.out.println(e.toString());
}
catch
(NullPointerException e) {
System.out.println(e.toString());
}
catch
(Exception e) {
System.out.println(e.toString());
}
}
public
static
void
main(String[] args) {
test(-1); test(0); test(1);
}
}
///
q行l果Q?br />java.lang.NullPointerException: this is a NullPointer Exception!
java.lang.Exception: this is a Exception!
java.lang.ArithmeticException: this is a Arithmetic Exception!
如果你把捕获Exception的catch攑֜前面Q编译就通不q?/font>
我们l常会遇到这L情况Q无Z个异常是否发生,必须执行某些特定的代码。比如文件已l打开Q关闭文件是必须的。但是,在try区内位于异常发生点以后的代码Q在发生异常后不会被执行。在catchZ的代码在异常没有发生的情况下不会被执行。ؓ了无论异常是否发生都要执行的代码Q可在所有异常控制器的末用一个finally从句Q在finally块中攄q些代码。(但在恢复内存时一般都不需要,因ؓ垃圾攉器会自动照料一切。)所以完整的异常控制l构象下面这个样子:
try { 警戒区域 }
catch (A a1) { 控制?A }
catch (B b1) { 控制?B }
catch (C c1) { 控制?C }
finally { 必须执行的代码}
///
// FinallyWorks.java
// The finally clause is always executed
public
class FinallyWorks {
staticint count = 0;
publicstaticvoid main(String[] args) {
while(true) {
try {
// post-increment is zero first time:
if(count++ == 0)
thrownew Exception();
System.out.println("No exception");
} catch(Exception e) {
System.out.println("Exception thrown");
} finally {
System.out.println("in finally clause");
if(count == 2) break; // out of "while"
}
}
}
}
///
q行l果Q?br />Exception thrown
in finally clause
No exception
in finally clause
一开始count=0发生异常Q然后进入finally块;q入循环W二轮没有异常,但又执行一ơfinally块,q在其中跛_循环?br />下面我们l出一个有的实用但较ؓ复杂一点的E序。我们创Z一个InputFile的类。它的作用是打开一个文Ӟ然后每次d它的一行内宏V?/font>
? L本文件ƈ昄到屏q上?/font>
///
//: Cleanup.java
// Paying attention to exceptions in constructors
import java.io.*;
class InputFile {
private BufferedReader in;
InputFile(String fname) throws Exception {
try {
in = new BufferedReader(new FileReader(fname));
// Other code that might throw exceptions
} catch(FileNotFoundException e) {
System.out.println("Could not open " + fname);
// Wasn't open, so don't close it
throw e;
} catch(Exception e) {
// All other exceptions must close it
try {
in.close();
} catch(IOException e2) {
System.out.println("in.close() unsuccessful");
}
throw e;
} finally {
// Don't close it here!!!
}
}
String getLine() {
String s;
try {
s = in.readLine();
} catch(IOException e) {
System.out.println("readLine() unsuccessful");
s = "failed";
}
return s;
}
void cleanup() {
try {
in.close();
} catch(IOException e2) {
System.out.println("in.close() unsuccessful");
}
}
}
publicclass Cleanup {
publicstaticvoid main(String[] args) {
try {
InputFile in = new InputFile("Cleanup.java");
String s;
int i = 1;
while((s = in.getLine()) != null)
System.out.println(""+ i++ + ": " + s);
in.cleanup();
} catch(Exception e) {
System.out.println( "Caught in main, e.printStackTrace()");
e.printStackTrace();
}
}
}
///
q行后输出的?行是Q?br />1: //: Cleanup.java
2: // Paying attention to exceptions in constructors
3: import java.io.*;
要说?InputFile的类包含一个构建器和两个方法cleanup和getLine。构建器要打开一个文件fnameQ首先要捕获FileNotFoundExceptioncd常。在它的处理代码中再掷出q个异常(throw e;)。在更高的控制器中试囑օ闭文Ӟq捕捉关闭失败的异常IOException。cleanup()关闭文gQgetLine()L件的一行到字符Ԍ它们都用了异常处理机制。Cleanup是主c,在main()中首先创Z个InputFilecd象,因ؓ它的构徏器声明时用了异常规范Q所以必ȝtry-catchl构来捕获异常?/font>
虽然Javacd提供了十分丰富的异常cdQ能够满绝大多数编E需要。但是,在开发较大的E序Ӟ也有可能需要徏立自q异常cR要创徏自己的异常类Q必M一个现有的异常cdl承——最好在含义上与新异常近伹{创Z个异常相当简单,只要按如下格式写两个构徏器就行:
class MyException extends Exception {
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
q里的关键是“extends Exception”Q它的意思是Q除包括一个Exception的全部含义以外,q有更多的含义。增加的代码数量非常——实际只d了两个构建器Q对MyException的创建方式进行了定义。请CQ假如我们不明确调用一个基cL建器Q编译器会自动调用基c默认构建器。在W二个构建器中,通过使用super关键字,明确调用了带有一个String参数的基cL建器?/font>
? 本例E演C徏立和应用自己的异常类?/font>
///
//: Inheriting.java
// Inheriting your own exceptions
class MyException extends Exception {
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
publicclass Inheriting {
publicstaticvoid f() throws MyException {
System.out.println(
"Throwing MyException from f()");
thrownew MyException();
}
publicstaticvoid g() throws MyException {
System.out.println(
"Throwing MyException from g()");
thrownew MyException("Originated in g()");
}
publicstaticvoid main(String[] args) {
try {
f();
} catch(MyException e) {
e.printStackTrace();
}
try {
g();
} catch(MyException e) {
e.printStackTrace();
}
}
}
///
输出l果Q?br />Throwing MyException from f()
MyException
at Inheriting.f(Inheriting.java:14)
at Inheriting.main(Inheriting.java:22)
Throwing MyException from g()
MyException: Originated in g()
at Inheriting.g(Inheriting.java:18)
at Inheriting.main(Inheriting.java:27)
class MyException2 extends Exception {
public MyException2() {}
public MyException2(String msg) {
super(msg);
}
public MyException2(String msg, int x) {
super(msg);
i = x;
}
public int val() { return i; }
private int i;
}
本章结Q?/font>
J2SE 1.5 以前的版本要求直接?XML 解析器来装蝲配置文gq存储设|。虽然这q是一件困隄事情Qƈ且解析器是^台的标准部分Q但是额外的工作L有点让h烦。最q更新的 java.util.Properties cȝ在提供了一UؓE序装蝲和存储设|的更容易的ҎQ?loadFromXML(InputStream is) ?storeToXML(OutputStream os, String comment) Ҏ?
Properties 基本知识
如果不熟?java.util.Properties c,那么现在告诉您它是用来在一个文件中存储?值对的,其中键和值是用等号分隔的Q如清单 1 所C?
清单 1. 一l属性示?/p>
foo=bar
fu=baz
清?1 装蝲?Properties 对象中后Q您可以找C个键Q?foo ?fu Q和两个| foo ?bar ?fu ?baz Q了。这个类支持?\u 的嵌?Unicode 字符Ԍ但是q里重要的是每一内定w当作 String ?
清单 2 昄了如何装载属性文件ƈ列出它当前的一l键和倹{只需传递这个文件的 InputStream l?load() ҎQ就会将每一个键-值对d?Properties 实例中。然后用 list() 列出所有属性或者用 getProperty() 获取单独的属性?
清单 2. 装蝲属?/p>
import java.util.*;
import java.io.*;
public class LoadSample {
public static void main(String args[]) throws Exception {
Properties prop = new Properties();
FileInputStream fis =
new FileInputStream("sample.properties");
prop.load(fis);
prop.list(System.out);
System.out.println("\nThe foo property: " +
prop.getProperty("foo"));
}
}
q行 LoadSample E序生成如清?3 所C的输出。注?list() Ҏ的输Z?值对的顺序与它们在输入文件中的顺序不一栗?Properties cd一个散列表QhashtableQ事实上是一?Hashtable 子类Q中储存一l键-值对Q所以不能保证顺序?
清单 3. LoadSample 的输?/p>
-- listing properties --
fu=baz
foo=bar
The foo property: bar
XML 属性文?br />q里没有什么新内容?Properties cLLq样工作的。不q,新的地方是从一?XML 文g中装载一l属性。它?DTD 如清?4 所C?
清单 4. 属?DTD
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD for properties -->
<!ELEMENT properties ( comment?, entry* ) >
<!ATTLIST properties version CDATA #FIXED "1.0">
<!ELEMENT comment (#PCDATA) >
<!ELEMENT entry (#PCDATA) >
<!ATTLIST entry key CDATA #REQUIRED>
如果不想l读 XML DTDQ那么可以告诉您它其实就是说在外?<properties> 标签中包装的是一?<comment> 标签Q后面是L数量?<entry> 标签。对每一?<entry> 标签Q有一个键属性,输入的内容就是它的倹{清?5 昄?清单 1中的属性文件的 XML 版本是什么样子的?
清单 5. XML 版本的属性文?/p>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "<properties>
<comment>Hi</comment>
<entry key="foo">bar</entry>
<entry key="fu">baz</entry>
</properties>
如果清单 6 所C,d XML 版本?Properties 文g与读取老格式的文g没什么不同?
清单 6. d XML Properties 文g
import java.util.*;
import java.io.*;
public class LoadSampleXML {
public static void main(String args[]) throws Exception {
Properties prop = new Properties();
FileInputStream fis =
new FileInputStream("sampleprops.xml");
prop.loadFromXML(fis);
prop.list(System.out);
System.out.println("\nThe foo property: " +
prop.getProperty("foo"));
}
}
关于资源l定的说?br />虽然 java.util.Properties cȝ在除了支持键-值对Q还支持属性文件作?XML 文gQ不q的是,没有内置的选项可以?ResourceBundle 作ؓ一?XML 文g处理。是的, PropertyResourceBundle 不?Properties 对象来装载绑定,不过装蝲Ҏ的用是编码到cM的,而不使用较新?loadFromXML() Ҏ?
q行清单 6 中的E序产生与原来的E序相同的输出,?清单 2所C?
保存 XML 属?br />新的 Properties q有一个功能是属性存储到 XML 格式的文件中。虽?store() Ҏ仍然会创Z个类?清单 1 所C的文gQ但是现在可以用新的 storeToXML() Ҏ创徏?清单 5 所C的文g。只要传递一?OutputStream 和一个用于注释的 String 可以了。清?7 展示了新?storeToXML() Ҏ?
清单 7. ?Properties 存储?XML 文g
import java.util.*;
import java.io.*;
public class StoreXML {
public static void main(String args[]) throws Exception {
Properties prop = new Properties();
prop.setProperty("one-two", "buckle my shoe");
prop.setProperty("three-four", "shut the door");
prop.setProperty("five-six", "pick up sticks");
prop.setProperty("seven-eight", "lay them straight");
prop.setProperty("nine-ten", "a big, fat hen");
FileOutputStream fos =
new FileOutputStream("rhyme.xml");
prop.storeToXML(fos, "Rhyme");
fos.close();
}
}
q行清单 7 中的E序产生的输出如清单 8 所C?/p>
清单 8. 存储?XML 文g
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "<properties>
<comment>Rhyme</comment>
<entry key="seven-eight">lay them straight</entry>
<entry key="five-six">pick up sticks</entry>
<entry key="nine-ten">a big, fat hen</entry>
<entry key="three-four">shut the door</entry>
<entry key="one-two">buckle my shoe</entry>
</properties>
l束?br />使用 XML 文gq是使用老式?a=b cd的文件完全取决于您自己。老式文g从内存的角度看肯定是轻量U的。不q,׃ XML 的普遍用,Z会期?XML 格式行hQ因为它已经被广泛用了Q只不过没有用到 Properties 对象。选择完全在您。分析Y件包 private XMLUtils cȝ源代码以获得关于所使用?XML 解析的更多信息?/p>
PTest.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 实现properties文g的读?br /> * @author bbflyerwww
* @date 2006-08-02
*/
public class PTest {
public static void main(String[] args) {
try {
long start = System.currentTimeMillis();
InputStream is = new FileInputStream("conf.properties");
Properties p = new Properties();
p.load(is);
is.close();
System.out.println("SIZE : " + p.size());
System.out.println("homepage : " + p.getProperty("homepage"));
System.out.println("author : " + p.getProperty("author"));
System.out.println("school : " + p.getProperty("school"));
System.out.println("date : " + p.getProperty("date"));
long end = System.currentTimeMillis();
System.out.println("Cost : " + (end - start));
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Java性能的优化(下)
在java中,Q对象)内存地址的比较,是通过==完成的。比?#160;
q样的语句中Q我们认为,如果obj1和obj2的内存地址相同Q则q回true
而equals()通常是比较内容的。这里说“通常” Q是因ؓ在最Ҏ的ObjectcMQequal()函数做的是地址的比较。而在其他几乎所有的cMQequals()都经q重载,q行内容的比较?/p>
而在说equals()的时候我们还涉及hashCode()是因为在有些应用中(比如QHashMap的key是对象)Q必d重蝲equals()的同旉载hashCode()?strong>因ؓjava中默?Object)的hashCode是根据对象的地址计算得到的?/strong>
我们通常不会注意到这个问题,因ؓ我们通常所使用?em>key都是单类型,或者是String, Long{一些特D的对象Q其Ҏ性请参看W者在写java 拷贝和深拷贝时的讨论)Q这时候,q个问题被我们无意间l过?/p>
有h已经概括了这U我们忽略了的情况:“如果你想一个对象A攑օ另一个收集(集合Q对象B里,或者用这个对象A为查找一个元对象在收集对 象B里位|的钥匙QkeyQ,q支持是否容UIisContains()Q,删除攉对象B里的元对象(remove()?Q这L操作Q那么,equals()和hashCode()函数必须开发者自己定义?#8221; Q括号ؓW者添加)
Z便于理解QD一D늨序ؓ例:
q段代码的结果是什么?{案是nullPointerExcetpion.
而把hashCode()的注释去除,E序可以返回正的l果了。ؓ什么呢Q因为:
Map.put(key,value)时根据key.hashCode生成一个内部hash|Ҏq个hash值将对象存放在一个table?/p>
Map.get(key)会比较key.hashCode和equalsҎQ当且仅当这两者相{时Q才能正定位到table。而我们说q,默认的java是对地址q行比较的?/p>
它包括两个类QPattern和Matcher Pattern
一个Pattern是一个正则表辑ּl编译后的表现模式?
Matcher
一个Matcher对象是一个状态机器,它依据Pattern对象做ؓ匚w模式对字W串展开匚w查?
首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经~译后的模式Q然后一个Matcher实例在这个给定的Pattern实例的模?#173;控制下进行字W串的匹配工作?
以下我们分别来看看q两个类Q?
2.Patternc?
Pattern的方法如下: static Pattern compile(String regex)
给定的正则表达式编译ƈ赋予lPatternc?
static Pattern compile(String regex, int flags)
同上Q但增加flag参数的指定,可选的flag参数包括QCASE
INSENSITIVE,MULTILINE,DOTALL,UNICODE CASEQ?CANON EQ
int flags()
q回当前Pattern的匹配flag参数.
Matcher matcher(CharSequence input)
生成一个给定命名的Matcher对象
static boolean matches(String regex, CharSequence input)
~译l定的正则表辑ּq且对输入的字串以该正则表达式ؓ模开展匹?该方法适合于该正则表达式只会用一ơ的情况Q也是只进行一ơ匹配工作,因ؓq种情况下ƈ不需要生成一个Matcher实例?
String pattern()
q回该Patter对象所~译的正则表辑ּ?
String[] split(CharSequence input)
目标字W串按照Pattern里所包含的正则表辑ּ为模q行分割?
String[] split(CharSequence input, int limit)
作用同上Q增加参数limit目的在于要指定分割的D|Q如limi设ؓ2Q那么目标字W串根据正则表辑ּ分ؓ割ؓ两段?
一个正则表辑ּQ也是一串有特定意义的字W,必须首先要编译成Z个Patterncȝ实例Q这个Pattern对象会使用matcher()Ҏ来生成一个Matcher实例Q接着便可以用该
Matcher实例以编译的正则表达式ؓ基础对目标字W串q行匚w工作Q多个Matcher是可以共用一个Pattern对象的?
现在我们先来看一个简单的例子Q再通过分析它来了解怎样生成一个Pattern对象q且~译一个正则表辑ּQ最后根据这个正则表辑ּ目标字W串q行分割Q?
import java.util.regex.*;
public class Replacement{
public static void main(String[] args) throws Exception {
// 生成一个Pattern,同时~译一个正则表辑ּ
Pattern p = Pattern.compile("[/]+");
//?/b>Pattern的split()Ҏ把字W串?/"分割
String[] result = p.split(
"Kevin has seen《LEON》seveal times,because it is a good film."
+"/
凯文已经看过《这个杀手不太冷》几ơ了Q因为它是一?
+"好电影?名词:凯文?);
for (int i=0; i<result.length; i++)
System.out.println(result[i]);
Kevin has seen《LEON》seveal times,because it is a good film.
凯文已经看过《这个杀手不太冷》几ơ了Q因为它是一部好电媄?
名词:凯文?
很明显,该程序将字符串按"/"q行了分D,我们以下再?
split(CharSequence input, int
limit)Ҏ来指定分D늚D|Q程序改动ؓQ?
tring[] result = p.split("Kevin has seen《LEON》seveal times,because
it is a good film./
凯文已经看过《这个杀手不太冷》几ơ了Q因为它是一部好电媄?名词:凯文?Q?);
q里面的参数"2"表明目标语句分ZDc?
输出l果则ؓQ?
Kevin has seen《LEON》seveal times,because it is a good film.
凯文已经看过《这个杀手不太冷》几ơ了Q因为它是一部好电媄?名词:凯文?
׃面的例子Q我们可以比较出java.util.regex包在构造Pattern对象以及~译指定的正则表辑ּ的实现手法与我们在上一中所介绍的Jakarta-ORO
包在完成同样工作时的差别Q?b style="COLOR: black; BACKGROUND-COLOR: #ffff66">Jakarta-ORO
包要先构造一个PatternCompilercd象接着生成一个Pattern对象Q再正则表辑ּ?/b>该PatternCompilercȝcompile()Ҏ来将所需的正则表辑ּ~译赋予Patternc:
PatternCompiler orocom=new Perl5Compiler();
Pattern pattern=orocom.compile("REGULAR EXPRESSIONS");
PatternMatcher matcher=new Perl5Matcher();
但是在java.util.regex包里Q我们仅需生成一个Patternc,直接使用它的compile()Ҏ可以达到同L效果:
Pattern p = Pattern.compile("[/]+");
因此gjava.util.regex的构造法?b style="COLOR: black; BACKGROUND-COLOR: #ffff66">Jakarta-ORO更ؓzƈҎ理解?
3.Matcherc?
MatcherҎ如下Q?Matcher appendReplacement(StringBuffer sb,
String replacement)
当前匹配子串替换ؓ指定字符Ԍq且替换后的子串以及其之前Cơ匹配子串之后的字符串段dC个StringBuffer对象里?
StringBuffer appendTail(StringBuffer sb)
最后一ơ匹配工作后剩余的字W串dC个StringBuffer对象里?
int end()
q回当前匚w的子串的最后一个字W在原目标字W串中的索引位置
?
int end(int group)
q回与匹配模式里指定的组相匹配的子串最后一个字W的位置?
boolean find()
试在目标字W串里查找下一个匹配子丌Ӏ?
boolean find(int start)
重设Matcher对象Qƈ且尝试在目标字符串里从指定的位置开始查找下一个匹配的子串?
String group()
q回当前查找而获得的与组匚w的所有子串内?
String group(int group)
q回当前查找而获得的与指定的l匹配的子串内容
int groupCount()
q回当前查找所获得的匹配组的数量?
boolean lookingAt()
目标字W串是否以匹配的子串起始?
boolean matches()
试Ҏ个目标字W展开匚w,也就是只有整个目标字W串完全匚w时才q回真倹{?
Pattern pattern()
q回该Matcher对象的现有匹配模式,也就是对应的Pattern
对象?
String replaceAll(String replacement)
目标字W串里与既有模式相匹配的子串全部替换为指定的字符丌Ӏ?
String replaceFirst(String replacement)
目标字W串里第一个与既有模式相匹配的子串替换为指定的字符丌Ӏ?
Matcher reset()
重设该Matcher对象?
Matcher reset(CharSequence input)
重设该Matcher对象q且指定一个新的目标字W串?
int start()
q回当前查找所获子串的开始字W在原目标字W串中的位置?
int start(int group)
q回当前查找所获得的和指定l匹配的子串的第一个字W在原目标字W串中的位置?
Q光看方法的解释是不是很不好理解Q不要急,待会l合例子比较容易明白了Q?
一个Matcher实例是被用来对目标字W串q行Z既有模式Q也是一个给定的Pattern所~译的正则表辑ּQ进行匹配查扄Q所有往Matcher的输入都是通过CharSequence接口提供的,q样做的目的在于可以支持对从多元化的数据源所提供的数据进行匹配工作?
我们分别来看看各Ҏ的用:
★matches()/lookingAt ()/find()Q?
一个Matcher对象是由一个Pattern对象调用其matcher()Ҏ而生成的Q一旦该Matcher对象生成,它就可以q行三种不同的匹配查找操?#173;Q?
matches()Ҏ试Ҏ个目标字W展开匚w,也就是只有整个目标字W串完全匚w时才q回真倹{?
lookingAt
()Ҏ检目标字W串是否以匹配的子串起始?
find()Ҏ试在目标字W串里查找下一个匹配子丌Ӏ?
以上三个Ҏ都将q回一个布值来表明成功与否?
★replaceAll ()/appendReplacement()/appendTail()Q?
Matchercd时提供了四个匹配子串替换成指定字符串的ҎQ?
replaceAll()
replaceFirst()
appendReplacement()
appendTail()
replaceAll()与replaceFirst()的用法都比较单,L上面Ҏ的解释。我们主要重点了解一下appendReplacement()和appendTail()Ҏ?
appendReplacement(StringBuffer sb, String replacement)
当前匹配子串替换ؓ指定字符Ԍq且替换后的子串以及其之前Cơ匹配子串之后的字符串段dC个StringBuffer对象里,而appendTail(StringBuffer
sb)
Ҏ则将最后一ơ匹配工作后剩余的字W串dC个StringBuffer对象里?
例如Q有字符串fatcatfatcatfat,假设既有正则表达式模式ؓ"cat"Q第一ơ匹配后调用appendReplacement(sb,"dog"),那么q时StringBuffer
sb的内容ؓfatdogQ也是fatcat中的cat被替换ؓdogq且与匹配子串前的内容加到sb里,而第二次匚w后调用appendReplacement(sb,"dog")Q那么sb的内容就变ؓfatdogfatdogQ如果最后再调用一ơappendTailQsbQ?那么sb最l的内容是fatdogfatdogfat?
q是有点模糊Q那么我们来看个单的E序Q?
//该例把句子里的"Kelvin"改ؓ"Kevin"
import java.util.regex.*;
public class MatcherTest{
public static void main(String[] args)
throws Exception {
//生成Pattern对象q且~译一个简单的正则表达?Kelvin"
Pattern p = Pattern.compile("Kevin");
//?/b>Patterncȝmatcher()Ҏ生成一个Matcher对象
Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in
Kelvin Chen's KelvinSoftShop company");
StringBuffer sb = new StringBuffer();
int i=0;
//使用find()Ҏ查找W一个匹配的对象
boolean result = m.find();
//使用循环句子里所有的kelvin扑ևq替换再内容加到sb?
while(result) {
i++;
m.appendReplacement(sb, "Kevin");
System.out.println("W?+i+"ơ匹配后sb的内ҎQ?+sb);
//l箋查找下一个匹配对?
result = m.find();
看了上面q个例程是否对appendReplacement()QappendTail()两个Ҏ的用更清楚呢,如果q是不太肯定最好自己动手写几行代码?#173;试一下?
★group()/group(int group)/groupCount()Q?
该系列方法与我们在上介l的Jakarta-ORO中的MatchResult
.group()ҎcM(有关Jakarta-ORO请参考上的内容)Q都是要q回与组匚w的子串内容,下面代码很好解释其用法Q?
import java.util.regex.*;
public class GroupTest{
public static void main(String[] args)
throws Exception {
Pattern p = Pattern.compile("(ca)(t)");
Matcher m = p.matcher("one cat,two cats in the yard");
StringBuffer sb = new StringBuffer();
boolean result = m.find();
System.out.println("该次查找获得匚wl的数量为:"+m.groupCount());
for(int i=1;i<=m
Matcher对象的其他方法因比较好理解且׃幅有限Q请读者自qE验证?
4Q一个检验Email地址的小E序Q?
最后我们来看一个检验Email地址的例E,该程序是用来验一个输入的EMAIL地址里所包含的字W是否合法,虽然q不是一个完整的EMAIL地址验程序,它不能检验所有可能出现的情况Q但在必要时您可以在其基上增加所需功能?
import java.util.regex.*;
public class Email {
public static void main(String[] args) throws Exception {
String input = args[0];
//输入的EMAIL地址是否?
非法W号"."?@"作ؓ起始字符
Pattern p = Pattern.compile("^\.|^\@");
Matcher m = p.matcher(input);
if (m
//是否以"www."v?
p = Pattern.compile("^www\.");
m = p.matcher(input);
if (m
//是否包含非法字W?
p = Pattern.compile("[^A-Za-z0-9\.\@_\-~#]+");
m = p.matcher(input);
StringBuffer sb = new StringBuffer();
boolean result = m.find();
boolean deletedIllegalChars = false;
while(result) {
//如果扑ֈ了非法字W那么就设下标记
deletedIllegalChars = true;
//如果里面包含非法字符如冒号双引号{,那么把他们消去Q加到SB里面
m.appendReplacement(sb, "");
result = m.find();
那么输出l果会是:EMAIL地址不能?www.'起始
如果输入的EMAIL为@k...@163.net
则输ZؓQEMAIL地址不能?.'?@'作ؓ起始字符
当输入ؓQcgjmail...@163.net
那么输出是Q?
输入的EMAIL地址里包含有冒号、逗号{非法字W,请修?
您现在的输入? cgjmail...@163.net
修改后合法的地址应类? cgjm...@163.net
5QȝQ?
本文介绍了jdk1.4.0-beta3里正则表辑ּ?-java.util.regex中的cM及其ҎQ如果结合与上一中所介绍?b style="COLOR: black; BACKGROUND-COLOR: #ffff66">Jakarta-ORO
API作比较,读者会更容易掌握该API的用,当然该库的性能在未来的日子里不断扩展Q希望获得最C息的读者最好到及时到SUN的网站去了解?
6Q结束语Q?
本来计划再多写一介l一下需付费的正则表辑ּ库中较具代表性的作品Q但觉得既然有了免费且优U的正则表辑ּ库可以用,何必q要L需付费的呢Q相信很多读?#173;也是q么想的:Q所以有兴趣了解更多其他的第三方正则表达式库的朋友可以自己到|上查找或者到我在参考资料里提供的网址ȝ看?
参考资?
java.util.regex的帮助文?
Dana Nourie 和Mike McCloskey所写的Regular Expressions and the
Java™ Programming Language
需要更多的W三Ҏ则表辑ּ资源以及Z它们所开发的应用E序Lhttp://www.meurrens.org/ip-Links/java/regex/index.html
关于作?
陈广?Kevin
Chen,汕头大学电子信息工程pdU学士,台湾大新出版C区开发部Q现正围l中日韩电子资料使用JAVA开发电子词典等相关目。可通过E-mail:cgjm...@163.net于他联系?/p>
L2 》老爸2 ?#160;儿子2
而不能是Q爷? ?#160;儿子1
q样׃ؕ套了 Q是不,闹笑话;所以,c规定了不承关p,防止乱套Qɾl构变得清晰?br />
所以,当我们用树型结构时Q用类?br />
׃Q在生活中还有许多这L情况Q如Q张三是中国人,那张三他也是国人双重n份?br />
再如Q李四是java工程?也是厨师?br />那怎么办?Q?br />
我们可以q么做:
规定多个w䆾
public interface zhengjian{
interface China(){
public void China_L(){
//.................
}
}
interface USA(){
public void USA_L(){
//...........
}
}
public class zhangsan extends zhengjian implements Chna , USA{
public void China_L(){
// ......
}
public void USA_L(){
// ......
}
}
当张三去国Ӟ老美叫他拿个l证来看Q?br />
zhangsan.USA_L();
呵呵Q再举个例子大家清楚了Q?br />
HP?#8220;三星”?#8220;IBM”两种Q我通常而言不可能一个HP是既?#8220;三星”又是“IBM”的吧~~~~~~~~c?br /> “家庭?#8221;?#8220;办公?#8221;两种Q但往往我们能买到拥有两U款式相l合厂品是吧 ~~~~~~~~~~~接口
中国hQ?u>cL?#8220;徏世袭?#8221;Q接口有?#8220;q反常理׃u”Q呵?img height="20" src="http://m.tkk7.com/Emoticons/QQ/lol.gif" width="20" border="0" />开个玩W?/u>?br />
q样说完大家再去看些相关文章׃觉得很容易了 呵呵
剩下抽象cd单了Q那是抽象类Q?font color="#000000">abstractQ?/font>他还是类Qclass)Q只不过是抽象的|了?br />
抽象c?font color="#000000">Ҏ具体cd的部分实?-----抽象cL对具体的抽象Ҏ特征包括Q?font color="#ff0000">Ҏ的名字、参数的数目、参数的cd?br />
不包括:q回cd、参数名字、和抛出的异常?br />
java接口Q抽象类Q用来声明一个新的类?Java设计师应当主要用接口和抽象cd软g单位与内部和外部耦合(软g工程中内?h?br />
在理想的情况下,一个具体类应当只实现接口和抽象cM声明的方法,而不应当l出多余的方法!
抽象cM提供一个类的部分实现。抽象类可以有实例变量、以及一个或多个构造函数?br />
个概念够抽象了吧Q?br />public abstract class Human{...}
男h也是
public abstract class man{...}
有一点必讲清:接口不能l承抽象c,接口只能l承实实在在的类Q?br />
如,我可以ؓ某个HP厂品Q类Q规定一U规范(接口Q,但由于各个厂家推陈出斎ͼ所以,我很隑֯某一cd厂品规定Q?#8220;家庭型hp”很难下个结论(接口Q?br />
从Y件优化角度来考虑cd接口问题。类class条理清楚内聚性好Q用于Y件模块内部实玎ͼ汽R的内部机
梎ͼQ接口interface很灵z耦合性好Q用于模块外部实玎ͼ如,汽R操作板的按钮Q。从而实现强内聚、松耦合
的Y件设计思想?/strong>
include指o | jsp:include动作 | |
语法格式 | <%@ include file=”..”%> | <jsp:include page=”..”> |
发生作用的时?/span> | 面转换期间 | h期间 |
包含的内?/span> | 文g的实际内?/span> | 面的输?/span> |
转换成的Servlet | 主页面和包含面转换Z?/span>Servlet | 主页面和包含面转换为独立的Servlet |
影响主页?/span> | 可以 | 不可?/span> |
include指o | jsp:include动作 | |
发生更改时是否需要显式更改主面 | 需?/span> | 不需?/span> |
~译旉 | 较慢Q资源必被解析 | 较快 |
执行旉 | E快 | 较慢Q每ơ资源必被解析 |
灉|?/span> | 较差Q页面名U固?/span> | 更好Q页面可以动态指?/span> |
2.如何~写U程安全的代?
很多书籍里都详细讲解了如何这斚w的问题,他们主要讲解的是如何同步U程对共享资源的使用的问题。主要是对synchronized关键字的各种用法Q以及锁的概c?
Java1.5中也提供了如d锁这cȝ工具cR这些都需要较高的技巧,而且相对难于调试?
但是Q线E同步是不得以的Ҏ,是比较复杂的,而且会带来性能的损失。等效的代码中,不需要同步在~写Ҏ度和性能上会更好些?
我这里强调的是什么代码是始终为线E安全的、是不需要同步的。如?
1)帔R始终是线E安全的Q因为只存在L作?
2)Ҏ造器的访?new 操作)是线E安全的Q因为每ơ都新徏一个实例,不会讉K׃n的资源?
3)最重要的是:局部变量是U程安全的。因为每执行一个方法,都会在独立的I间创徏局部变量,它不是共享的资源。局部变量包括方法的参数变量?
struts user guide里有Q?
Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class.
?只用用局部变量?-~写U程安全的代码最重要的原则就是,在ActioncM只用局部变量,不用实例变量?/p>
ȝQ?
在Java的Web服务器环境下开发,要注意线E安全的问题。最单的实现方式是在Servlet和Struts Action里不要用类变量、实例变量,但可以用类帔R和实例常量?
如果有这些变量,可以它们{换ؓҎ的参C入,以消除它们?
注意一个容易淆的地方Q被Servlet或Action调用的类?如值对象、领域模型类)中是否可以安全的使用实例变量Q如果你在每ơ方法调用时
新徏一个对象,再调用它们的ҎQ则不存在同步问?--因ؓ它们不是多个U程׃n的资源,只有׃n的资源才需要同?--而Servlet和Action的实例对于多个线E是׃n的?
换句话说QServlet和Action的实例会被多个线E同时调用,而过了这一?如果在你自己的代码中没有另外启动U程Q且每次调用后箋业务对象旉是先新徏一个实例再调用Q则都是U程安全的?/p>