静态加载顺序

一.关键字static


1.static可以修饰成员变量、成员方法,不能修饰构造函数,构造函数是在创建对象时使用的。而static是类的属性。

2.当一个函数没有访问实例变量数据时,才能被static修饰。因为静态不能访问非静态的,在静态加载时,有可能还没有创建对象,成员变量和成员方法都是和对象有关的。


二.静态函数注意事项


  1.被static修饰的函数称为类函数,非静态函数也称为实例函数
2.static可以修饰成员变量、成员方法,但是不能修饰构造函数

3.静态函数是在类加载的时候就在内存中加载完成,可以直接运行的函数。(只要是函数最终都要进入栈内存,方法是什么时候调用,什么时候执行,不调用不执行。)

4.非静态函数在类加载完成之后,通过new在堆中开辟内存空间,然后通过对象调用函数。

5.静态不能访问非静态

因为静态函数在类加载完成可以直接通过类名调用,而这时有可能还没有创建对象,非静态函数是依赖对象的。

6.非静态可以访问静态

因为当非静态函数可以运行,那么说明类中一定创建了对象,说明对象所在的类已经加载完成,那么非静态就可以访问静态。


三.类和对象的加载过程


1.类加载过程:

① JVM启动,加载所需要的class文件
② JVM加载class文件时,会把所有的静态内容(静态成员变量、静态方法、静态代码块)都先加载到方法区中的静态区中。
③ 静态加载完成之后,JVM开始给所有的成员变量默认初始化,静态成员变量开辟空间。
④ 当给类中的所有静态成员变量默认初始化完成,开始按照代码的顺序依次执行(遇到静态代码块就执行,遇到静态成员变量就显示初始化)
⑤ 静态都执行完毕,类才彻底加载完成


2.对象的加载过程:


① 当类加载完成,使用new关键字创建对象,在堆给对象分配内存空间
② 给对象所属的类的非静态成员变量分配空间并进行默认初始化
③ 在JVM自动调取构造函数时先执行隐式三步
  • super()区访问父类构造,对父类进行初始化
  • 给非静态成员变量进行显示赋值
  • 执行构造代码块
④ 在执行构造函数中的其它代码
⑤ 构造函数执行完毕,对象创建完成。


四.静态内存图解


StaticDemo类

[java] view plain copy
  1. package cn.jason03;  
  2. /** 
  3.  * 这是static的用法 
  4.  * @author Jason 
  5.  * 
  6.  */  
  7. class Demo {  
  8.     int x;  
  9.     static int y = 3;  
  10.     // 静态代码块  
  11.     static {  
  12.         System.out.println("静态代码块");  
  13.     }  
  14.     // 定义构造代码块  
  15.     {  
  16.         System.out.println("我是构造代码块");  
  17.         System.out.println("x=" + x);  
  18.     }  
  19.     //构造函数  
  20.     public Demo() {  
  21.     }  
  22.       
  23.     static void print() {  
  24.         System.out.println("y=" + y);  
  25.     }  
  26.   
  27.     void show() {  
  28.         System.out.println("x=" + x + "  y=" + y);  
  29.     }  
  30. }  
  31.   
  32. class StaticDemo {  
  33.     public static void main(String[] args) {  
  34.         //类名调用print方法  
  35.         Demo.print();  
  36.         //创建对象  
  37.         Demo d = new Demo();  
  38.         //给成员变量x赋值  
  39.         d.x = 10;  
  40.         //用对象调用show方法  
  41.         d.show();  
  42.     }  
  43. }  
静态加载顺序

StaticCode类

[java] view plain copy
  1. package cn.jason03;  
  2.   
  3. /** 
  4.  * 静态属性执行顺序1 
  5.  *  
  6.  * @author Jason 
  7.  */  
  8. class StaticCode {  
  9.     static int x = 10;  
  10.     static int y = show();  
  11.   
  12.     static int show() {  
  13.         System.out.println("show..........x = " + x);  
  14.         System.out.println("show..........y = " + y);  
  15.         return 100;  
  16.     }  
  17.   
  18.     // 静态代码块  
  19.     static {  
  20.         System.out.println("静态代码块运行....y= " + y);  
  21.     }  
  22.   
  23.     void print() {  
  24.         System.out.println("....................");  
  25.     }  
  26. }  
  27.   
  28. public class StaticCodeDemo {  
  29.     public static void main(String[] args) {  
  30.         new StaticCode().print();  
  31.     }  
  32. }  

内存图解:

静态加载顺序


五.颠覆思维的小题目

第一道题:

[java] view plain copy
  1. package cn.jason07;  
  2.   
  3. public class StaticInitTest {  
  4.     /*static { 
  5.         value = 10; 
  6.         print("静态代码块"); 
  7.     }*/  
  8.     static int value = getValue();  
  9.     static { // 通过静态初始化块为name变量初始化  
  10.         System.out.println("静态代码块中value的值=" + value);  
  11.         name = "周杰伦";  
  12.     }  
  13.   
  14.     static {  
  15.         value = 10;  
  16.         print("静态代码块");  
  17.           
  18.     }  
  19.   
  20.     static String name = "林青霞"// 定义静态变量  
  21.   
  22.     public static void print(String s) {  
  23.         System.out.println("value的值=" + value + " " + "名字是:"+name);  
  24.     }  
  25.   
  26.     public static int getValue() {  
  27.         return ++value;  
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.   
  32.         System.out.println("value的值:" + StaticInitTest.value);  
  33.         System.out.println("name的值:" + StaticInitTest.name);  
  34.   
  35.     }  
  36. }  
输出结果:

静态代码块中value的值=1
value的值=10 名字是:周杰伦
value的值:10
name的值:林青霞


[java] view plain copy
  1. package cn.jason07;  
  2.   
  3. public class StaticInitTest {  
  4.     static {  
  5.         value = 10;  
  6.         print("静态代码块");  
  7.     }  
  8.     static int value = getValue();  
  9.     static { // 通过静态初始化块为name变量初始化  
  10.         System.out.println("静态代码块1中value的值=" + value);  
  11.         name = "周杰伦";  
  12.     }  
  13.   
  14.     /*static { 
  15.         value = 10; 
  16.         print("静态代码块2"); 
  17.          
  18.     }*/  
  19.   
  20.     static String name = "林青霞"// 定义静态变量  
  21.   
  22.     public static void print(String s) {  
  23.         System.out.println("value的值=" + value + " " + "名字是:"+name);  
  24.     }  
  25.   
  26.     public static int getValue() {  
  27.         return ++value;  
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.   
  32.         System.out.println("value的值:" + StaticInitTest.value);  
  33.         System.out.println("name的值:" + StaticInitTest.name);  
  34.   
  35.     }  
  36. }  
输出结果是:

value的值=10 名字是:null
静态代码块1中value的值=11
value的值:11
name的值:林青霞


注意:

  • 静态函数是在类加载的时候就在内存中加载完成,可以直接运行的函数。静态属性优先于对象存在的,静态属性是类所共享的,静态成员变量赋值可以在定义静态变量之前,但是输出不能在定义静态变量之前。(只要是函数最终都要进入栈内存,方法是什么时候调用,什么时候执行,不调用不执行。)
  • 对于对象而言,栈内存的引用地址不是对象,仅仅是为堆内存中对象分配内存空间时随机分配的地址值而已,真正的对象在堆内存。指向只是方便使用成员属性。
  • 凡是对于静态可以不用创建对象,直接可以用类名调用。凡是对于非静态,如要访问非静态成员方法和成员属性,那么需要想方设法创建对象来访问。(为什么说想方设法呢?比如非静态的内部类,如果非静态内部类被private修饰,那么只能在外部类里创建对象来访问被private修饰的内部类属性和行为,在外部类之外是不能访问的。)

第二道题:

[java] view plain copy
  1. package cn.jason01;  
  2.   
  3. /** 
  4.  * 静态加载顺序 
  5.  *  
  6.  * @author Jason 
  7.  * 
  8.  */  
  9. public class StaticTest {  
  10.     public static void main(String[] args) {  
  11.         staticFunction();  
  12.         show();  
  13.     }  
  14.   
  15.     static StaticTest st = new StaticTest();  
  16.   
  17.     static {  
  18.         System.out.println("1");  
  19.     }  
  20.   
  21.     {  
  22.         System.out.println("2");  
  23.     }  
  24.   
  25.     public StaticTest() {  
  26.         System.out.println("3");  
  27.         System.out.println("构造函数中的.....a=" + a + " b=" + b);  
  28.   
  29.     }  
  30.   
  31.     public static void staticFunction() {  
  32.         System.out.println("4");  
  33. //      System.out.println("b=="+b);  
  34.     }  
  35.   
  36.     int a = 110;  
  37.   
  38.     static int b = 112;  
  39.   
  40.     public static void show() {  
  41.         System.out.println("show..........b=" + b);  
  42.     }  
  43. }  

输出结果:

2
3
构造函数中的.....a=110 b=0
1
4
show..........b=112


第二道题解析:
①运行时,JVM先加载main函数所在的类,也就是StaticTest类。加载就是把StaticTest类的字节码全部放在方法区,与此同时只有静态成员变量先默认初始化了,其他都没有执行只是放在里面。然后按照代码顺序进行执行。所以第一步先执行static StaticTest st = new StaticTest()。

②在堆内存new StaticTest()开辟一个空间,随机分配十六进制地址值,非静态成员变量这个空间在开辟一个小空间,默认初始化值,int a=0。

③现在JVM自动调用构造函数,所以public StaticTest() {}进栈内存,先执行隐式三步。隐式三步第二步给非静态成员显示初始化,这是int a=110;隐式三步第三步就是执行构造代码块(反编译之后一目了然),所以先输出2。隐式三步执行完,现在执行构造代码块中的其它代码,所以输出3,构造函数中的.........a=110,b=0

④按照代码顺序向下执行,那么开始执行静态代码块,输出1

⑤给静态变量b显示初始化,这时b=112,然后才调用方法,一定是先给静态显示初始化完毕才调用方法的(对于本题是这样的),输出4.如果把staticFunction函数中的注释放开,如果b=112,那么说明静态变量显示赋值在调用方法之前完成。放开注释结果正是b=112,说明验证是正确的。

⑥在调用show方法,这时也能验证执行时按照代码的顺序执行的。输出show..........b=112


总结:

1.加载时只对静态成员变量默认初始化值,其它内容都只加载不执行。

2.执行是按照代码顺序执行的。

一.关键字static


1.static可以修饰成员变量、成员方法,不能修饰构造函数,构造函数是在创建对象时使用的。而static是类的属性。

2.当一个函数没有访问实例变量数据时,才能被static修饰。因为静态不能访问非静态的,在静态加载时,有可能还没有创建对象,成员变量和成员方法都是和对象有关的。


二.静态函数注意事项


  1.被static修饰的函数称为类函数,非静态函数也称为实例函数
2.static可以修饰成员变量、成员方法,但是不能修饰构造函数

3.静态函数是在类加载的时候就在内存中加载完成,可以直接运行的函数。(只要是函数最终都要进入栈内存,方法是什么时候调用,什么时候执行,不调用不执行。)

4.非静态函数在类加载完成之后,通过new在堆中开辟内存空间,然后通过对象调用函数。

5.静态不能访问非静态

因为静态函数在类加载完成可以直接通过类名调用,而这时有可能还没有创建对象,非静态函数是依赖对象的。

6.非静态可以访问静态

因为当非静态函数可以运行,那么说明类中一定创建了对象,说明对象所在的类已经加载完成,那么非静态就可以访问静态。


三.类和对象的加载过程


1.类加载过程:

① JVM启动,加载所需要的class文件
② JVM加载class文件时,会把所有的静态内容(静态成员变量、静态方法、静态代码块)都先加载到方法区中的静态区中。
③ 静态加载完成之后,JVM开始给所有的成员变量默认初始化,静态成员变量开辟空间。
④ 当给类中的所有静态成员变量默认初始化完成,开始按照代码的顺序依次执行(遇到静态代码块就执行,遇到静态成员变量就显示初始化)
⑤ 静态都执行完毕,类才彻底加载完成


2.对象的加载过程:


① 当类加载完成,使用new关键字创建对象,在堆给对象分配内存空间
② 给对象所属的类的非静态成员变量分配空间并进行默认初始化
③ 在JVM自动调取构造函数时先执行隐式三步
  • super()区访问父类构造,对父类进行初始化
  • 给非静态成员变量进行显示赋值
  • 执行构造代码块
④ 在执行构造函数中的其它代码
⑤ 构造函数执行完毕,对象创建完成。


四.静态内存图解


StaticDemo类

[java] view plain copy
  1. package cn.jason03;  
  2. /** 
  3.  * 这是static的用法 
  4.  * @author Jason 
  5.  * 
  6.  */  
  7. class Demo {  
  8.     int x;  
  9.     static int y = 3;  
  10.     // 静态代码块  
  11.     static {  
  12.         System.out.println("静态代码块");  
  13.     }  
  14.     // 定义构造代码块  
  15.     {  
  16.         System.out.println("我是构造代码块");  
  17.         System.out.println("x=" + x);  
  18.     }  
  19.     //构造函数  
  20.     public Demo() {  
  21.     }  
  22.       
  23.     static void print() {  
  24.         System.out.println("y=" + y);  
  25.     }  
  26.   
  27.     void show() {  
  28.         System.out.println("x=" + x + "  y=" + y);  
  29.     }  
  30. }  
  31.   
  32. class StaticDemo {  
  33.     public static void main(String[] args) {  
  34.         //类名调用print方法  
  35.         Demo.print();  
  36.         //创建对象  
  37.         Demo d = new Demo();  
  38.         //给成员变量x赋值  
  39.         d.x = 10;  
  40.         //用对象调用show方法  
  41.         d.show();  
  42.     }  
  43. }  
静态加载顺序

StaticCode类

[java] view plain copy
  1. package cn.jason03;  
  2.   
  3. /** 
  4.  * 静态属性执行顺序1 
  5.  *  
  6.  * @author Jason 
  7.  */  
  8. class StaticCode {  
  9.     static int x = 10;  
  10.     static int y = show();  
  11.   
  12.     static int show() {  
  13.         System.out.println("show..........x = " + x);  
  14.         System.out.println("show..........y = " + y);  
  15.         return 100;  
  16.     }  
  17.   
  18.     // 静态代码块  
  19.     static {  
  20.         System.out.println("静态代码块运行....y= " + y);  
  21.     }  
  22.   
  23.     void print() {  
  24.         System.out.println("....................");  
  25.     }  
  26. }  
  27.   
  28. public class StaticCodeDemo {  
  29.     public static void main(String[] args) {  
  30.         new StaticCode().print();  
  31.     }  
  32. }  

内存图解:

静态加载顺序


五.颠覆思维的小题目

第一道题:

[java] view plain copy
  1. package cn.jason07;  
  2.   
  3. public class StaticInitTest {  
  4.     /*static { 
  5.         value = 10; 
  6.         print("静态代码块"); 
  7.     }*/  
  8.     static int value = getValue();  
  9.     static { // 通过静态初始化块为name变量初始化  
  10.         System.out.println("静态代码块中value的值=" + value);  
  11.         name = "周杰伦";  
  12.     }  
  13.   
  14.     static {  
  15.         value = 10;  
  16.         print("静态代码块");  
  17.           
  18.     }  
  19.   
  20.     static String name = "林青霞"// 定义静态变量  
  21.   
  22.     public static void print(String s) {  
  23.         System.out.println("value的值=" + value + " " + "名字是:"+name);  
  24.     }  
  25.   
  26.     public static int getValue() {  
  27.         return ++value;  
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.   
  32.         System.out.println("value的值:" + StaticInitTest.value);  
  33.         System.out.println("name的值:" + StaticInitTest.name);  
  34.   
  35.     }  
  36. }  
输出结果:

静态代码块中value的值=1
value的值=10 名字是:周杰伦
value的值:10
name的值:林青霞


[java] view plain copy
  1. package cn.jason07;  
  2.   
  3. public class StaticInitTest {  
  4.     static {  
  5.         value = 10;  
  6.         print("静态代码块");  
  7.     }  
  8.     static int value = getValue();  
  9.     static { // 通过静态初始化块为name变量初始化  
  10.         System.out.println("静态代码块1中value的值=" + value);  
  11.         name = "周杰伦";  
  12.     }  
  13.   
  14.     /*static { 
  15.         value = 10; 
  16.         print("静态代码块2"); 
  17.          
  18.     }*/  
  19.   
  20.     static String name = "林青霞"// 定义静态变量  
  21.   
  22.     public static void print(String s) {  
  23.         System.out.println("value的值=" + value + " " + "名字是:"+name);  
  24.     }  
  25.   
  26.     public static int getValue() {  
  27.         return ++value;  
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.   
  32.         System.out.println("value的值:" + StaticInitTest.value);  
  33.         System.out.println("name的值:" + StaticInitTest.name);  
  34.   
  35.     }  
  36. }  
输出结果是:

value的值=10 名字是:null
静态代码块1中value的值=11
value的值:11
name的值:林青霞


注意:

  • 静态函数是在类加载的时候就在内存中加载完成,可以直接运行的函数。静态属性优先于对象存在的,静态属性是类所共享的,静态成员变量赋值可以在定义静态变量之前,但是输出不能在定义静态变量之前。(只要是函数最终都要进入栈内存,方法是什么时候调用,什么时候执行,不调用不执行。)
  • 对于对象而言,栈内存的引用地址不是对象,仅仅是为堆内存中对象分配内存空间时随机分配的地址值而已,真正的对象在堆内存。指向只是方便使用成员属性。
  • 凡是对于静态可以不用创建对象,直接可以用类名调用。凡是对于非静态,如要访问非静态成员方法和成员属性,那么需要想方设法创建对象来访问。(为什么说想方设法呢?比如非静态的内部类,如果非静态内部类被private修饰,那么只能在外部类里创建对象来访问被private修饰的内部类属性和行为,在外部类之外是不能访问的。)

第二道题:

[java] view plain copy
  1. package cn.jason01;  
  2.   
  3. /** 
  4.  * 静态加载顺序 
  5.  *  
  6.  * @author Jason 
  7.  * 
  8.  */  
  9. public class StaticTest {  
  10.     public static void main(String[] args) {  
  11.         staticFunction();  
  12.         show();  
  13.     }  
  14.   
  15.     static StaticTest st = new StaticTest();  
  16.   
  17.     static {  
  18.         System.out.println("1");  
  19.     }  
  20.   
  21.     {  
  22.         System.out.println("2");  
  23.     }  
  24.   
  25.     public StaticTest() {  
  26.         System.out.println("3");  
  27.         System.out.println("构造函数中的.....a=" + a + " b=" + b);  
  28.   
  29.     }  
  30.   
  31.     public static void staticFunction() {  
  32.         System.out.println("4");  
  33. //      System.out.println("b=="+b);  
  34.     }  
  35.   
  36.     int a = 110;  
  37.   
  38.     static int b = 112;  
  39.   
  40.     public static void show() {  
  41.         System.out.println("show..........b=" + b);  
  42.     }  
  43. }  

输出结果:

2
3
构造函数中的.....a=110 b=0
1
4
show..........b=112


第二道题解析:
①运行时,JVM先加载main函数所在的类,也就是StaticTest类。加载就是把StaticTest类的字节码全部放在方法区,与此同时只有静态成员变量先默认初始化了,其他都没有执行只是放在里面。然后按照代码顺序进行执行。所以第一步先执行static StaticTest st = new StaticTest()。

②在堆内存new StaticTest()开辟一个空间,随机分配十六进制地址值,非静态成员变量这个空间在开辟一个小空间,默认初始化值,int a=0。

③现在JVM自动调用构造函数,所以public StaticTest() {}进栈内存,先执行隐式三步。隐式三步第二步给非静态成员显示初始化,这是int a=110;隐式三步第三步就是执行构造代码块(反编译之后一目了然),所以先输出2。隐式三步执行完,现在执行构造代码块中的其它代码,所以输出3,构造函数中的.........a=110,b=0

④按照代码顺序向下执行,那么开始执行静态代码块,输出1

⑤给静态变量b显示初始化,这时b=112,然后才调用方法,一定是先给静态显示初始化完毕才调用方法的(对于本题是这样的),输出4.如果把staticFunction函数中的注释放开,如果b=112,那么说明静态变量显示赋值在调用方法之前完成。放开注释结果正是b=112,说明验证是正确的。

⑥在调用show方法,这时也能验证执行时按照代码的顺序执行的。输出show..........b=112


总结:

1.加载时只对静态成员变量默认初始化值,其它内容都只加载不执行。

2.执行是按照代码顺序执行的。