标签:假设 字节码 png app 技术 com 实例化 object ble
类加载(Class Loading)是一种机制,他描述的是将字节码以文件形式加载到内存再经过连接、初始化后,最终形成可以被虚拟机直接使用的Java类型地过程。
Class Loading 包含了加载(Loading)、连接(Linking)、初始化(Initialization)三大部分,其中Linking又包含了三个部分:校验(Verification)、准备 (Preparation)、解析(Resolution)。而一个类的生命周期只是在Class Loader的基础上多了:使用(Using),卸载(Unloading)两部分。
Class Loaders的组成:
虚拟机需要完成以下3件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
1.JVM在加载数组的时候加载的仅仅是数组的类型类(例如String[] 加载器只会加载String这个类型类),而数组的创建则由JVM直接完成。
这里我们多问几个为什么:
2. JVM为什么只加载数组的类型类:
我认为JVM这样做的目的主要是为了节省时间,我们知道数组里面装的都是同一种类型的元素,JVM没必要将一个重复的内容加载多次浪费时间。
3. N维数组怎么加载:
如果是N维数组,类加载器会从最外层开始一层一层的递归加载,直到加载到非数组类型为止。
4. 引用类型与基本类型加载起来会不会有区别:
其实基本类型早已经在javac阶段装箱成封装对象了,例如int会被装箱成Integer,long装箱成Long等等,所以是没有区别的。
是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。但从整体上看,验证阶段大致上会完成下面4个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证。
是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
这个阶段中有两个容易产生混淆的概念需要强调一下,首先,这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。其次,这里所说的初始值“通常情况”下是数据类型的零值,假设一个类变量的定义为:
public static int value=123;
那变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器<clinit>()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行。表7-1列出了Java中所有基本数据类型的零值。
假设上面类变量value的定义变为:public static final int value=123;
编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123。
是虚拟机将常量池内的符号引用替换为直接引用的过程
在连接的准备阶段,类变量已赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员自己写的逻辑去初始化类变量和其他资源,举个例子如下:
public static int value1 = 5; public static int value2 = 6; static{ value2 = 66; }
在准备阶段value1和value2都等于0;
在初始化阶段value1和value2分别等于5和66;
何时触发初始化
类加载器名称 |
加载的范围 |
启动类加载器 Bootstrap ClassLoader |
存放在<JAVA_HOME>\lib目录中的,并且是虚拟机 识别的类库加载到虚拟机内存中 |
扩展类加载器 Extension ClassLoader |
存放在<JAVA_HOME>\lib\ext目录中的所有类库, 开发者可以直接使用; |
应用程序加载器 Application ClassLoader |
加载用户类路径上指定的类库,开发者可以直 接使用,一般情况下这个就是程序中默认的类 加载器; |
某个特定的类加载器在接到加载类的请求 时,首先将加载任务委托给父类加载器, 依次递归,如果父类加载器可以完成类加 载任务,就成功返回;只有父类加载器无 法完成此加载任务时,才自己去加载
双亲委派模型好处:Java类随着它的类加载器一起具备了带 有优先级的层次关系,保证java程序稳 定运行
public class SuperClazz { public SuperClazz(){ System.out.println("我是父类构造方法"); } static { System.out.println("我是父类静态代码块"); } public static int value = 123; public static final String STR = "hello world"; public static final int WHAT = value; } public class SonClazz extends SuperClazz { public SonClazz(){ System.out.println("我是子类构造方法"); } static { System.out.println("我是子类静态代码块"); } }
public class DemoTest { public static void main(String args[]){ /*
System.out.println(SonClazz.value);
输出结果:
我是父类静态代码块
123
结论:对于静态字段,只有直接定义这个字段的类,才能被初始化。
*/
/*
SuperClazz[] aa = new SuperClazz[10];
输出结果:(什么也没有输出)
结论:只是声明了一个数组形式的变量,类没有初始化
*/
/*
System.out.println(SonClazz.STR);
输出结果:hello world
结论:编译期的传播优化,因为是常量,所以就会直接编译到了运行的类里面,就不会再去定义的类里面找了
,STR还是在常量池里面的;
*/
/**
我是父类静态代码块
123
*/
//System.out.println(SonClazz.WHAT);
}
public class DemoTest2 { static { i=0; System.out.println(i);//这句编译器会报错:Cannot reference a field before it is defined(非法向前应用) } static int i=1; }
public class DemoTest2 { static { i=2; // System.out.println(i); } static int i=1; public static void main(String args[]) { System.out.println(i); } } 输出结果:1
标签:假设 字节码 png app 技术 com 实例化 object ble
原文地址:https://www.cnblogs.com/lys-lyy/p/10771751.html