【JAVA类加载的全过程】

1.其实这一部分的内容在解析static那几个关键字已经有所提及,但是类加载在作为一个很重要的点,我们单独拿出来再看看。我们知道,在执行JAVA程序的时候,首先是编译,把源码编译成字节码文件。然后是执行,其实这个执行的过程就是一个类加载的过程。执行一个类,就是JVM把这个类的字节码加载到内存中操作,那么我们来分析一下这个过程

2.首先是类加载。

(1)JVM把字节码文件加载到方法区,同时在堆的里面生成一个class对象,并且生成一个动态的数据结构。注意这里,加载完成之后在堆的里面就会有一个Class对象,这也就是为什么我们有了反射操作。

(2)链接。链接一般分为三步,验证,准备,解析。验证一般都是验证这个字节码文件是否合法,对虚拟机是不是有害等等;准备就是为类的变量分配内存(注意是类的变量,也就是静态的成员变量),并且赋初始值,这里的初始值,并不是说代码里面的赋值,而是默认的值,一般基本数据类型就是0,引用是null;解析就是把符号引用解析为直接引用,这个不深入,我们后边专门说说。

(3)初始化:调用类的构造器,完成对类的初始化。这个有人可能会有疑问,什么叫做类的构造器?其实这个是编译器帮我们完成的,我们无法直接定义类构造器,编译器在在编译的过程中会自动收集我们的静态赋值操作,例如我们的静态代码块/静态成员变量等等。这个时候,才完成对静态成员的赋值(加入有赋值操作)。

这些操作在多线程下也是线程安全的,虚拟机会保证正确的加锁和同步。

那么我们来看一段代码吧。

package D1;
import java.util.*;
public class test1
{
	static
	{
		System.out.println("静态初始化test1");
	}
	public static void main(String []args) throws Exception
	{
           	B b1=new B();
	}
}
class A
{
	public static int  width=100;
  public static final int id=100;
	static 
	{
		System.out.println("A 被加载!");
	}
	public A()
	{
		System.out.println("A完成构造!");
	}
}

class B extends A
{
	static
	{
		System.out.println("静态初始化B");
	}
	public B()
	{
		System.out.println("B完成构造");
	}
	
	
	
}


知道了上边的东西,这段代码的输出就很明确了。首先肯定是加载test1,然后夹杂B的父类A,最后加载B,创建对象的时候,肯定也是首先创建A,再构造B。那么输出如下:

静态初始化test1
A 被加载!
静态初始化B
A完成构造!
B完成构造

3.那么类什么时候被加载呢?也就是类加载的时机。其实类被主动引用的时候是会被加载的,但是在被动引用的时候不会被加载。具体情况如下:

主动引用一个类:

(1)new这个类的对象

(2)通过反射访问这个类

(3)加载这个类的子类

(4)访问这个类的静态变量

被动引用一个类:

  (1)定义对象数组,类不会被加载

(2)访问一个类中的常量,类不会被加载(因为常量都是在常量池中,不需要加载这个类就可以访问)

但是我们还需要注意两点:

(1)在继承的关系中,访问类的静态成员数据时,只有定义这个成员的类才会被加载,其他通过继承得到的这个成员的类不会被加载.

(2)一个类在多次定义对象的时候,只会被加载一次。

下边是测试的代码。敲一敲 就明白了

package D1;
import java.util.*;
public class test1
{
	static
	{
		System.out.println("静态初始化test1");
	}
	public static void main(String []args) throws Exception
	{
		/**B定义两个对象,但是只会被加载一次*/
		//B b1=new B();
		//System.out.println("---------");
		//B b2=new B();
		/**访问一个静态块的时候,只有真正访问的地方才会出现初始化。这里虽然时访问的时B.width,但是由于width是在A中定义的,所以只会加载A*/
		//System.out.println(B.width);
		/**访问final修饰的变量,也就是常量,不会进行类加载*/
		//System.out.println(B.id);
		/** 反射操作这个类,进行类加载*/
		//Class.forName("D1.A");//采用反射访问的时候,也是会被加载
	   /**定义对象数组的时候,不会进行类加载*/
		//	A []a=new A[10];
	}
}
class A
{
	public static int  width=100;
  public static final int id=100;
	static 
	{
		System.out.println("A 被加载!");
	}
	public A()
	{
		System.out.println("A完成构造!");
	}
}

class B extends A
{
	static
	{
		System.out.println("静态初始化B");
	}
	public B()
	{
		System.out.println("B完成构造");
	}
	
	
	
}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值