最近在看一本叫《Component Development for the Java Platform》的书,里面内容非常有兴趣,但是碰到不少疑惑,希望高手加以指点。
下面代码均可直接编译运行。思想如下:
Point类是一个接口,PointImpl实现了Point接口,但是它存在缺陷,即它的move()函数只对x进行了操作而未对y进行操作;假设应用程序PointClient已经运行并已载入了这个有缺陷的类,通过PointServe,我们可以用一个没有缺陷的版本(去掉PointImpl中Move函数的y的注释),在运行期替换掉有缺陷的版本,并重新load进来(ServerPoint的Reload())不影响程序的运行。由于PointImpl不能放在code/hapter2/ection2_5目录下,因为这样reload时会引起PointImpl的装载器和原来的装载器是同一个而导致不会load新版本,于是我在code/hapter2/ection2_5建了一个子目录subdir,将PointImpl.class放到了subdir中,并删掉了code/hapter2/ection2_5中的PointImpl.class,运行时出现了
Exception in thread "main" java.lang.ClassNotFoundException: code.chapter2.section2_5.PointImpl
当我在code/hapter2/ection2_5也保留一份PointImpl.class是就会出现
Exception in thread "main" java.lang.ClassCastException: code.chapter2.section2_5.PointImpl
at code.chapter2.section2_5.PointServer.createPoint(PointServer.java:14)
at code.chapter2.section2_5.PointClient.main(PointClient.java:12)
我弄了很久都不明白,希望高手指点一下(我使用Eclipse3.1编译运行)
给出代码:
代码1:
package code.chapter2.section2_5;public interface Point {
public int getX();
public int getY();
public void move(int dx, int dy);
}
-----------------------------------------------------------------------
代码2:
package code.chapter2.section2_5;public class PointImpl {
private int x; 
private int y;
public int getX() {
return x;
}
public int getY() {
return y;
} public void move(int dx, int dy) {
x += dx;
//y += dy;
// oops! forgot to move y
}
public String toString() {
return "Point at " + x + ", " + y;
}
}
//-------------------------------------------------------------------
代码3:
package code.chapter2.section2_5;import java.net.*;public class PointServer {
static ClassLoader cl; static Class ptClass; public static synchronized Point createPoint(Point template)
throws Exception {
if (ptClass == null)
reloadImpl();
Point newPt = (Point)ptClass.newInstance();
if (template != null) {
newPt.move(template.getX(), template.getY());
}
return newPt;
} public static synchronized void reloadImpl() throws Exception {
URL[] serverURLs = new URL[] { new URL("file:subdir/") };
cl = new URLClassLoader(serverURLs);
ptClass = cl.loadClass("code.chapter2.section2_5.PointImpl");
}
}
-----------------------------------------------------------------------------------
代码4:
package code.chapter2.section2_5;import java.io.*;public class PointClient {
static Point pt; public static void main(String[] args) throws Exception {

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

pt = PointServer.createPoint(null);

System.out.println(pt);

while (true) {
System.out.println("MOVE, RELOAD, or EXIT");
String cmdRead = br.readLine();
String cmd = cmdRead.toUpperCase();
if (cmd.equals("EXIT") || cmd.equals("exit")) {
return;
} else if (cmd.equals("RELOAD") || cmd.equals("reload")) {
PointServer.reloadImpl();
pt = PointServer.createPoint(pt);
System.out.println(pt);
} else if (cmd.equals("MOVE") || cmd.equals("move")) {
pt.move(1, 1);
System.out.println(pt);
}
}
}
}

解决方案 »

  1.   

    URL[] serverURLs = new URL[] { new URL("file:subdir/") };file:subdir/换成你的需要热部署的那个类所在的code父目录绝对路径,
    注决eclipse的user.dir目录在哪里
      

  2.   

    你的PointImpl没有实现Piont接口把你的PointImpl.class放在subdir/code/chapter2/section2_5下然后file:subdir/写成file:bin/subdir,注意把eclipse生成的PointImpl类删除,
    假设你的类是生成在 [工程目录]/bin目录的要不要给个工程你运行一下
      

  3.   

    谢谢楼上的,给各工程吧,我自己改完了还是找不着PointImpl.class
    或者[email protected]
      

  4.   

    我还有点疑问,new URL("file:subdir/") 这个相对路径的subdir到底是相对于谁的路径啊?
    是相对于整个应用程序(也就是code的父目录)还是相对于 code.chapter2.section2_5.PointServer.class(即code/chapter/section2_5)的呢?
      

  5.   

    楼上的兄弟:
    收到你的信了,非常感谢你的帮忙,又遇到些新问题。我做了回复,也不知道你收到没有,于是在这里再把我回复的内容说一下。运行完你的工程以后,找不到class的问题解决了,可是没能起到热装载的作用。我将PointServer.java中的reloadImpl()加上打印,修改如下:
    public static synchronized void reloadImpl() throws Exception {
            URL[] serverURLs = new URL[] { new URL("file:bin/bin/subdir/") };
            cl = new URLClassLoader(serverURLs);
            System.out.println("cl's loader:"+cl.getClass().getClassLoader());
            ptClass = cl.loadClass("code.chapter2.section2_5.PointImpla");
            System.out.println("ptClass's loader:"+ptClass.getClassLoader());
        }
    运行时的结果如下:
    user.dir=D:\Java资源\电子书\Component Development for the Java Platform\ttt
    cl's loader:null
    ptClass's loader:sun.misc.Launcher$AppClassLoader@82ba41
    newPt:sun.misc.Launcher$AppClassLoader@82ba41
    Point at 0, 0
    MOVE, RELOAD, or EXIT
    可以看到ptClass的装载器是系统类装载器(AppClassLoader)而不是URLClassloader,我在createPoint()加入了对newPt的打印System.out.println("newPt:"+newPt.getClass().getClassLoader());结果newPt也是由AppClassLoader而不是URLClassloader载入的,根据JVM的载入规则,同一装载器是不会重复载入某个同名class的,所以在subdir下的那个PointImpla.class根本没被载入(或者通过运行结果也能看出来,我把一个修改过的PointImpla放到subdir里,没修改过的还放到原位置不动,运行结果是修改过的没有被载入)。请问为什么ptClass的装载器是系统类装载器(AppClassLoader)而不是URLClassloader呢?(我试过奖cl的类型由ClassLoader改为URLClassLoader,结果同上)。
    不好意思,麻烦你了,等待你的回复。