分享
 
 
 

Unloading and Reloading classes

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

轉java.sun.com

The July 22, 2003 Tech Tip titled "Compiling Source Directly From a Program" offered a way to compile source files directly from your Java program. It presented a simple editor in which you can name a class and provide the source. That approach works fine, provided the class is not one that you already compiled and loaded during the current execution run. If indeed you previously compiled and loaded the class, you can't then edit it and recompile it using the approach covered in the previous Tech Tip. The issue here is the underlying class loader. All classes loaded through the system class loader will never be unloaded. To allow the program to let you reedit the source for a class, and then recompile and reload it, you need to work with a different class loader. In this tip, you'll learn how to create a custom class loader, one that lets you load your newly compiled class (or any class). You'll also learn how to discard the class such that the next time you compile the class, the newly compiled class is loaded.

An instance of the ClassLoader class of the java.lang package is responsible for loading all classes. For system classes, that class loader is available through the getSystemClassLoader method of ClassLoader. For user classes, if that class is already loaded, you can ask for the ClassLoader with the getClassLoader method of Class.

In the RunIt program from the earlier Tech Tip, the forName method of Class was used to load the class data (that is, to get an instance of Class). If you want to use a different class loader, you have to use the loadClass method of ClassLoader to load the class data. In other words, you need to code this:

String className = ...;

Class aClass = loader.loadClass(className);

instead of this:

String className = ...;

Class aClass = Class.forName(className);

Functionally, the two code blocks above are identical when loading through the same class loader. The first block loads the class through the same loader as where the code is found. The second loads the class through the class loader specified by the loader variable. In both cases, you would then call something like newInstance to create an instance of the class.

Repeated calls to Class.forName load the same class (assuming the class loader of the calling class doesn't change). Repeated calls to loader.loadClass load the same class. However, the second block allows you to reload a class. To do that, you create a new ClassLoader:

String className = ...;

// create new loader instance

ClassLoader loader = ...;

Class aClass = loader.loadClass(className);

In this particular code block, a new loader is created between calls to loadClass. If the class definition for className changes between calls, the new version of the class is loaded the second time through.

If you change the RunIt program to use this mechanism, you can edit the source after compiling and running the program. The program should generate output appropriate to the new source, not the old.

The only thing left is where to get the ClassLoader. The ClassLoader class is itself an abstract class. Predefined loaders include the SecureClassLoader of java.security, which adds permission support, and the URLClassLoader of java.net. Of the two predefined loaders, only URLClassLoader offers support for public construction through either its constructors or static newInstance methods. See the documentation for URLClassLoader for further details.

Creating a URLClassLoader involves creating an array of URL objects. These URL objects serve as the locations that the custom class loader uses to find classes. You specify the elements of the array similarly to the way you specify path elements for the CLASSPATH environment variable, where the path elements are separated by a ; on Windows and a : on Unix machines. Each element in the URL array can be a located locally or on a remote host. Anything ending in a "/" is presumed to be a directory. Anything else is presumed to be a JAR file.

For instance, if you want to create a ClassLoader that works like the default classpath, that is, only searching in the current directory, you can code the following:

File file = new File(".");

ClassLoader loader = new URLClassLoader(

new URL[] {file.toURL()}

);

The first line creates a File object, referencing the current directory. The second line calls the URLClassLoader constructor. Passed into the constructor is an array of one URL object: the URL to the File.

If you change the RunIt program to include the following code, recompile it, and run it, the program will discard the loaded classes between runs and reload them. Notice that unlike the RunIt program in the earlier Tech Tip, the following code does not create an instance of the class to invoke the main method. There is no need to create an instance of the class because the main method is static,

// Create new class loader

// with current dir as CLASSPATH

File file = new File(".");

ClassLoader loader = new URLClassLoader(

new URL[] {file.toURL()}

);

// load class through new loader

Class aClass = loader.loadClass(className.getText());

// run it

Object objectParameters[] = {new String[]{}};

Class classParameters[] =

{objectParameters[0].getClass()};

Method theMethod = aClass.getDeclaredMethod(

"main", classParameters);

// Static method, no instance needed

theMethod.invoke(null, objectParameters);

Here's the complete code example, with the RunIt program renamed to RunItReload.

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import java.io.*;

import java.net.*;

import java.lang.reflect.*;

public class RunItReload extends JFrame {

JPanel contentPane;

JScrollPane jScrollPane1 = new JScrollPane();

JTextArea source = new JTextArea();

JPanel jPanel1 = new JPanel();

JLabel classNameLabel = new JLabel("Class Name");

GridLayout gridLayout1 = new GridLayout(2,1);

JTextField className = new JTextField();

JButton compile = new JButton("Go");

Font boldFont = new java.awt.Font(

"SansSerif", 1, 11);

public RunItReload() {

super("Editor");

setDefaultCloseOperation(EXIT_ON_CLOSE);

contentPane = (JPanel) this.getContentPane();

this.setSize(400, 300);

classNameLabel.setFont(boldFont);

jPanel1.setLayout(gridLayout1);

compile.setFont(boldFont);

compile.setForeground(Color.black);

compile.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

try {

doCompile();

} catch (Exception ex) {

System.err.println(

"Error during save/compile: " + ex);

ex.printStackTrace();

}

}

});

contentPane.add(

jScrollPane1, BorderLayout.CENTER);

contentPane.add(jPanel1, BorderLayout.NORTH);

jPanel1.add(classNameLabel);

jPanel1.add(className);

jScrollPane1.getViewport().add(source);

contentPane.add(compile, BorderLayout.SOUTH);

}

public static void main(String[] args) {

Frame frame = new RunItReload();

// Center screen

Dimension screenSize =

Toolkit.getDefaultToolkit().getScreenSize();

Dimension frameSize = frame.getSize();

if (frameSize.height > screenSize.height) {

frameSize.height = screenSize.height;

}

if (frameSize.width > screenSize.width) {

frameSize.width = screenSize.width;

}

frame.setLocation(

(screenSize.width - frameSize.width) / 2,

(screenSize.height - frameSize.height) / 2);

frame.show();

}

private void doCompile() throws Exception {

// write source to file

String sourceFile = className.getText() + ".java";

FileWriter fw = new FileWriter(sourceFile);

fw.write(source.getText());

fw.close();

// compile it

int compileReturnCode =

com.sun.tools.javac.Main.compile(

new String[] {sourceFile});

if (compileReturnCode == 0) {

// Create new class loader

// with current dir as CLASSPATH

File file = new File(".");

ClassLoader loader =

new URLClassLoader(new URL[] {file.toURL()});

// load class through new loader

Class aClass = loader.loadClass(

className.getText());

// run it

Object objectParameters[] = {new String[]{}};

Class classParameters[] =

{objectParameters[0].getClass()};

Method theMethod = aClass.getDeclaredMethod(

"main", classParameters);

// Static method, no instance needed

theMethod.invoke(null, objectParameters);

}

}

}

You need to compile and execute this program in a slightly different way than you did for the RunIt program in the earlier Tech Tip. Because the custom class loader is using the current directory as the place where reloadable classes need to come from, you can't load the actual RunItReload class from the same classpath. Otherwise, the system class loader will load the compiled class from the same location (and class loader). You need to tell the compiler to send the compiled classed for RunItReload to a different location. You run the program with that other location in the classpath, not with "." in it. Remember that you need to include tools.jar in the classpath to compile. The following command sends the newly compiled .class files for RunItReload to the XYZ subdirectory. Feel free to pick a different subdirectory name. (Although the command is shown on multiple lines it needs to go on one line):

In Windows:

mkdir XYZ

javac -d XYZ -classpath

c:\j2sdk1.4.2\lib\tools.jar RunItReload.java

In Unix:

mkdir XYZ

javac -d XYZ -classpath

/homedir/jdk14/j2sdk1.4.2/lib/tools.jar

RunItReload.java

Replace homedir with your actual home directory.

If you get an error that the system cannot find the path specified, be sure to create the XYZ directory before compilation.

As before, you need to include the tools.jar file in your runtime classpath, and you need to include the XYZ directory for the actual RunItReload program. To run the program, issue the following command. (Again, although the command is shown on multiple lines, it needs to go on one line).

In Windows:

java -classpath

c:\j2sdk1.4.2\lib\tools.jar;XYZ RunItReload

In Unix:

java -classpath

/homedir/jdk14/j2sdk1.4.2/lib/tools.jar:

XYZ RunItReload

The XYZ here is carried over from the prior javac step. The target directory for compilation (specified after -d) must match the runtime classpath.

Running the program displays the GUI. Then you can:

Enter the name of the class, such as Sample2, to be compiled in the JTextField.

Enter the source code in the JTextArea. Here's the source code for Sample2:

public class Sample2 {

public static void main(String args[]) {

System.out.println(new java.util.Date());

// System.out.println("Hello, World!");

}

}

Click the Go button.

Output is sent to the console. For example, Sample2 should produce output that looks something like this:

Tue Aug 19 11:25:16 PDT 2003

Comment out the line that prints the date, and uncomment the line that prints "Hello World". Click the Go button. You should now see the following in your console:

Hello, World!

You see a different line displayed because a new class loader was created, one that unloaded previously loaded classes.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有