JVM之类加载与初始化篇一(初始化)

JVM中类加载和初始化的说明: 在java代码中,类型的加载、连接、初始化过程都是在运行期完成的。

1.加载:查找并加载类的二进制数据,具体来说, 是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的 方法区中,让后在内存中创建一个java.lang.Class对象(jvm规范并未规定Class对象位于哪里,HotSpot虚拟机将其放在了方法区中)用来封装类在方法区中的 数据结构

2.连接

验证: 确保被加载类的正确性

-准备: 为类的静态变量分配内存,并将其初始化为默认值

-解析: 把类中的符号引用转为直接引用(部分)

3.初始化: 为类的静态变量赋予正确的初始值(类中指定的值)

4.类实例化:1).为新对象分配内存空间;2).为实例变量赋予默认值;3).为实例变量赋予初始值。

5.垃圾回收,对象终结。

 

JVM中类初始化:

1.Java程序对类的使用方式可以分为两种(主动使用被动使用);

2.jvm规范规定,所有的jvm的实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们

主动使用(类的初始化时机

1.创建类的实例

2.访问某个类或接口的静态变量,或对静态变量赋值

3.调用类的静态方法

4.反射(如:Class.forName())

5.初始化一个类的子类

6.程序启动时被标记为启动类的类

7.JDK1.7 提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic, REF_putStatic,      REF_invokeStatic句柄对应的类没有初始化,则初始化

被动使用

除了以上七中情况,其他使用java类的方式都被看作是对类的被动使用,都不会导致类的初始化 (*注:只是不会初始 化,但是会被加载)

类初始化步骤:

1.假如这个类还没有被加载和连接,那就先进行加载和连接。

2.假如类存在直接父类,并且这个父类还没有被初始化,那么先初始化直接父类。

3.假如类中存在初始化语句,那么一次执行这些初始化语句。

代码说明(1): 

public class InterfaceLoader {

	public static void main(String[] args) {
		System.out.println(Person.val);
	}
}

class Human{
	public static  int val = 1;
	
	static{
		System.out.println("Human被初始化");
	}
}

class Person extends Human{
	
	private static int pVal = 0;
	
	static{
		System.out.println("Person被初始化");
	}
}

输出结果:Human被初始化
                   1

结果说明: 对于静态字段来说,只有直接定义了该字段的类才会被初始化,程序中是对Human类的主动使用,而不是对Person类的主动使用,但是Person类会被加载(查看类加载的jvm参数:-XX:+TraceClassLoading)。

 

代码说明(2):

public class InterfaceLoader {

	public static void main(String[] args) {
		System.out.println(Person.pVal);
	}
}

class Human{
	public static  int val = 1;
	
	static{
		System.out.println("Human被初始化");
	}
}

class Person extends Human{
	
	public static int pVal = 0;
	
	static{
		System.out.println("Person被初始化");
	}
}

输出结果:Human被初始化
                  Person被初始化
                  0

结果说明: 程序中是对Person 类的主动使用,Person是Human的子类,因此Human会在Person之前初始化。

 

代码说明(3):

public class InterfaceLoader {

	public static void main(String[] args) {
		System.out.println(Person.pVal);
	}
}

class Human{
	public static  int val = 1;
	
	static{
		System.out.println("Human被初始化");
	}
}

class Person extends Human{
	
	public static final int pVal = 0;
	
	static{
		System.out.println("Person被初始化");
	}
}

输出结果:  0

结果说明: 程序中Person 类和Human类并没有 被初始化,因为编译器做了优化, 常量在编译阶段会存入到调用这个常量的方法所在类的常量池中。本质上,调用类并没有直接引用到定义常量的类,因此Person并没有被主动使用。

 

代码说明(4):

public class InterfaceLoader {

	public static void main(String[] args) {
		System.out.println(Person.pVal);
	}
}

class Human{
	public static  int val = 1;
	
	static{
		System.out.println("Human被初始化");
	}
}

class Person extends Human{
	
	public static final String pVal = UUID.randomUUID().toString();
	
	static{
		System.out.println("Person被初始化");
	}
}

输出结果:  Human被初始化
                    Person被初始化
                   4c2cd56d-d5a5-48ef-9e8a-99769fe18623

结果说明: 程序中pVal是常量,但是无法在编译器确定最终结果,只能在运行期得到结果,因此Person类要初始化。

 

代码说明(5):

public class InterfaceLoader {

	public static void main(String[] args) {

		Person[] pArr = new Person[1];
            System.out.println(pArr.getClass());
	}
}

class Person{
	
	public static final String pVal = UUID.randomUUID().toString();
	
	static{
		System.out.println("Person被初始化");
	}
}

输出结果:  class [Lcom.zou.classLoader.Person;

结果说明: 程序中Person类并没有被初始化,因为new Person[1] 并不是对Person类的主动使用。对于数组实例来说,其类型是由jvm运行期动态生成的,如: [Lcom.zou.classLoader.Person

 

代码说明(6):

public class InterfaceLoader {

	public static void main(String[] args) {

		System.out.println(Person.pVal);
	}
}

interface Human{
	Thread val = new Thread(){
		{
			System.out.println("Human接口初始化");
		}
	};
}

interface Person extends Human{
	
	Thread pVal = new Thread(){
		{
			System.out.println("Person接口初始化");
		}
	};
	
}

输出结果:  Person接口初始化
                    Thread[Thread-0,5,main]

结果说明: 当一个接口初始化时,并不要求其父接口都完成初始化,接口只有在主动使用时才会被初始化

 

注:静态变量的默认值和初始值。默认值:类初始化前(也就是在连接的准备阶段JVM给静态变量赋予的值,如int类型为0,String类型为null)。初始值:程序中给变量的初始值,如:public static int count = 2,2是count的初始值

 

 代码说明(7):

public class SingletonTest {
	public static void main(String[] args) {
		System.out.println(Singleton.count1);
		System.out.println(Singleton.count2);
	}
}

class Singleton{
	
	public static int count2;
	
	public static int count1 = 2;
	
	private static Singleton singleton = new Singleton();
	
	private Singleton(){
		count1++;
		count2++;
	}
	
	public static Singleton getInstance(){
		return singleton;
	}
}

输出结果:  3
                    1

结果说明: count1和count2结果分别是(2,1);结果很好理解,count1,count2被初始化后为(1,0),分别累加,最终结果(2,1)。请对比下一个程序

 

 代码说明(8):

public class SingletonTest {
	public static void main(String[] args) {
		System.out.println(Singleton.count1);
		System.out.println(Singleton.count2);
	}
}

class Singleton{
	
	private static Singleton singleton = new Singleton();
	
	public static int count2;
	
	public static int count1 = 2;
	
	
	private Singleton(){
		count1++;
		count2++;
	}
	
	public static Singleton getInstance(){
		return singleton;
	}
}

输出结果:  2
                    1

结果说明: count1和count2结果分别是(2,1)。类的初始化在java层面上来看是由上自下依次初始化,在类初始化之前,在准备阶段,count1和count2都被默认赋予了默认值(0,0);首先初始化singleton类变量,count1和count2变为(1,1);随后初始化count2,count1类变量,由于count2没有初始值,count2依然为1,而count1有初始值2,会覆盖原来的1,最后结构为(2,1)

 

文章有问题请留言,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值