Java虚拟机与程序的生命周期
•在如下几种情况下,Java虚拟机将结束生命周期
–执行了System.exit()方法
–程序正常执行结束
–程序在执行过程中遇到了异常或错误而异常终止
–由于操作系统出现错误而导致Java虚拟机进程终止
类的加载连接与初始化:如下图
加载:
--是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,
然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构
--类的加载的最终产品是位于堆区中的Class对象
--Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口
如图:
•连接
–验证:确保被加载的类的正确性
-类文件的结构检查
–语义检查
–字节码验证
–二进制兼容性的验证
–准备:为类的静态变量分配内存,并将其初始化为默认值
–解析:把类中的符号引用转换为直接引用
•初始化:为类的静态变量赋予正确的初始值
类的初始化步骤:
假如这个类还没有被加载和连接,那就先进行加载和连接
假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接父类
假如类中存在初始化语句,那就依次执行这些初始化语句
注意:当java虚拟机初始化一个类时,要求它的所有父类都已经初始化,但是这条规则
并不适用于接口,在初始化一个类时并不会先初始化它所实现的接口,在初始化一个
接口时并不会初始化它的父接口,只有当程序首次使用特定接口的静态变量时才会导致
该接口的初始化
•Java程序对类的使用方式可分为两种
–主动使用(六种)
–创建类的实例
–访问某个类或接口的静态变量,或者对该静态变量赋值
–调用类的静态方法
–反射(如Class.forName(“com.shengsiyuan.Test”))
–初始化一个类的子类
–Java虚拟机启动时被标明为启动类的类(Java Test)
–被动使用
注意:只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用
所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们
//类的初始化过程
class Test{
public static final Test test=new Test();
public static int a;
public static int b=0;
Test(){
a++;
b++;
}
}
class Demo{
public static void main(String[] args){
System.out.println(Test.a);
System.out.println(Test.b);
}
}
/*
程序运行结果:1,0
分析:首先主函数的类被加载进内存,对主函数所在的类进行静态初始化,接着对Test类进行
加载连接初始化,加载是将类的在二进制代码加载进内存,连接过程中为类的静态变量
分配内存并将其初始化为默认值,这时Test中的a,b被赋值为默认值0,test为null;然后解析将类中
的符号引用转换为直接引用,最后初始化,为类的静态变量赋于正确有初始值,这时会new Test();
调用Test构造函数,a,b自增变为1,接着a不变,b又被赋为0,最后结果变为1,0*/
class Test{
public static int a;
public static int b=0;
public static final Test test=new Test();
Test(){
a++;
b++;
}
}
class Demo{
public static void main(String[] args){
System.out.println(Test.a);
System.out.println(Test.b);
}
}
/*
程序运行结果:1,1
结果分析如上,只是在初始化时,a,b先进行赋值,a是默认值0,b被赋值为0,
接着new Test(),a,b被自增为1,结果为1.1
注意:静态变量(静态代码块)的初始化是从上到下,一步一步进行的
*/
//类的主动使用
interface Parent{
int a=1;
}
class Chirld implements Parent{
static{
System.out.println("chirld code");
}
}
class Demo{
public static void main(String[] args){
/*
程序运行结果为:1
为什么没有打印child code;明明使用了child类?
因为必须是主动作用类时才会对类进行初始化,而chirld.a并不是对类的主动使用
只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,
才可以认为是对类或接口的主动使用
*/
System.out.println(Chirld.a);
}
}
类加载器:
类加载器用于来把类加载到java虚拟机中,从JDK1.2版本开始,类加载过程采用父亲委托机制
这种机制能更好地保证java平台的安全,在委托机制上,除了java虚拟机自带的根类加载器以处,
其余的类加载器都有且只有一个父加载器;当java程序请求某个加载器加载类时,这个加载器首先
委托自己的父加载器去加载这个类,若父类能加载这个类,则由父加载器完成加载任务,
否则才由这个加载器本身来加载这个类
有两种类型的类加载器
–Java虚拟机自带的加载器
•根类加载器(Bootstrap)
•扩展类加载器(Extension)
•系统类加载器(System)
–用户自定义的类加载器
•java.lang.ClassLoader的子类
•用户可以定制类的加载方式
注意:类加载器并不需要等到某个类被“首次主动使用”时再加载它
调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化
如图:
根加载器(Bootstrap):该加载器没有父加载器,它负责加载虚拟机的核心类库
根加载器的实现依赖于底层操作系统,属于虚拟机的实现一部分,它并没有
继承java.lang.ClassLoader类
扩展类加载器:它的父加载器为根类加载器,它从java.ext.dirs系统属性所指定的
目录中加载类库。或者从JDK的安装目录的jre\lib\ext子目录下加载类库
系统类加载器:也称为应用类加载器,它的父类加载器为扩展加载器,它从环境变量
classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的
加载器的默认加载器,系统加载器是纯java类,是java.lang.ClassLoader类的子类
/*
自定义类加载器
1.继承ClassLoader类
2.定义方法加载二进制数据
3.重写findClass()方法,并调用defineClass()方法来返回一个Class对象
*/
package fu.dao.li.test;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
class Student {
int age=25;
public Student(){
System.out.println("student is loaded by:"+this.getClass().getClassLoader());
}
}
//自定义类加载器,继承ClassLoader类
public class MyClassLoader extends ClassLoader{
private String filename;
private String path;
private String type=".class";
public MyClassLoader() {
super();
}
public MyClassLoader(ClassLoader parent,String name) {
super(parent);
this.filename=name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
//自定义加载二进制数据到内存的方法
public byte[] loadData(String name){
InputStream in=null;
ByteArrayOutputStream baos=null;
int ch=0;
String fileName=name.replace(".", "\\");
try {
in=new FileInputStream(path+fileName+type);
baos=new ByteArrayOutputStream();
while((ch=in.read())!=-1){
baos.write(ch);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return baos.toByteArray();
}
//返回class对象
public Class<?> findClass(String name){
byte[] data=loadData(name);
//调用ClassLoader类中的defineClass方法
return defineClass(name,data,0,data.length);
}
public static void main(String[] args) throws Exception {
MyClassLoader loader1=new MyClassLoader();
loader1.setPath("e:\\program code\\jvmtest\\bin\\");
Class<?> claxx2=loader1.findClass("fu.dao.li.test.Student");
Object obj2=claxx2.newInstance();
}
}