什么是初始化块,有什么作用?静态初始化块与非静态初始化快有什么区别?
静态初始化块、构造函数、非静态初始化块在一个类中的执行顺序?
下面这个程序输出的结果是什么?为什么是这个结果?
public class TestK{
public static void main(String[] args){
new A();
}
{
System.out.println("Test' non-static initialization"+"blook is invoked");
}
static{
System.out.println("Test' static initialization"+"blook is invoked");
}
}
class A extends B{
A(){
System.out.println("A's constructor is invoked");
}
{
System.out.println("A's non-static initialization"+"blook is invoked");
}
static{
System.out.println("A's static initialization"+"blook is invoked");
}
}
class B{
B(){
System.out.println("B's constructor is invoked");
}
{
System.out.println("B's non-static initialization"+"blook is invoked");
}
static{
System.out.println("B's static initialization"+"blook is invoked");
}
}
----------------解决方案--------------------------------------------------------
Test' static initializationblook is invoked
B's static initializationblook is invoked
A's static initializationblook is invoked
B's non-static initializationblook is invoked
B's constructor is invoked
A's non-static initializationblook is invoked
A's constructor is invoked
执行的流程是先静态然后才是非静态,先父类再子类,先一般函数再构造函数!!
----------------解决方案--------------------------------------------------------
请问楼上的:
先静态后非静态,那为什么不输出Test' non-static initialization blook is invoked请解释一下!
----------------解决方案--------------------------------------------------------
去看下类的装载机制你就了解了!!!
因为static main的执行完程序退出!!!
----------------解决方案--------------------------------------------------------
执行的流程是先静态然后才是非静态,先父类再子类,先一般函数再构造函数!!对这句还是有点不明白,能不能具体的而把上面程序解释一下?
----------------解决方案--------------------------------------------------------
这个讲起来挺复杂的,下面的资料你看下,应该就会明白的
引用自:http://hi.baidu.com/toponer/blog/item/120faa4402c93b4f510ffe15
每个java开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉 及到 了java技术体系中的类加载。Java的类加载机制是java技术体系中比较核心的部分,虽然和大部分开发人 员直接打交道不多,但是对其背后的机理有一定理解有助于排查程序中出现的类加载失败等技术问题,对 理解java虚拟机的连接模型和java语言的动态性都有很大帮助。
//java.lang.Class.java public static Class<?> forName(String className)throws ClassNotFoundException { return forName0(className, true, ClassLoader.getCallerClassLoader()); } //java.lang.ClassLoader.java // Returns the invoker's class loader, or null if none. static ClassLoader getCallerClassLoader() { // 获取调用类(caller)的类型 Class caller = Reflection.getCallerClass(3); // This can be null if the VM is requesting it if (caller == null) { return null; } // 调用java.lang.Class中本地方法获取加载该调用类(caller)的ClassLoader return caller.getClassLoader0(); } //java.lang.Class.java //虚拟机本地实现,获取当前类的类加载器,前面介绍的Class的getClassLoader()也使 用此方法 native ClassLoader getClassLoader0();
4.3 在编写自定义类加 载器时,如果没有设定父加载器,那么父加载器是?前面讲过,在不指定父类加载器的情况下,默认采用系统类加载器。可能有人觉得不明白,现在我 们来看一下JDK对应的代码实现。众所周知,我们编写自定义的类加载器直接或者间接继承自 java.lang.ClassLoader抽象类,对应的无参默认构造函数实现如下:
//摘自java.lang.ClassLoader.java protected ClassLoader() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); } this.parent = getSystemClassLoader(); initialized = true; }
我们再来看一下对应的getSystemClassLoader()方法的实现:
private static synchronized void initSystemClassLoader() { //... sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); scl = l.getClassLoader(); //... }
我们可以写简单的测试代码来测试一下: System.out.println(sun.misc.Launcher.getLauncher ().getClassLoader()); 本机对应输出如下: AppClassLoader@197d257">sun.misc.Launcher$AppClassLoader@197d257 所以,我们现在可以相信当自定义类加载器没有指定父类加载器 的情况下,默认的父类加载器即为系统类加载器。同时,我们可以得出如下结论: 即时用户自定义类加载器不指定父类加载器,那么,同样可以加 载如下三个地方的类: 1. <Java_Runtime_Home>/lib下的类 2. < Java_Runtime_Home >/lib/ext下或者由系统变量java.ext.dir指定位置中的类 3. 当前工程类 路径下或者由系统变量java.class.path指定位置中的类 4.4 在编写自定义类加 载器时,如果将父类加载器强制设置为null,那么会有什么影响?如果自定义的类加载器不能加载指定类 ,就肯定会加载失败吗? JVM规范中规定如果用户自定义的类加载器将父类加载器强制设置为null,那么会自动将启动类加载 器设置为当前用户自定义类加载器的父类加载器(这个问题前面已经分析过了)。同时,我们可 以得出如下结论: 即时用户自定义类加载器不指定父类加载器,那么,同样可以加载到 <Java_Runtime_Home>/lib下的类,但此时就不能 够加载<Java_Runtime_Home>/lib/ext目录下的类 了。 说明:问题 3和问题4 的推断结论是基于用户自定义的类加载器本身延续了 java.lang.ClassLoader.loadClass (…)默认委派逻辑,如果用户对这一默认委派逻 辑进行了改变,以上推断结论就不一定成立了,详见问题 5。 4.5 编写自定义类加载器时,一般有哪些注意点? 1. 一般尽量不要覆 写已有的loadClass(…)方法中的委 派逻辑一般在JDK 1.2之前的版本才这样做,而且事实证明,这样做极有可能引起系统默认的类加载器不能 正常工作。在JVM规范和JDK文档中 (1.2或者以后版本中),都没有建议用户覆写loadClass(…) 方法,相比而言,明确提示开发者在开发自定义的类加载器时覆写 findClass(…)逻辑。举一个例子来验证该问题:
//用户自定义类加载器WrongClassLoader.Java(覆写loadClass逻辑) public class WrongClassLoader extends ClassLoader { public Class<?> loadClass(String name) throws ClassNotFoundException { return this.findClass(name); } protected Class<?> findClass(String name) throws ClassNotFoundException { //假设此处只是到工程以外的特定目录D:/library下去加载类 具体实现代码省略 } }
通过前面的分析我们已经知道,用户自定义类加 载器(WrongClassLoader)的默 认的类加载器是系统类加载 器,但是现在问题4种的结论就不成立了。大家可以简 单测试一下,现在 <Java_Runtime_Home>/lib、< Java_Runtime_Home >/lib/ext和工 程类路径上的类都加载不上 了。
//问题5测试代码一 public class WrongClassLoaderTest { public static void main(String[] args) { try { WrongClassLoader loader = new WrongClassLoader(); Class classLoaded = loader.loadClass("beans.Account"); System.out.println(classLoaded.getName()); System.out.println(classLoaded.getClassLoader()); } catch (Exception e) { e.printStackTrace(); } } }
(说明:D:\classes\beans\Account.class物理存在的)输出结果: java.io.FileNotFoundException: D:\classes\java\lang\Object.class ( 系统找不到指定的路径。) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init> (FileInputStream.java:106) at WrongClassLoader.findClass (WrongClassLoader.java:40) at WrongClassLoader.loadClass (WrongClassLoader.java:29) at java.lang.ClassLoader.loadClassInternal (ClassLoader.java:319) at java.lang.ClassLoader.defineClass1 (Native Method) at java.lang.ClassLoader.defineClass (ClassLoader.java:620) at java.lang.ClassLoader.defineClass (ClassLoader.java:400) at WrongClassLoader.findClass (WrongClassLoader.java:43) at WrongClassLoader.loadClass (WrongClassLoader.java:29) at WrongClassLoaderTest.main (WrongClassLoaderTest.java:27) Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object at java.lang.ClassLoader.defineClass1 (Native Method) at java.lang.ClassLoader.defineClass (ClassLoader.java:620) at java.lang.ClassLoader.defineClass (ClassLoader.java:400) at WrongClassLoader.findClass (WrongClassLoader.java:43) at WrongClassLoader.loadClass (WrongClassLoader.java:29) at WrongClassLoaderTest.main (WrongClassLoaderTest.java:27) 这说明,连要加载的类型的超类型java.lang.Object都加载不到了。这里列举的由于 覆写loadClass(…)引起的逻辑错误明显是比较简单的,实际引起的逻辑错误可能复杂的多。
//问题5测试二 //用户自定义类加载器WrongClassLoader.Java(不覆写loadClass逻辑) public class WrongClassLoader extends ClassLoader { protected Class<?> findClass(String name) throws ClassNotFoundException { //假设此处只是到工程以外的特定目录D:/library下去加载类 具体实现代码省略 } }
将自定义类加载器代码WrongClassLoader.Java做以上修改后,再运行测试代码,输 出结果如下: beans.Account WrongClassLoader@1c78e57 这说明,beans.Account加载成功,且是由自定义类加载器WrongClassLoader加载。 这其中的原因分析,我想这里就不必解释了,大家应该可以分析的出来了。 2. 2、正确设置父类加载器通过上面问题4和问题5的分析我们应该已经理解,个人觉得这是自定义用户类加载器时最 重要的一点,但常常被忽略或者轻易带过。有了前面JDK代码的分析作为基础,我想现在大家都 可以随便举出例子了。 3. 3、保证findClass( String )方法的逻辑正确性事先尽量准确理解待定义的类加载器要完成的加载任务,确保最大程度上能够获取到对应的字节码 内容。
[此贴子已经被作者于2007-9-28 22:30:33编辑过]
----------------解决方案--------------------------------------------------------
谢谢ls!
----------------解决方案--------------------------------------------------------
ding ding hao
----------------解决方案--------------------------------------------------------