类的加载指的是将类的.class文件中的二进制数据读取到内存中,将其放在运行时数据的方法区内,然后在内存中创建一个java.lang.Class对象(规范并说明Class对象位于在哪里,HotSpot虚拟机将其放在了方法去中)用来封装类在方法去内的数据结构。
- 在java代码中,类型的加载、连接与初始化的过程都是在程序运行期间完成的
- 提供了更大的灵活性,增加了更多的可能性
一.类的五个阶段
- 加载:查找并加载类的二进制数据
- 连接:
- 验证:确保被加载类的正确性
- 准备:为类的静态变量分配内存,并将其初始化为默认值
- 解析:把类中的符号引用转换为直接引用
- 初始化:为类的静态变量赋予正确的初始值
- 使用
- 卸载
//假设
class Test {
public static int a = 1;
}
//在连接的准备阶段虚拟机会把a赋予默认值0分配空间,而在初始化阶段才会赋予正确的值
二、在如下情况下java虚拟机会结束生命进程
- 执行System.exit()
- 程序正常执行结束
- 程序在执行中遇到错误异常终止
- 由于操作系统错误导致的java虚拟性进程终止
三、Java类的使用
- Java程序对类的使用方式可以分为两种
- 主动使用
- 被动使用
- 所有的java虚拟机实现必须在每个类的接口被java程序“首次主动使用”时才初始化他们
- 主动使用(七种) 本质根据虚拟机的助记符的使用
- 创建类的实例 getstatic
- 访问某个类或者接口的静态变量,或者对该静态变量赋值 putstatic
- 调用类的静态方法 invokestatic
- 反射(如Class.forName(“com.test.Test”))
- 初始化一个类的子类
- Java虚拟机启动时被标明为启动类
- Jdk1.7开始提供的动态语言支持:java.lang.invoke.MethodHandler实例的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic句柄的类没有初始化,则进行初始化
- 被动使用
- 其他使用方式不会导致类的初始化产生,但有可能加载和连接
public class TestClassLoader {
public static void main(String[] args) {
System.out.println(Children.value);
//打印结果为init parent 和 parent
//Children类并没有被初始化,原因是该类并没有被主动使用
}
}
class Parent {
public static String value = "parent";
static {
System.out.println("init parent");
}
}
class Children extends Parent {
public static String value2 = "child";
static {
System.out.println("init children");
}
}
关于类加载与常量池的实例
/**
* 类加载与常量池
* @author Administrator
*
*/
public class TestClassLoader2 {
public static void main(String[] args) {
System.out.println(Parent2.value);
//打印结果为parent
//当使用final关键字后在编译阶段,常量被存入到调用这个方法所在类的常量池中
//就是说在TestClassLoader2的常量池中,之后TestClassLoader2和Parent2就没有任何关系了
//甚至,我们可以将Parent2的class删除
//本质上调用类并不会直接引用到定义常量的类
//因此并不会触发定义常量的类的初始化
}
}
class Parent2 {
public static final String value = "parent";
static {
System.out.println("init parent");
}
}
示例2:
public class TestClassLoader3 {
public static void main(String[] args) {
System.out.println(Parent3.value);
//打印结果为
//init parent
//016391ec-6e45-4ffd-a3ed-a3391f633d09
//当value = UUID.randomUUID().toString()时编译阶段无法获取value的值
//必须要对类进行初始化后才能得到
//当一个常量在编译期间无法确定,就不会放到调用类的常量池中
}
}
class Parent3 {
public static final String value = UUID.randomUUID().toString();
static {
System.out.println("init parent");
}
}
四、JVM参数的使用
-XX:+<option>, 表示要开启option选项
-XX:-<option>, 表示要关闭option选项
JVM提供大量参数有些参数默认开启有些默认关闭,”+”,”-”为参数开关
-XX:<option>=<value>
表示将option的值设置为value
五、简单的助记符:
Idc表示将int,float,String类型的常量值从常量池推送至栈顶
bipush表示将单字节(-128,127)的常量值推送至栈顶
sipush表示将一个短整型常量值(-32768,32767)的常量值推送至栈顶
iconst_1表示将int类型1推送至栈顶(-1,0,1,2,3,4,5)特殊
Iconst_m1 表示将int类型-1推送至栈顶