只知道static修饰的变量可以作为全局变量用,在项目里为了用着方便,将static修饰的变量加了一个volatile关键字修饰,这样多线程的时候,都可以保证自己拿到的值是主存里的最新值。先说static吧:
直接上代码
package com.demo.javase.staticdemo;
/**
* @ClassName: OuterClass.java
* @Description: 块、静态块、构造器等执行顺序
* @author KeepGoingPawn
* @Date 2018年8月23日 上午10:37:18
*/
public class OuterClass
{
/*静态代码块*/
static{System.out.println("OuterClass static load."); }
/*无参构造器*/
public OuterClass(){ System.out.println("flag"); }
/*有参构造器*/
public OuterClass(String flag){ System.out.println("flag:"+flag); }
/*内部类*/
class InnerClass{private OuterClass out = new OuterClass("inner");}
/*静态内部类*/
static class InnerStaticClass
{
/*内部类的静态代码块*/
static{System.out.println("InnerStaticClass static load.");}
/*外围类的静态变量*/
private static OuterClass out = new OuterClass("innerStatic");
/*内部类的静态方法*/
private static void load(){System.out.println("InnerStaticClass func load().");}
}
/*外围静态方法*/
public static OuterClass getInstatnce(){return OuterClass.InnerStaticClass.out;}
/*main函数*/
public static void main(String[] args)
{
OuterClass.InnerStaticClass.load();
OuterClass out = OuterClass.InnerStaticClass.out;
OuterClass.InnerClass innerClass = out.new InnerClass() ;
}
}
大家可以先猜一下,究竟会输出什么东东?
直接给答案吧,让我们循着答案去一步步解释。
OuterClass static load.
InnerStaticClass static load.
flag:innerStatic
InnerStaticClass func load().
flag:inner
上一张代码图:
由结果追溯过程:
外围类的静态块在类加载时首先执行->故出现第一行:OuterClass static load.
然后是加载静态内部类:先加载静态块,出现第二行:InnerStaticClass static load.
然后初始化静态内部类的成员变量innerStatic,调用外围类构造器,出现第三行flag:innerStatic
然后调用静态内部类的静态方法load(),出现第四行InnerStaticClass func load().
最后调用内部类的构造器生成一个内部类的对象,出现第五行flag:inner。
感觉真心有点乱,按照debug的顺序先记住。可以参考周志明大大的《深入理解JVM》第七章虚拟机类加载机制,来看这些东西,应该还好些。
PS:由static引发的一些列东西:
【1】单例模式实现:因为类只会加载一次,所以在用单例的时候,可以考虑使用静态内部类实现。
【2】静态代码块,也是在类加载的时候会发生,之前记得用java做游戏的时候,一些资源可以放到块语句中,达到预加载目的。
【3】父子类加载顺序:静态代码块、代码块、构造器、方法。加载顺序依次为:
1. 这是父类的静态代码块!
2. 这是子类的静态代码块!
3. 这是父类的局部代码块!
4. 这是父类的构造方法!
5. 这是子类的局部代码块!
6. 这是子类的构造方法!
7. 这是子类的普通方法!
插入代码及图片:
public class StaticDemo {
public static void main(String[] args) {
Student s = new Student();
s.sayHello();}
}
class Person {
static{System.out.println("这是父类的静态代码块!");}
{System.out.println("这是父类的局部代码块!");}
Person(){System.out.println("这是父类的构造方法!");}
}
class Student extends Person{
static{System.out.println("这是子类的静态代码块!");}
{System.out.println("这是子类的局部代码块!");}
Student(){System.out.println("这是子类的构造方法!"); }
public void sayHello(){System.out.println("这是子类的普通方法!");}
}
Debug时:发现如下执行顺序:
1:执行父类静态代码块
2:执行子类静态代码块
3:调用子类构造器去初始化子类,调用过程中先去调用父类构造器,调用父类构造器过程中,发现父类有代码块,所以先执行父类代码块,然后执行父类构造器
4:执行子类构造器时,发现有代码块,先去执行子类代码块,然后执行子类构造器
5:调用子类普通方法。
个人一些想法:
1.之前对父子类构造器 代码块 静态代码块等就是迷迷糊糊,通过这几个例子有了稍微清晰点的认知。
2.平时写代码注意不到这块,只有当参与一些设计时才会想到这些东东。
3.继续查漏补缺。