??xml version="1.0" encoding="utf-8" standalone="yes"?> publicinterfaceContents { publicinterface Destination { publicclass Goods { protectedclass GDestination implements Destination { public Destination dest(String s) { class TestGoods { 同时Q我们也发现了在外部cM用范围之外得到内部类对象的第一个方法,那就是利用其外部cȝҎ(gu)创徏q返回。上例中的cont()和dest()Ҏ(gu)是q么做的。那么还有没有别的方法呢Q当然有Q其语法格式如下Q?/p> outerObject=new outerClass(Constructor Parameters); outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters); 注意在创建非静态内部类对象Ӟ一定要先创v相应的外部类对象。至于原因,也就引出了我们下一个话题—?/p> 对刚才的例子E作修改Q?/p> publicclass Goods { private valueRate=2; private class Content implements Contents { protected class GDestination implements Destination { public Destination dest(String s) { 有h?x)问Q如果内部类里的一个成员变量与外部cȝ一个成员变量同名,也即外部cȝ同名成员变量被屏蔽了Q怎么办?没事QJava里用如下格式表达外部cȝ引用Q?/p> 有了它,我们׃怕这U屏蔽的情况了?/p>
和普通的cMP内部cM可以有静态的。不q和非静态内部类相比Q区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套cd相像
了,Java内部cMC++嵌套cL大的不同在于是否有指向外部的引用这一点上Q当然从设计的角度以?qing)以它一些细节来讲还有区别?/p> 除此之外Q在M非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类Q内部类的嵌套可以不止一层)。不q静态内部类中却可以拥有q一切。这也算是两者的W二个区别吧?/p> 是的QJava内部cM可以是局部的Q它可以定义在一个方法甚至一个代码块之内?/p> publicclass Goods1 { public staticvoid main(String[] args) { 上面是q样一个例子。在Ҏ(gu)dest中我们定义了一个内部类Q最后由q个Ҏ(gu)q回q个内部cȝ对象。如果我们在用一个内部类的时候仅需要创建它的一个对象ƈ创给外部Q就可以q样做。当?dng)定义在方法中的内部类可以使设计多样化Q用途绝不仅仅在q一炏V?/p> 下面有一个更怪的例子Q?/p> publicclass Goods2{ public void track() { internalTracking(true); } public staticvoid main(String[] args) { 你不能在if之外创徏q个内部cȝ对象Q因已经出了它的作用域。不q在~译的时候,内部cTrackingSlip和其他类一样同时被~译Q只不过它由它自q作用域,出了这个范围就无效Q除此之外它和其他内部类q没有区别?/p> java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一P当你只需要创Z个类的对象而且用不上它的名字时Q用内部类可以使代码看上去z清楚。它的语法规则是q样的:(x) 下面接着前面l箋举例子:(x) q里Ҏ(gu)cont()使用匿名内部cȝ接返回了一个实C接口Contents的类的对象,看上ȝ十分简z?/p> 在java的事件处理的匿名适配器中Q匿名内部类被大量的使用。例如在惛_闭窗口时加上q样一句代码:(x)
有一炚w要注意的是,匿名内部cȝ于没有名字,所以它没有构造函敎ͼ但是如果q个匿名内部cȝ承了一个只含有带参数构造函数的父类Q创建它的时候必d?
q些参数Qƈ在实现的q程中用super关键字调用相应的内容Q。如果你惌初始化它的成员变量,有下面几U方法:(x) java内部cL什么好处?Z么需要内部类Q?/p>
首先举一个简单的例子Q如果你惛_C个接口,但是q个接口中的一个方法和你构想的q个cM的一个方法的名称Q参数相同,你应该怎么办?q时候,?
可以Z个内部类实现q个接口。由于内部类对外部类的所有内定w是可讉K的,所以这样做可以完成所有你直接实现q个接口的功能?/p> 不过你可能要质疑Q更改一下方法的不就行了吗? 的确Q以此作计内部类的理由,实在没有说服力?/p> 真正的原因是q样的,java中的内部cd接口加在一P可以的解军_被C++E序员抱怨java中存在的一个问题——没有多l承。实际上QC++的多l承设计h很复杂,而java通过内部cd上接口,可以很好的实现多l承的效果?/p> 本文的目的只是向大家介绍一下内部类的概念以?qing)用方法,在后l文章里Q将?x)针?gu)文中的内容D更多具体的例子,以及(qing)介绍如何使用内部cLZ个Applicaton Framework?/p>
]]>
?
q个例子里类Content和GDestination被定义在了类Goods内部Qƈ且分别有着protected和private修饰W来控制讉KU?
别。Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实C两个接口Content?
Destination。在后面的mainҎ(gu)里,直接?Contents c和Destination dq行操作Q你甚至q这两个内部cȝ名字都没有看见!q样Q内部类的第一个好处就体现出来了—?strong>隐藏你不惌别h知道的操?/font>Q也卛_装性?
int value();
}
String readLabel();
}
privateclass Content implements Contents {
private int i = 11;
public int value() {
return i;
}
}
privateString label;
private GDestination(String whereTo) {
label = whereTo;
}
publicString readLabel() {
return label;
}
}
return new GDestination(s);
}
publicContents cont() {
returnnew Content();
}
}
publicstatic void main(String[] args) {
Goods p = new Goods();
Contents c = p.cont();
Destination d = p.dest("Beijing");
}
}
修改的部分用蓝色昄恕T谡饫镂颐歉鳪oodscd加了一个private成员变量valueRateQ意义是货物?
价值系敎ͼ在内部类Content的方法value()计算价值时把它乘上。我们发玎ͼvalue()可以讉KvalueRateQ这也是内部cȝW二?
好处—?strong>一个内部类对象可以讉K创徏它的外部cd象的内容Q?
甚至包括U有变量Q这是一个非常有用的Ҏ(gu),为我们在设计时提供了更多的思\和捷径。要惛_现这个功能,内部cd象就必须有指向外部类对象的引用?
Java~译器在创徏内部cd象时Q隐式的把其外部cd象的引用也传了进dƈ一直保存着。这样就使得内部cd象始l可以访问其外部cd象,同时q也是ؓ(f)什
么在外部cM用范围之外向要创建内部类对象必须先创建其外部cd象的原因?
private int i = 11*valueRate;
public int value() {
return i;
}
}
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new GDestination(s);
}
publicContents cont() {
returnnew Content();
}
}outerClass.this
public Destination dest(String s) {
class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
return new GDestination(s);
}
Goods1 g= new Goods1();
Destination d = g.dest("Beijing");
}
}
privatevoid internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() { return id; }
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
}
Goods2 g= new Goods2();
g.track();
}
}new interfacename(){......}; ?new superclassname(){......}; public class Goods3 {
publicContents cont(){
returnnewContents(){
private int i = 11;
public int value() {
return i;
}
};
}
}frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
]]>
2、在q行数据保存操作的action的方法中Q如saveҎ(gu)Q,增加如下代码Q?br />
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("error.submit.double"));
saveMessages(request.getSession(), messages);
//如果是重复提交,重新生成token
saveToken(request);
return mapping.findForward("edit");
}
//不是重复提交׃存数据,q删除该ơ提交的token
resetToken(request);
3、在~辑记录的jsp面Q?lt;html:errors />标签Q显C错误提C?br />
]]>
我们在测试某cLQ由于它要与其他cd生联p,因此往往在测试此cȝ代码中也与之联pȝcM一h试了。这U测试,被测试的cȝ接依赖于其他 c,一旦其他类发生改变Q被试cM随之被迫改变。更重要的是Q这些其他类可能未l过试Q因此必d试q些c,才能试被测试类。这U情况下Q测? 驱动开发成为空谈。而如果其他类中也引用了被试c,我们到底先测试哪一个类Q因此,在测试中Q如果我们能被试cd立v来,使其完全不依赖于其他cȝ 具体实现Q这P我们p做到试先行Q先试哪个c,先实现哪个c,而不与之联pȝcL否已l实现?/p>
虚拟对象(mock object)是为此需要而诞生的。它通过JDK中的反射机制Q在q行时动态地创徏虚拟对象。在试代码中,我们可以验证q些虚拟对象是否被正地调用 了,也可以在明确的情况下Q让其返回特定的假想倹{而一旦有了这些虚拟对象提供的服务Q被试cd可以虚拟对象作为其他与之联pȝ真实对象的替w,从? L地搭v一个很完美的测试环境?/p>
JMock是帮助创建mock对象的工P它基于Java开发,在Java试与开发环境中有不可比拟的优势Q更重要的是Q它大大化了虚拟对象的用?/p>
本文中,通过一个简单的试用例来说明JMock如何帮助我们实现q种孤立试。有三个主要的类QUserQUserDAOQ及(qing) UserService。本文中Q我们只需试UserServiceQ准备虚拟UserDAO。对于UserQ由于本w仅是一个过于简单的POJOQ可 以不用测试。但如果你是一个完主义者,也可以用JMock的虚拟它。在q领域,JMock几乎无所不能?)
User是一个POJOQ用以在视图中传输数据及(qing)映射数据库。其代码如下Q?/p>
package com.sarkuya.model;
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UserDAO负责与数据库打交道,通过数据库保存、获取User的信息。尽我们可以不用知道JMock如何通过JDK的反机制来实现孤立试Q但?
应知道QJDK的反机制要求这些在q行时创建的动态类必须定义接口。在使用JMock的环境中Q由于我们要虚拟UserDAOQ意味着UserDAO
必须定义接口。代码如下:(x)
package com.sarkuya.dao;
import com.sarkuya.model.User;
public interface UserDAO {
public void saveUser(User user);
public User getUser(Long id);
}
UserService存有UserDAO的引用,通过其对外提供应用的服务。相应地Q我们先定义了其接口(管在本文中Q作试c,UserService不需要有接口Q但如果以后此类需要被虚拟Q也应该带有接口Q基于此原因Q我们也为其定义了接??/p>
package com.sarkuya.service;
import com.sarkuya.dao.UserDAO;
import com.sarkuya.model.User;
public interface UserService {
public void setUserDAO(UserDAO userDAO);
public void saveUser(User user);
public User getUser(Long id);
}
可以看到Q除了setUserDAO()外,其另外的Ҏ(gu)与UserDAO一栗这是设计模式中门面模式的典型应用,应用只通过UserService提供服务Q而UserService在内部通过调用UserDAO来实现相应的功能?/p>
Ҏ(gu)试先行的原则,你应该先写测试,再编写实现。这里先~写实现的原因,主要是读者更加清楚我们接着要测试什么。由于本文是着重介lJMock的用,加上UserServiceImpl比较单,因此先列出其代码如下Q?/p>
package com.sarkuya.service.impl;
import com.sarkuya.dao.UserDAO;
import com.sarkuya.model.User;
import com.sarkuya.service.UserService;
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public UserServiceImpl() {
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public User getUser(Long id) {
return userDAO.getUser(id);
}
public void saveUser(User user) {
userDAO.saveUser(user);
}
}
下面是UserService的测试代码:(x)
package com.sarkuya.service;
import com.sarkuya.dao.UserDAO;
import com.sarkuya.model.User;
import com.sarkuya.service.impl.UserServiceImpl;
import junit.framework.*;
import org.jmock.Mock;
import org.jmock.MockObjectTestCase;
public class UserServiceTest extends MockObjectTestCase {
private UserService userService = new UserServiceImpl();
private Mock userDAO = null;
public UserServiceTest(String testName) {
super(testName);
}
protected void setUp() throws Exception {
userDAO = new Mock(UserDAO.class);
userService.setUserDAO((UserDAO)userDAO.proxy());
}
protected void tearDown() throws Exception {
}
public static Test suite() {
TestSuite suite = new TestSuite(UserServiceTest.class);
return suite;
}
public void testGetUser() {
User fakeUser = new User("John");
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
User user = userService.getUser(1L);
assertNotNull(user);
assertEquals("John", user.getName());
}
public void testSaveUser() {
User fakeUser = new User("John");
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
User user = userService.getUser(1L);
assertEquals("John", user.getName());
userDAO.expects(once()).method("saveUser").with(same(fakeUser));
user.setName("Mike");
userService.saveUser(user);
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(user));
User modifiedUser = userService.getUser(1L);
assertEquals("Mike", user.getName());
}
}
此段代码有几点应注意Q?/p>
1、此试cȝ承了JMock的MockObjectTestCase
2、private Mock userDAO = null;说明userDao是一个准备虚拟的对象
3、在setup()中,userDAO.class传入Mock()后,再通过proxy()Ҏ(gu)q回一个UserDAO的代理类实例(卌拟对象实?Qƈ赋guserService
4、在testGetUser()Ҏ(gu)中,如果我们先将W一行及(qing)W二行代码屏蔽掉Q可以看出,q是一个真实环境下的测试代码。先获取一个UserQ?
然后认光I|再确认其姓名为“John”。此Ӟ在真实环境下Q这D代码要试成功的前提必LUserDAO已经q接C数据库,然后q回一?
User后传lUserService?br />
但问题是Q到目前为止Q且不说UserDAOq未l历q接数据库这一pdJ琐而痛苦的q程Q我们甚臌未实现UserDAO的接口!那么Qؓ(f)何加上第一?
?qing)第二行代码后就可以了呢Q这正是JMock的威力所在。先实例化一个测试用的fakeUserQ然后通过一pd的指令,在第二行代码中告诉JMock?
该如何“做假”。尽这句代码很长,我们可作如下理解Q?br />
1) userDAO.expects(once())Q我们期望userDAO的某Ҏ(gu)被执行一ơ,如果此方法未被执行,或者执行了二次以上Q测试就不会(x)通过
2) method("getUser")Q这个期望被执行一ơ的Ҏ(gu)名ؓ(f)userDAO.getUser()
3) with(eq(1L))Q执行getUser()Ҏ(gu)Ӟ认其传入的参数gؓ(f)?L?br />
4) will(returnValue(fakeUser))Q上q条件均满后,q回一个虚假的对象Q即我们前面实例化的fakeUser
M来说Q当讑֮好第二行语句后,JMock在后台监控着Q确保userDAO.getUser()必须Q且只被执行一ơ,且参数?L”已l正地传给了此Ҏ(gu)Q一旦这些条件被满Q就q回fakeUser?br />
而在W三行,User user = userService.getUser(1L)触发所有这些条Ӟ作ؓ(f)奖励Q它接受了奖品fakeUserq赋guser对象。而下面第四行?qing)第五行均对此user对象q行试Q不通过才怪?/p>
5) testSaveUser()Ҏ(gu)中的原理cM。其思\是,id为?”的user从数据库中取出,其名改为“Mike”,再存回数据库Q然后再从数据库中取出此userQ确保其名字已被改变?br /> W五行userDAO.expects(once()).method("saveUser").with(same(fakeUser))比较Ҏ(gu)。首 先,with(same(fakeUser))说明Q传入参数必LfakeUser此实例,管我们在下面的语句中通过user.setName ("Mike")Q但只是改变了其name的属性,而fakeUser的实例引用ƈ未发生改变,因此可以满条g。其ơ,其后没有.will (returnValue(fakeUser))Q因为userDAO.saveUser()不需要返回Q何对象或基本数据cd?br /> 另外Q当再次执行userDAO.expects()ӞJMock重讑օ监控条g。我们也可以通过userDAO.reset()来显式是清除监控条g?/p>
通过以上实例代码?qing)其说明Q我们看出,用好JMock的关键是先设|监控条Ӟ再写相应的测试语句。一旦设好监控条件后Q在某段代码块执行完毕时Q? 如果监控条g未得到满I或是没有通过expects()再次重设条gQ或通过reset()来显式是清除监控条gQ测试将无法通过?/p>
以上介绍了JMock的基本用方法。而这U基本用法,占了全面掌握JMock所需学习(fn)的知?0%以上。关于JMock的更多细节,感兴的读者可以访问JMock的网站进一步学?fn)?/p>
很多朋友在深入的接触 JAVA 语言后就?x)发现这样两个词Q反?/span> (Reflection) 和内?/span> (Introspector) Q经常搞不清楚这到底是怎么回事Q在什么场合下应用以及(qing)如何使用Q今天把q二者放在一起介l,因ؓ(f)它们二者是相辅相成的?/span>
反射
相对而言Q反比内省更容易理解一炏V用一句比较白的话来概括,反射是让你可以通过名称来得到对?/span> ( c,属性,Ҏ(gu) ) 的技术。例如我们可以通过cd来生成一个类的实例;知道了方法名Q就可以调用q个Ҏ(gu)Q知道了属性名可以访问这个属性的倹{?/span>
q是写两个例子让大家更直观的了解反射的用方法:(x)
//
通过cd来构造一个类的实?br />
Class cls_str = Class.forName(
"java.lang.String"
); |
//
通过Ҏ(gu)名来调用一个方?br />
String methodName =
"length"
; |
上面的两个例子是比较常用Ҏ(gu)。看C面的例子有发问了:(x)Z么要q么ȝ呢?本来一条语句就完成的事情干吗要整这么复杂?没错Q在上面的例子中实没有必要q么ȝ。不q你惛_q样一个应用程序,它支持动态的功能扩展Q也是说程序不重新启动但是可以自动加蝲新的功能Q这个功能用一个具体类来表C。首先我们必Mؓ(f)q些功能定义一个接口类Q然后我们要求所有扩展的功能cdd现我指定的接口,q个规定了应用程序和可扩展功能之间的接口规则Q但是怎么动态加载呢Q我们必让应用E序知道要扩展的功能cȝcdQ比如是 test.Func1 Q当我们把这个类?/span> ( 字符?/span> ) 告诉应用E序后,它就可以使用我们W一个例子的Ҏ(gu)来加载ƈ启用新的功能。这是cȝ反射Q请问你有别的选择吗?
关于Ҏ(gu)的反徏议大家看我的另外一文章?/span> 利用 Turbine 的事件映来扩展 Struts 的功?/span> 》,地址是:(x) 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 Ҏ(gu)Q通过q些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚)Q这?/span> API 存放于包 java.beans 中?/span>
一般的做法是通过c?/span> Introspector 来获取某个对象的 BeanInfo 信息Q然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor Q,通过q个属性描q器可以获取某个属性对应的 getter/setter Ҏ(gu)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 Ҏ(gu)。但也ƈ不Lq样Q什么意思呢Q就是说对一?/span> Bean cLԌ我可以没有属性,但是只要?/span> getter/setter Ҏ(gu)中的其中一个,那么 Java 的内省机制就?x)认为存在一个属性,比如cM有方?/span> setMobile Q那么就认ؓ(f)存在一?/span> mobile 的属性,q样可以方便我们?/span> Bean c通过一个接口来定义而不用去兛_具体实现Q不用去兛_ Bean 中数据的存储。比如我们可以把所有的 getter/setter Ҏ(gu)攑ֈ接口里定义,但是真正数据的存取则是在具体cMd玎ͼq样可提高系l的扩展性?/span>
ȝ
?/span> Java 的反以?qing)内省应用到E序设计中去可以大大的提供程序的化和可扩展性。有很多目都是采取q两U技术来实现其核心功能,例如我们前面提到?/span> Struts Q还有用于处?/span> XML 文g?/span> Digester 目Q其实应该说几乎所有的目都或多或的采用q两U技术。在实际应用q程中二者要怺l合方能发挥真正的智能化以及(qing)高度可扩展性?/span>
response.setHeader("Content-Disposition", "inline; filename="fliename)
点击打开?x)在ie中打开?/p>
需要说明的有三点:(x)
Ø 中文文g名需要进行iso8859-1转码方可正确昄Q?
fileName = new String(fileName.getBytes("GBK"),"iso8859-1");
Ø 传递的文g名,需要包含后~名(如果此文件有后缀名)Q否则丢失文件的属性,而不能自行选择相关E序打开?
Ø 有下载前询问Q是打开文gq是保存到计机Q和通过IE览器直接选择相关应用E序插g打开两种方式Q前者如上代码所C,后者如下:(x)
response.setHeader("Content-disposition","filename="+ "Example.xls" );
3. 在研I文件的上传?qing)下载过E中Q有几点体会(x)
E序的I/O操作往往是性能的瓶颈所在,java io定义了两个基本的抽象c?InputStream和OutputStream,对于不同的数据类型比如磁盘,|络又提供了不同的实玎ͼjava.io 也提供了一些缓冲流(BufferedStream)Qɼ盘可以很快的读写一大块的数? 而Java基本的I/OcMơ只能读写一个字?但缓冲流(BufferedStream)可以一ơ读写一Ҏ(gu)据,,~冲?Buffered Stream)大大提高了I/O的性能。所?
Ø块块的读写数据会(x)非常?因此,量大块的读写数?
Ø使用BufferedInputStream和BufferedOutputStream来批处理数据以提高性能
Ø对象的序列化(serialization)非常影响I/O的性能,量用
注:(x)
1、Servlet中输出流的简单处理方法:(x)
2、输出流跟PrintWriter out = response.getWriter();不能同时使用?br />
ClassNotFoundException
是最常见的类装入异常cd。它发生在装入阶Dc(din)Java 规范?ClassNotFoundException
的描q是q样的:(x)
当应用程序试N过cȝ字符串名Uͼ使用以下三种Ҏ(gu)装入c,但却找不到指定名U的cd义时抛出该异常?/p>
Class
中的 forName()
Ҏ(gu)?/li>
ClassLoader
中的 findSystemClass()
Ҏ(gu)?/li>
ClassLoader
中的 loadClass()
Ҏ(gu)?/li>
所以,如果昑ּ地装入类的尝试失败,那么抛?ClassNotFoundException
。清?1 中的试用例提供的示例代码抛Z一?ClassNotFoundException
Q?/p>
清单 1. ClassNotFoundExceptionTest.java
|
q个试用例定义了一个类装入器(MyClassLoader
Q,用于装入一个不存在的类Q?code>DoesNotExistQ。当它运行时Q会(x)出现以下异常Q?/p>
|
因ؓ(f)q个试试图使用?loadClass()
的显式调用来q行装入Q所以抛?ClassNotFoundException
?/p>
通过抛出 ClassNotFoundException
Q类装入器提C,定义cL所需要的字节码在c装入器所查找的位|上不存在。这些异怿复v来通常比较单。可以用 IBM ?verbose 选项(g)查类路径Q确保用的c\径设|正(要获?verbose 的更多信息,请参阅本pd?W一文?/a>Q。如果类路径讄正确Q但是仍然看到这个错误,那么是需要的cdc\径中不存在。要修复q个问题Q可以把cȝ动到c\径中指定的目录或 JAR 文g中,或者把cL在的位置d到类路径中?/p>
![]() ![]() |
![]() |
NoClassDefFoundError
是类装入器在装入阶段抛出的另一个常见异常。JVM 规范?NoClassDefFoundError
的定义如下:(x)
如果 Java 虚拟机或ClassLoader
实例试图装入cd义(作ؓ(f)正常的方法调用的一部分Q或者作Z?new 表达式创建新实例的一部分Q,但却没有扑ֈcd义时抛出该异常?
当目前执行的cdl编译,但是找不到它的定义时Q会(x)存在 searched-for cd义?
实际上,q意味着 NoClassDefFoundError
的抛出,是不成功的隐式类装入的结果?/p>
清单 2 到清?4 的测试用例生了 NoClassDefFoundError
Q因为类 B
的隐式装入会(x)p|Q?/p>
清单 2. NoClassDefFoundErrorTest.java
|
|
|
q几个清单中的代码编译好之后Q删?B
的类文g。当代码执行Ӟ׃(x)出现以下错误Q?/p>
|
c?A
扩展了类 B
Q所以,当类 A
装入Ӟc装入器?x)隐式地装入c?B
。因为类 B
不存在,所以抛?NoClassDefFoundError
。如果显式地告诉c装入器装入c?B
Q例如通过 loadClass("B")
调用Q,那么׃(x)抛出 ClassNotFoundException
?/p>
昄Q要修复q个Ҏ(gu)CZ中的问题Q在对应的类装入器的c\径中Q必d在类 B
。这个示例看h可能价g大、也不真实,但是Q在复杂的有许多cȝ真实pȝ中,?x)因为类在打包或部v期间的遗p发生这cLc(din)?/p>
在这个例子中Q?code>A 扩展?B
Q但是,即 A
用其他方式引?B
Q也?x)出现同L(fng)问题 —?例如Q以Ҏ(gu)参数引用或作为实例字Dc(din)如果两个类之间的关pL引用关系而不是承关p,那么?x)在W一ơ?A
时抛出错误,而不是在装入 A
时抛出?/p>
![]() ![]() |
![]() |
c装入器能够抛出的另一个异常是 ClassCastException
。它是在cd比较中发C兼容cd的时候抛出的。JVM 规范指定 ClassCastException
是:(x)
该异常的抛出Q表明代码企图把对象的类型{换成一个子c,而该对象q不是这个子cȝ实例?
清单 5 演示的代码示例会(x)产生一?ClassCastException
Q?/p>
清单 5. ClassCastException.java
|
在清?5 中,调用?storeItem()
Ҏ(gu)Q用一?Integer
数组、一?int
和一个字W串作ؓ(f)参数。但是在内部Q该Ҏ(gu)做了两g事:(x)
String
对象cd转换?Object
cdQ用于参数列表)?/li>
Object
cd转换?Integer
cdQ在Ҏ(gu)定义中)?/li>
当程序运行时Q会(x)出现以下异常Q?/p>
|
q个异常是由昑ּcd转换抛出的,因ؓ(f)试用例试图把类型ؓ(f) String
的东西{换成 Integer
?/p>
当检查对象(例如清单 5 中的 item
Qƈ把类型{换成目标c(Integer
Q时Q类装入器会(x)(g)查以下规则:(x)
java.lang.Object
?code>java.lang.Cloneable ?java.io.Serializable
?/li>
如果q反了以上Q何一条规则,那么c装入器׃(x)抛出 ClassCastException
。修复这cd常的最单方式就是仔l检查对象要转换到的cd是否W合以上提到的规则。在某些情况下,在做cd转换之前?instanceof
q行(g)查是有意义的?/p>
![]() ![]() |
![]() |
在把本机调用链接到对应的本机定义Ӟc装入器扮演着重要角色。如果程序试图装入一个不存在或者放错的本机库时Q在链接阶段的解析过E会(x)发生 UnsatisfiedLinkError
。JVM 规范指定 UnsatisfiedLinkError
是:(x)
对于声明?native
的方法,如果 Java 虚拟机找不到和它对应的本a定义Q就?x)抛异常?
当调用本机方法时Q类装入器会(x)试装入定义了该Ҏ(gu)的本机库。如果找不到q个库,׃(x)抛出q个错误?/p>
清单 6 演示了抛?UnsatisfiedLinkError
的测试用?Q?/p>
清单 6. UnsatisfiedLinkError.java
|
q段代码调用本机Ҏ(gu) call_A_Native_Method()
Q该Ҏ(gu)是在本机?myNativeLibrary
中定义的。因个库不存在,所以在E序q行时会(x)发生以下错误Q?/p>
|
本机库的装入p?System.loadLibrary()
Ҏ(gu)的类的类装入器启?—?在清?6 中,是 UnsatisfiedLinkErrorTest
的类装入器。根据用的c装入器Q会(x)搜烦不同的位|:(x)
sun.boot.library.path
?/li>
java.ext.dirs
Q然后是 sun.boot.library.path
Q然后是 java.library.path
?/li>
sun.boot.library.path
Q然后是 java.library.path
?/li>
在清?6 中,UnsatisfiedLinkErrorTest
cLql类装入器装入的。要装入所引用的本机库Q这个类装入器先查找 sun.boot.library.path
Q然后查?java.library.path
。因为在两个位置中都没有需要的库,所以类装入器抛?UnsatisfiedLinkageError
?/p>
一旦理解了库装入过E所涉及(qing)的类装入器,可以通过把库攑֜合适位|来解决q类问题?/p>
![]() ![]() |
![]() |
JVM 规范指定 ClassCircularityError
的抛出条件是Q?/p>
cL接口׃是自q类或超接口而不能被装入?
q个错误是在链接阶段的解析过E中抛出的。这个错误有点奇怪,因ؓ(f) Java ~译器不允许发生q种循环情况。但是,如果独立地编译类Q然后再把它们放在一P可能发生这个错误。请设想以下场景。首先,~译清单 7 和清?8 中的c:(x)
|
|
然后Q分别编译清?9 和清?10 中的c:(x)
|
|
最后,采用清单 7 的类 A
和清?10 的类 B
Qƈq行一个应用程序,试图装入 A
或?B
。这个情늜h可能不太可能Q但是在复杂的系l中Q在把不同部分放在一L(fng)时候,可能?x)发生类似的情况?/p>
昄Q要修复q个问题Q必避免@环的cdơ结构?/p>
![]() ![]() |
![]() |
JVM 规范指出Q抛?ClassFormatError
的条件是Q?/p>
负责指定所h的编译类或接口的二进制数据Ş式有误?
q个异常是在c装入的链接阶段的校验过E中抛出。如果字节码发生了更改,例如ȝ本号或次版本号发生了更改Q那么二q制数据的Ş式就?x)有误。例如,如果对字节码故意做了更改Q或者在通过|络传送类文g时现Z错误Q那么就可能发生q个异常?/p>
修复q个问题的惟一Ҏ(gu)是获得字节码的正确副本Q可能需要重新进行编译?/p>
![]() ![]() |
![]() |
Ҏ(gu) JVM 规范Q抛?ExceptionInInitializer
的情冉|Q?/p>
E
Q而且 E
的类不是 Error
或者它的某个子c,那么׃(x)创徏 ExceptionInInitializerError
cȝ一个新实例Qƈ?E
作ؓ(f)参数Q用q个实例代替 E
?br />ExceptionInInitializerError
的新实例Q但是因为出?Out-Of-Memory-Error
而无法创建新实例Q那么就抛出 OutOfMemoryError
对象作ؓ(f)代替?/li>
清单 8 中的代码抛出 ExceptionInInitializerError
Q?/p>
清单 8. ExceptionInInitializerErrorTest.java
|
当静态代码块中发生异常时Q会(x)被自动捕捉ƈ?ExceptionInInitializerError
包装该异常。在下面的输Z可以看到q点Q?/p>
|
q个错误在类装入的初始化阶段抛出。修复这个错误的Ҏ(gu)是检查造成 ExceptionInInitializerError
的异常(在堆栈跟t的 Caused by:
下显C)q寻N止抛个异常的方式?/p>
GWT lets you avoid many of these headaches while offering your users the same dynamic, standards-compliant experience. You write your front end in the Java programming language, and the GWT compiler converts your Java classes to browser-compliant JavaScript and HTML.
GWT website: http://code.google.com/webtoolkit/