
上面这七个过程,除了解析这个过程外,其余过程必须按部就班地执行,即顺序是确定的,而解析过程不一定,在某些情况下可以在初始化阶段之后再执行,这是为了支持java语言的运行时绑定(也称为动态绑定或晚期绑定)。class SuperClass{
static{
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass{
static{
System.out.println("SubClass init!");//子类中引用父类的静态字段,不会导致类初始化
}
}
public class Test {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}运行结果:SuperClass init!123可以看到,只会打印出父类的初始化语句。class ConstClass{
static{
System.out.println("ConstClass init!");
}
public static final String HELLOWORLD = "hello world";
}
public class Test {
public static void main(String[] args) {
System.out.println(ConstClass.HELLOWORLD);
}
}运行结果:hello world只是输出了hello world,并没有输出ConstClass init!,可见ConstClass类并没有被初始化。(1)通过一个类的全限定名来获取其定义的二进制字节流。
(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
(3)在内存中生成一个代表这个类的java.lang.Class对象(并没有明确规定是在java堆中,对于HotSpot虚拟机来说,Class对象比较特殊,它虽然是对象,但是存放在方法区里面),作为对方法区中这些数据的访问入口。
对于(1),并没有指明二进制字节流的获取途径,也即不一定都是从一个Class文件中获取,还可以从如下方式获取:
1)从压缩包中获取,比如 JAR包、EAR、WAR包等public class Test {
static{
i = 0;//可以给变量赋值,编译通过
System.out.println(i);//编译不通过!!不能进行访问后面的静态变量
}
static int i =1;
}有点与我们平常的认知相反,这里是可以下赋值,却不能访问...任意一个类,都需要由加载它的类加载器和这个类本身共同确定其在Java 虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达的更通俗一些:比较两个类是否相等,只有在这两个类是同一个类加载器加载的前提下才意义。否则,即使这两个类来自同一个Class文件,被同一个虚拟机加载,但只要加载他们的类加载器不同,那这两个类就必定不相等。
这里的“相等”,包括代表类的 Class 对象的equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果,也包括 instanceof 关键字对对象所属关系判定等情况。下面代码演示了不同类加载器对 instanceof 关键字运算的结果的影响。
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@Override
public Class<> loadClass(String name)
throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1)
+ ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
Class c = myLoader.loadClass("org.bupt.xiaoye.blog.ClassLoaderTest");
Object obj = c.newInstance();
System.out.println(obj.getClass());
System.out.println(ClassLoaderTest.class);
System.out.println(obj instanceof ClassLoaderTest);
}
}运行结果如下:class org.bupt.xiaoye.blog.ClassLoaderTest class org.bupt.xiaoye.blog.ClassLoaderTest false我们使用了一个自定义的类加载器去加载ClassLoaderTest,由第一句也可以看出这个对象也的确是ClassLoaderTest实例化出来的对象,但是这个对象在与类class org.bupt.xiaoye.blog.ClassLoaderTest 做属性检查的时候却反悔了false,这就是因为虚拟机中存在了两个ClassLoaderTest类,一个由系统应用程序类加载器加载,一个由我们自定义的类加载器加载,虽然是 来自同一个Class文件,但依然是两个独立的类。从虚拟机的角度来讲,只存在两种不同的类加载器:
一种是启动类加载器(Bootstrap ClassLoader),这个类加载器用 C++ 语言实现, 是虚拟机自身的一部分:
另一种就是所有其它的类加载器, 这些类加载器用Java 语言实现,独立于虚拟机外部,并且全都继承与抽象类 java.lang.ClassLoader。
从Java 开发人员的角度来看,类加载器还可以划分的更细致一些,绝大多数Java 程序都会用到以下3种系统提供的类加载器:
(1)启动类加载器(Bootstrap ClassLoader) : 这个类加载器负责将存放在 <JAVA_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar ,名字不符合类库不会加载) 类库加载到虚拟机内存中。启动类加载器无法被 java 程序直接引用,如需要,直接使用 null 代替即可。
图中的类加载器之间的这种层次关系,称为类加载器的双亲委派模型。双亲委派模型要求除了顶层的启动类加载器,其余的类加载器都应该有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承关系来实现,而是使用组合关系来复用父加载器的代码。protected Class<> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 首先检查类是否已经被加载过
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 调用父类加载器加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
//父类加载器无法完成加载,调用本身的加载器加载
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(
t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(
t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
热门源码