`
deepnighttwo
  • 浏览: 52158 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Get a load of that name! (很不错的文章!)

 
阅读更多

Get a load of that name!

Subtle differences in various ways you can dynamically load a class

<!--<BLOCKQUOTE><STRONG>Summary</STRONG><BR>--><!--</BLOCKQUOTE>-->By Vladimir Roubtsov


Printer-friendly versionPrinter-friendly version | Send this article to a friendMail this to a friend


<!-- REPLACE PAGECOUNT -->

<!-- START BIG AD (336x280) jw-articles-336x280.txt -->

<!-- END BIG AD (336x280) -->

March 14, 2003

Q What is the difference between Class.forName() and ClassLoader.loadClass()?

A Both methods try to dynamically locate and load a java.lang.Class object corresponding to a given class name. However, their behavior differs regarding which java.lang.ClassLoader they use for class loading and whether or not the resulting Class object is initialized.

The most common form of Class.forName(), the one that takes a single String parameter, always uses the caller's classloader. This is the classloader that loads the code executing the forName() method. By comparison, ClassLoader.loadClass() is an instance method and requires you to select a particular classloader, which may or may not be the loader that loads that calling code. If picking a specific loader to load the class is important to your design, you should use ClassLoader.loadClass() or the three-parameter version of forName() added in Java 2 Platform, Standard Edition (J2SE): Class.forName(String, boolean, ClassLoader).

Additionally, Class.forName()'s common form initializes the loaded class. The visible effect of this is the execution of the class's static initializers as well as byte code corresponding to initialization expressions of all static fields (this process occurs recursively for all the class's superclasses). This differs from ClassLoader.loadClass() behavior, which delays initialization until the class is used for the first time.

You can take advantage of the above behavioral differences. For example, if you are about to load a class you know has a very costly static initializer, you may choose to go ahead and load it to ensure it is found in the classpath but delay its initialization until the first time you need to make use of a field or method from this particular class.

The three-parameter method Class.forName(String, boolean, ClassLoader) is the most general of them all. You can delay initialization by setting the second parameter to false and pick a given classloader using the third parameter. I recommend always using this method for maximum flexibility.

Class initialization errors are tricky
Just because you successfully load a class does not mean there won't be any more problems. Recollect that static initialization code can throw an exception, and it will get wrapped in an instance of java.lang.ExceptionInInitializerError, at which point, the class becomes unusable. Thus, if it is important to process all such errors at a known point in code, you should use a Class.forName() version that performs initialization.

Furthermore, if you handle ExceptionInInitializerError and take measures so that the initialization can be retried, it will likely not work. This code demonstrates what happens:

public class Main
{
public static void main (String [] args) throws Exception
{
for (int repeat = 0; repeat < 3; ++ repeat)
{
try
{
// "Real" name for X is outer class name+$+nested class name:
Class.forName ("Main$X");
}
catch (Throwable t)
{
System.out.println ("load attempt #" + repeat + ":");
t.printStackTrace (System.out);
}
}
}

private static class X
{
static
{
if (++ s_count == 1)
throw new RuntimeException ("failing static initializer...");
}

} // End of nested class

private static int s_count;

} // End of class

This code attempts to load the nested class X three times. Even though X's static initializer fails only on the first attempt, all of them fail:

>java Main
load attempt #0:
java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:140)
at Main.main(Main.java:17)
Caused by: java.lang.RuntimeException: failing static initializer...
at Main$X.<clinit>(Main.java:40)
... 3 more
load attempt #1:
java.lang.NoClassDefFoundError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:140)
at Main.main(Main.java:17)
load attempt #2:
java.lang.NoClassDefFoundError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:140)
at Main.main(Main.java:17)

It is slightly surprising that the errors on subsequent load attempts are instances of java.lang.NoClassDefFoundError. What happens here is that the JVM has already noted the fact that X has been loaded (before the initialization is attempted), and the class cannot be unloaded until the current classloader is garbage collected. So, on subsequent calls to Class.forName(), the JVM does not attempt initialization again but, rather misleadingly, throws an instance of NoClassDefFoundError.

The proper way to reload such a class is to discard the original classloader instance and create a new one. Of course, this can be done only if you had anticipated that and used the proper three-parameter form of forName().

The hidden Class.forName()
I am sure you have used Java's X.class syntax to obtain a Class object for a class whose name is known at compile time. Less well known is how this is implemented at the byte-code level. The details are different across compilers, but all of them generate code that uses the one-parameter form of Class.forName() behind the scenes. For example, javac from J2SE 1.4.1 translates Class cls = X.class; into the following equivalent form:

...
// This is how "Class cls = X.class" is transformed:
if (class$Main$X == null)
{
class$Main$X = class$ ("Main$X");
}
Class cls = class$Main$X;

...

static Class class$ (String s)
{
try
{
return Class.forName (s);
}
catch (ClassNotFoundException e)
{
throw new NoClassDefFoundError (e.getMessage());
}
}

static Class class$Main$X; // A synthetic field created by the compiler

Of course, everything mentioned above about Class.forName()'s short form always initializing the class in question applies to X.class syntactic form as well. The details are different when such syntax is used to get Class objects for primitive and array types, and I leave that as an exercise for curious readers.

Fun with Sun's javac
In the previous example, you saw that the result of loading the class was cached in a special package-private static field artificially created by the compiler, and a synthetic helper method executed Class.forName(). The reason this is convoluted may be because the syntax used was unavailable in early Java versions, so the feature was added on top of the Java 1.0 byte-code instruction set.

Armed with this insight, you can have a bit of fun at the compiler's expense. Compile this tongue-in-cheek code snippet using javac from J2SE 1.3.1:

public class Main
{
public static void main (String [] args) throws Exception
{
System.out.println ("String class: " + String.class);
class$java$lang$String = int.class;
System.out.println ("String class: " + String.class);
}

static Class class$java$lang$String;

} // End of class

If you run it, you will get the following, which is ridiculous at best:

>java Main
String class: class java.lang.String
String class: int

At least the compiler in J2SE 1.4.1 will reject the code above. But you can still fool it by setting the field reflectively:

public static void main (String [] args) throws Exception
{
System.out.println ("String class: " + String.class);
Main.class.getDeclaredField ("class$java$lang$String").set (null, int.class);
System.out.println ("String class: " + String.class);
}

So, next time you code the familiar Class.forName() incantation, you should know what it entails and what alternatives exist.


<!-- REPLACE PAGEURLS --><!-- REPLACE TALKBACK -->

Printer-friendly versionPrinter-friendly version | Send this article to a friendMail this to a friend

About the author
Vladimir Roubtsov has programmed in a variety of languages for more than 13 years, including Java since 1995. Currently, he develops enterprise software as a senior developer for Trilogy in Austin, Texas.

分享到:
评论

相关推荐

    2009 达内Unix学习笔记

    [] 匹配中括号里的内容[a-z][A-Z][0-9]。 ! 事件。 $ 取环境变量的值。 | 管道。把前一命令的输出作为后一命令的输入,把几个命令连接起来。 |经常跟tee连用,tee 把内容保存到文档并显示出来。 三、通用后...

    Live-debug-a-running-Process

    Live debugging of a running process refers to the process of debugging a program that is currently executing, without interrupting its normal operation. This technique is particularly useful when ...

    二级减速器课程设计说明书reducer design specification.doc

    In addition to the pure material transport, it can also cooperate with the requirements of the technological process in the production process of various industrial enterprises to form a rhythmic ...

    VB编程资源大全(英文源码 控件)

    Label3D.zip This is a Label 3D user control that works like the standard VB label control, but you can define the 3D look of the control.&lt;END&gt;&lt;br&gt;14 , D-Tray101.zip This is a useful tool to ...

    BURNINTEST--硬件检测工具

    Here is a summary of all changes that have been made in each version of BurnInTest. Release 5.3 build 1035 revision 4 WIN32 release 10 November 2008 - Lenovo China specific build. Lenovo system ...

    C 语言编缉神经网络工具

    An example of a valid weights file that you have on this shareware diskette is mytrain.wts. System Requirements You need a PC with EGA or VGA to run this. We have never tried it on a CGA, but ...

    微软内部资料-SQL性能优化3

    This lesson outlines some of the common causes that contribute to the perception of a slow server. What You Will Learn After completing this lesson, you will be able to:  Describe locking ...

    VB编程资源大全(英文源码 网络)

    chatclnt.zip Client side of an internet chat program&lt;END&gt;&lt;br&gt;9 , chatserv.zip Server side of an internet chat program&lt;END&gt;&lt;br&gt;10 , hlink.zip Is a control that you can use to link your ...

    CE中文版-启点CE过NP中文.exe

    There is apparently some malware going around that blocks execution of Cheat Engine (Saying file missing, check filename, etc...) If you have been a victim of this then try this windows repair tool to...

    acpi控制笔记本风扇转速

    control method attempts to create 2 objects of the same name. This once again returns AE_ALREADY_EXISTS. When this exception occurs, it invokes the mechanism that will dynamically serialize the ...

    UE(官方下载)

    The benefit of a column maker is that it can help you to format your text/code, or in some cases to make it easier to read in complex nested logic. Quick Open UltraEdit and UEStudio provide multiple ...

    ImpREC 1.7c

    - Added a filter to "Get API Calls" to get valid addresses only - Fixed wrong image base usage when disabling "Use PE Header From Disk" for reloc'ed target for example (Thanks to Thigo) v1.4.1a -...

    一个跨平台的CString源码

    The name "CStdString" is just a #define of one of these, // based upone the UNICODE macro setting // // This header also declares our own version of the MFC/ATL UNICODE-MBCS // conversion macros. ...

    Using LUA with Visual C++ (Introduction)

    the second one is a pointer to a user-defined reader function, the third pointer is a user-defined value that the reader function will receive, and the fourth one is a name we decide ourselves for ...

    经典小巧的表格组件 EhLib4.14

     it is necessary to elaborate the name of type by the name of the module.  For instance write GridsEh.TGridDrawState or Grids.TGridDrawState instead  of TGridDrawState. + In TDBGridEh  Added ...

    Sakemail

    The side effect for this is that YOUR app must check if the host is a host name or a IP address, in my app I remove the periods and try to convert the result to a float (long integers don‘t work, ...

    EhLib-delphi

     it is necessary to elaborate the name of type by the name of the module.  For instance write GridsEh.TGridDrawState or Grids.TGridDrawState instead  of TGridDrawState. + In TDBGridEh  Added ...

    Advanced Apple Debugging & Reverse Engineering v0.9.5

    In this chapter, you’re going to get acquainted with LLDB and investigate the process of introspecting and debugging a program. You’ll start off by introspecting a program you didn’t even write — ...

    VB编程资源大全(英文源码 表单)

    InetPass.zip This example demonstrates how to get windows' cached passwords. "This example will show all of the cached passwords on a local system !"&lt;END&gt;&lt;br&gt;29 , Toolbar1.zip Label ToolBar....

    OutlookAttachView v2.73

    This means that you can save a list with large amount of attachment lines into your disk without any memory problem, as long as you have enough disk space to store the saved file. The drawback of ...

Global site tag (gtag.js) - Google Analytics