目录
1、当访问一个静态域时,只有真正声明该静态域的类才会被初始化,例如通过子类引用父类的静态变量,并不会导致子类初始化
3、引用常量不会触发此类的初始化(因为常量在编译的时候就被存入到类的常量池中了)
一、jvm类的加载机制
提到java类的主动运用和被动运用就必须要简单提及一下jvm类的加载机制。
类被加载到jvm虚拟机内存中,到被卸出内存中,可以分为这几个阶段:加载->验证->准备->解析->初始化->使用->卸载
1、加载
通过获取这个类的class文件将这个字节流所代表的的静态存储结构转化为方法区的运行时的数据结构,方法区就是用来存储已被加载的类信息、常亮、静态变量、常量池,编译后类的代码的运行时内存区域。
在堆中生成一个java.lang.class对象用来代表所要处理的类,同时作为访问方法区的入口
2、链接
1、验证:确保验证被加载后的类是否有正确的结构,类数据是否会符合虚拟机的要求。
2、准备 :为类的静态变量(static)在方法区分配内存,并且赋初值例如int对象为0,String为null等(而一般成员变量会在类实例化new时候跟随对象被分配到堆内存中)
3、解析:将类中的符号引用变成直接引用
3、类的初始化
为静态变量赋程序设定的初值
这里就会分为主动引用(必须对类进行初始化)和被动引用(不会对类进行初始化)
4、主动引用
遇到下面五种情况会主动引用:
1、new一个对象
2、调用类的静态成员除了final常量和静态方法
3、反射
4、子类初始化前会先初始化父类
5、包含main方法的类,虚拟机启动时会先初始化该类
代码实现:
1 new一个对象
package jvm_test;
public class Jvm_test {
public static void main(String[] args) {
new A();
//System.out.println(A.width);
}
}
class A
{ public static int width=100;
static
{
System.out.println("静态初始块A");
width=300;
}
public A()
{
System.out.println("创建A类对象");
}
}
实现结果:
静态初始块A
创建A类对象
2、调用类的静态成员和静态方法(除了final常量,JVM会把这个final常量放到类里面的常量池当中,因此并不会主动加载final所修饰的类或者成员变量,就好比C中#define 定义一样,常量在编译 阶段就被放入到常量池中了,使用的时候直接就会去池子中去取)
package jvm_test;
public class Jvm_test {
public static void main(String[] args) {
System.out.println(A.width);
}
}
class A
{ public static int width=100;
public static final int max=10;
static
{
System.out.println("静态初始块A");
width=300;
}
public A()
{
System.out.println("创建A类对象");
}
}
100
3、反射
package jvm_test;
public class Jvm_test extends A {
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("jvm_test.Jvm_test");
}
}
class A
{ public static int width=100;
public static final int max=10;
static
{
System.out.println("静态初始块A");
width=300;
}
public static void test()
{
System.out.println("1111");
}
public A()
{
System.out.println("创建A类对象");
}
}
静态初始块A
4、子类初始化前会先初始化父类
代码同上
5、包含main方法的类,虚拟机启动时会先初始化该类
代码同上
5、被动引用
1、当访问一个静态域时,只有真正声明该静态域的类才会被初始化,例如通过子类引用父类的静态变量,并不会导致子类初始化
package jvm_test;
public class Jvm_test{
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(A.width);
}
}
class A extends A_father
{
public static final int max=10;
static
{
System.out.println("静态初始块A");
}
public static void test()
{
System.out.println("1111");
}
public A()
{
System.out.println("创建A类对象");
}
}
class A_father
{
public static int width=100;
}
100
//这时候A并不会被初始化,因为调用的是A.width,而width是在A_father中定义的,这样只会初始化A_father
2、通过数组定义类引用,不会触发此类的初始化
package jvm_test;
public class Jvm_test{
public static void main(String[] args) throws ClassNotFoundException {
A[] as=new A[10];
}
}
class A extends A_father
{
public static final int max=10;
static
{
System.out.println("静态初始块A");
}
public static void test()
{
System.out.println("1111");
}
public A()
{
System.out.println("创建A类对象");
}
}
class A_father
{
public static int width=100;
}
3、引用常量不会触发此类的初始化(因为常量在编译的时候就被存入到类的常量池中了)
package jvm_test;
public class Jvm_test{
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(A.max);
}
}
class A extends A_father
{
public static final int max=10;
static
{
System.out.println("静态初始块A");
}
public static void test()
{
System.out.println("1111");
}
public A()
{
System.out.println("创建A类对象");
}
}
class A_father
{
public static int width=100;
}
10
-------错误之处请指出,谢谢