现在的位置: 首页 > 综合 > 正文

Java类的初始化

2019年08月22日 ⁄ 综合 ⁄ 共 3016字 ⁄ 字号 评论关闭

类的初始化时机
1.创建类的实例
2.访问类或接口的静态变量(static final常量除外,static final变量可以)
3.调用类的静态方法
4.反射(Class.forName(packageName.className))
5.初始化类的子类(子类初始化问题:满足主动调用,即访问子类中的静态变量、方法,否则仅父类初始化)
6.java虚拟机启动时被标明为启动类的类

注:加载顺序:启动类的static block最先加载(父类静态成员、静态代码块—>子类静态成员、静态代码块—>父类实例成员、代码块——>父类构造函数—>子类实例成员、代码块—>子类构造函数)

Java代码

  1. package test01;     
  2. class Singleton {     
  3.     public static Singleton singleton = new Singleton();     
  4.     public static int a;     
  5.     public static int b = 0;     
  6.     
  7.     private Singleton() {     
  8.         super();     
  9.         a++;     
  10.         b++;     
  11.     }     
  12.     public static Singleton GetInstence() {     
  13.         return singleton;     
  14.     }     
  15. }     
  16.     
  17. public class MyTest {     
  18.     public static void main(String[] args) {     
  19.         Singleton mysingleton = Singleton.GetInstence();     
  20.         System.out.println(mysingleton.a);     
  21.         System.out.println(mysingleton.b);     
  22.     }     
  23. }    

从入口开始看

Singleton mysingleton = Singleton.GetInstence();

是根据内部类的静态方法要一个Singleton实例。

这个时候就属于主动调用Singleton类了。

之后内存开始加载Singleton类

1):对Singleton的所有的静态变量分配空间,赋默认的值,所以在这个时候,singleton=null、a=0、b=0。注意b的0是默认值,并不是咱们手工为其赋予的的那个0值。

2):之后对静态变量赋值,这个时候的赋值就是我们在程序里手工初始化的那个值了。此时singleton = new Singleton();调用了构造方法。构造方法里面a=1、b=1。之后接着顺序往下执行。

3):

  1. public static int a;  
  2. public static int b = 0;  

a没有赋值,保持原状a=1。b被赋值了,b原先的1值被覆盖了,b=0。所以结果就是这么来的。类中的静态块static块也是顺序地从上到下执行的。

 

编译时常量、非编译时常量的静态变量

Java代码

  1. package test01;     
  2. class FinalStatic {     
  3.     public static final int A = 4 + 4;   
     
  4.     static {     
  5.         System.out.println("如果执行了,证明类初始化了……");     
  6.     }    
  7. }     
  8.     
  9. public class MyTest03 {     
  10.     public static void main(String[] args) {     
  11.         System.out.println(FinalStatic.A);     
  12.     }     
  13. }    

结果是只打印出了8,证明类并没有初始化。反编译源码发现class里面的内容是

public static final int A = 8;

也就是说编译器很智能的、在编译的时候自己就能算出4+4是8,是一个固定的数字。没有什么未知的因素在里面。

将代码稍微改一下

public static final int A = 4 + new Random().nextInt(10);

这个时候静态块就执行了,证明类初始化了。在静态final变量在编译时不定的情况下。如果客户程序这个时候访问了该类的静态变量,那就会对类进行初始化,所以尽量使用静态final变量,尽量没什么可变因素在里面,否则性能会有所下降。

1. ClassLoader的剖析

ClassLoader的loadClass方法加载一个类不属于主动调用,不会导致类的初始化。如下代码块

Java代码

  1. ClassLoader classLoader = ClassLoader.getSystemClassLoader();     
  2. Class clazz = classLoader.loadClass("test01.ClassDemo");   

并不会让类加载器初始化test01.ClassDemo,因为这不属于主动调用此类

 

 

jvm初始化类时,要求其父类都已经被初始化,但此规则不适用于接口
1.在初始化一个类时,并不会先初始化它所实现的接口
2.在初始化一个接口时,并不会先初始化它的父类接口
因此,一个父接口并不会因为它的子接口或实现类的初始化而初始化。当且仅当程序首次使用接口的静态变量(注意是静态变量,使用静态常量不会初始化)时,才回导致该接口的初始化

public class Test02 {
    
public static void main(String[] args) {
        System.out.println(J.i);
        System.out.println(K.j);
    }
    
static int out(String s, int i) {
        System.out.println(s 
+ "=" + i);
        
return i;
    }
}
interface I {
    
int i = 1;
    
int ii = Test02.out("ii"2);
}
interface J extends I {
    
int j = Test02.out("j"3);
    
int jj = Test02.out("jj"4);
}
interface K extends J {
    
int k = Test02.out("k"5);
}

调用:System.out.println(J.i);
打印:1
解释:J.i 对于J而言没有主动调用,对I 有调用但是i 为static final常量,因此J、I类都没有初始化
调用:System.out.println(K.j);
打印:j=3
          jj=4
          3
解释:K.j 对于K而言没有主动调用,对于J 调用了该接口的j为static final 变量,满足主动调用,因此初始该接口


转自:

http://aixiangct.blog.163.com/blog/static/91522461201131872019995?suggestedreading

抱歉!评论已关闭.