标签:初始化 zip 导致 默认 void instance 区域 是什么 main函数
ClassLoader的主要职责就是负责各种class文件到jvm中,ClassLoader是一个抽象的class,给定一个class文件的二进制名,ClassLoader会尝试加载并且在jvm中生成构建这个类的各个数据结构,然后使其分布在对应的内存区域中。
类的记载过程一般分为三个比较大的阶段,分别是加载阶段,连接阶段和初始化阶段,如下图所示
加载阶段:主要负责查找并且加载类的二进制数据文件,其实就是class文件。
连接阶段:连接阶段所做的工作比较多,细分的话还可以分为如下三个阶段。
初始化阶段:为类的静态变量赋予正确的初始值(代码编写阶段给定的值)
jvm对类的初始化是一个延迟的机制,即:使用的是lazy的方式,当一个类在首次使用的时候才会被初始化,在同一个运行时包下,一个class只会被初始化一次
(运行时包和类的包时有区别的,下次再说),那么什么是类的主动使用和被动使用呢?
jvm虚拟机规范规定了,每个类或者接口被java程序首次主动使用时才会对其进行初始化,当然随着JIT技术越来越成熟,JVM运行期间的编译也越来越只能,
不排除JVM在运行期间提前预判并且初始化某个类。
JVM同时规范了以下6种主动使用类的场景,具体如下
public class SimpleOne { static{ System.out.println("我会被初始化"); } public static int x = 10; }
这段代码中x是一个简单的静态变量,其他类即使不对SimpleOne进行new的创建,直接访问x也会导致类的初始化。
public class SimpleTwo { static{ System.out.println("我会被初始化"); } // 静态方法 public static void test(){ } }
同样,在其他类中直接调用test静态方法也会导致类的初始化。
public class InvokeClass { public static void main(String[] args) { try{ Class.forName("com.lanlei.classLoader.SimpleOne"); }catch(ClassNotFoundException e){ } } }
运行上面的代码,同样会看到静态代码块中的输出语句执行。
public class Parent { static{ System.out.println("父类初始化了"); } public static int y = 100; }
public class Child extends Parent{ static{ System.out.println("子类会被初始化"); } public static int x = 10; }
public class ActiveLoadTest { public static void main(String[] args) { System.out.println(Child.x); } }
在ActiveLoadTest中,我们调用了Child的静态变量,根据前面的知识可以得出Chid类被初始化了,Child类又是Parent类的子类,子类的初始化会进一步导致
父类的初始化,当然这里需要注意的一点是,通过子类使用父类的静态变量只会导致父类的初始化,子类则不会被初始化,示例代码如下:
public class ActiveLoadTest { public static void main(String[] args) { System.out.println(Child.y); } }
改写后的ActiveLoadTest,直接用Child访问子类的静态变量y,并不会导致Child的初始化,仅仅会导致Parent的初始化。
除了上述6种情况,其余的都被称为被动引用,不会导致类的加载和初始化。
在正式讲解类的各个阶段的内容之前,请大家思考下面这段程序的输出结果,如果你不能准确计算出结果或者感觉有点模棱两可,那么请认真看完本小节。
public class Singleton { // ① private static int x =0; private static int y; private static Singleton instance = new Singleton(); // ② private Singleton(){ x++; y++; } public static Singleton getInstance(){ return instance; } public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println(singleton.x); System.out.println(singleton.y); } }
运行上面的程序代码输出将是多少?如果将注释②的代码移到注释①的位置,输出结果又是什么呢?两种输出会产生不一样的结果,为何会发生这样的
现象,下面就看下本小节寻找答案。
简单来说,类的加载就是将class文件中的二进制数据读取到内存中,然后将该字节流所代表的静态存储结构转换为方法区中运行时的数据结构,并且在堆内存总
生成一个该类的java.lang.Class对象,作为访问方法区数据结构的入口,如下图所示。
类加载的最终产物就是堆内存中的class对象,对同一个ClassLoader来讲,不管某个类被加载了多少次,对应到堆内存中的class对象始终是同一个。虚拟机
规范中指出了类的加载是通过一个全限定名(包名+类名)来获取二进制数据流,但是并没有限定必须通过某种方式获得,比如我们常见的二进制文件的形式,
但是除此之外还会有如下的几种形式。
类的连接阶段可以细分为三个小的过程,分别为验证,准备,解析。
先写到这,改天补充,有点事
标签:初始化 zip 导致 默认 void instance 区域 是什么 main函数
原文地址:https://www.cnblogs.com/pb13/p/11371997.html