public class StaticTest ...{
public static int X = 10;
public static void main(String[] args) ...{
System.out.println(Y); //输入60
}
static ...{
X = 30;
}
public static int Y = X * 2;
} 在上面的代码中,在初始化的时候,静态域的初始化和静态代码块的执行会从上到下依次执行。因此变量X的值首先初始化成10,后来又被赋值成30;而变量Y的值则被初始化成60。
Java类和接口的初始化只有在特定的机遇才会发作,这些机遇包括:
创建一个Java类的实例。如:MyClass obj = new MyClass()
调用一个Java类中的静态办法。如:MyClass.sayHello()
在顶层Java类中执行assert语句。
经过Java反射API也能够形成类和接口的初始化。需求注意的是,当访问一个Java类或接口中的静态域的时候,只有真正声明这个域的类或接口才会被初始化。考虑上面的代码:
class B ...{
static int value = 100;
static ...{
System.out.千选拖把println(Class B is initialized.); //输入
}
}
class A extends B ...{
static ...{
System.out.println(Class A is initialized.); //不会输入
}
}
public class InitTest ...{
public static void main(String[] args) ...{
}
创建自己的类加载器
在 Java应用开发过程中,能够会需求创建应用自己的类加载器。典型的场景包括实现特定的Java字节代码查找方式、对字节代码停止加密/解密以及实现同名 Java类的隔离等。创建自己的类加载器并不是一件复杂的事情,只需求继承自java.lang.ClassLoader类并覆写对应的办法即可。 java.lang.ClassLoader中提供的办法有不少,上面介绍几个创建类加载器时需求考虑的:
defineClass():这个办法用来完成从Java字节代码的字节数组到java.lang.Class的转换。这个办法是不能被覆写的,一般是用原生代码来实现的。
findLoadedClass():这个办法用来根据名称查找曾经加载过的Java类。一个类加载器不会反复加载同一名称的类。
findClass():这个办法用来根据名称查找并加载Java类。
loadClass():这个办法用来根据名称加载Java类。
resolveClass():这个办法用来链接一个Java类。
这里比较 容易混杂的是findClass()办法和loadClass()办法的作用。前面提到过,在Java类的链接过程中,会需求对Java类停止解析,而解析能够会招致当前Java类所援用的其它Java类被加载。在这个时候,JVM就是经过调用当前类的定义类加载器的loadClass()办法来加载其它类的。findClass()办法则是应用创建的类加载器的扩展点。应用自己的类加载器应该覆写findClass()办法来添加自定义的类加载逻辑。 loadClass()办法的默认实现会担任调用findClass()办法。
前面提到,类加载器的代理形式默认使用的是父类优先的战略。这个战略的实现是封装在loadClass()办法中的。如果希望修改此战略,就需求覆写loadClass()办法。
上面的代码给出了自定义的类加载的罕见实现形式:
public class MyClassLoader extends ClassLoader ...{
protected Class findClass(String name) throws ClassNotFoundException ...{
byte[] b = null; //查找或生成Java类的字节代码
return defineClass(name, b, 0, b.length);
}
}