【初学者常见问题】Java中关于static的谜团

         static是Java中的一个关键字,它能够声明在方法中,如public static void test(),这就是一个静态方法,他可以通过类名.方法名来调用,当然也可以给变量声明,如public static int a = 10; 它就是一个静态变量,也可以通过上述类名.方法名来调用。

         以前写过一篇《反思——基础很重要》的文章,基础的扎实往往在后面的道路中会如鱼得水。那次只给出题目,是一道在笔试过程中经常出现的题目,这几天读《深入理解java虚拟机》这本书又有了对这段代码深入的体会。先附上这段代码。

public class Hello{  
    public static void main(String args[]){  
        A ab = new B();  
        ab = new B();  
    }  
}  
class A{  
    static{  
        System.out.println("1");  
    }  
    public A(){  
        System.out.println("2");  
    }  
}  
class B extends A{  
    static{  
        System.out.println("a");  
    }  
    public B(){  
        System.out.println("b");  
    }  
}  

         看到这段代码,肯定会想到类初始化这个阶段,类初始化时类加载过程中的最后一步————加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(initialization)。静态块在类初始化过程中只构造一次而且是先于构造方法,所以它的结果只有一个1,a,所以无论你new多少个对象,静态快的输出只是一次1,a。

 下面的几个条件是虚拟机规范有5种情况下必须对类进行“初始化”:

          1.遇到new,getstatic,putstatic或invokestatic这4条字节码指令时,如果类没有进行初始化,则需要初始化,场景主要用在

                                a.使用new关键字实例化对象

                                b.读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)

                                c.调用另一个类的静态方法

          2.使用java.lang.reflect包的方法进行反射调用的时候

          3.当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化

          4.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机先会初始化这个主类。

         上面的代码例子体现了1(a场景),3条件

         再看下面几个代码例子:

例1.1

public class SuperClass {
	static{
		System.out.println("SuperClass init!");
	}
	
	public static int value = 123;
}

public class SubClass extends SuperClass{
	static{
		System.out.println("SubClass init");
	}
}

public class NotInitialization {
	
	public static void main(String[] args) {
		System.out.println(SubClass.value);
	}
}
   这个例子,输出时SuperClass init ! 和123,对于静态字段,只有直接定义该字段的类才会被初始化。因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化和子类的初始化。

例子1.2

public class ConstClass {
	static{
		System.out.println("ConstClass init!");
	}
	
	public static final String HELLOWORLD = "Hello World";
}

public class NotInitialization {
	
	public static void main(String[] args) {
		System.out.println(ConstClass.HELLOWORLD);
	}
}

            这个例子这体现了1条件下的b场景,常量HelloWord在编译阶段通过常量传播优化,已经将常量值“Hello World"存储到了NotInitialization的常量池中,所以两个类ConstClass和NotInitialization没有任何联系了,以后ConstClass.HELLOWORLD实际上直接是NotInitialization从常量池中取值了~~~

            上面我们讲到了类在初始化时,要求其父类全部已经初始化了(例子1.1),但是有一句话:一个接口在初始化时,和类初始化有一点不同,它并不要求其父接口全部完成了初始化,只是在真正使用到父接口的时候(如引用接口中定义的变量)才会初始化。

                         (转载本站文章请注明作者和出处 Coder的不平凡 ,请勿用于任何商业用途)


【初学者常见问题】Java中关于static的谜团,布布扣,bubuko.com

【初学者常见问题】Java中关于static的谜团

上一篇:《你必须知道的495个C语言问题》笔记--数组和指针


下一篇:Python写的一个优美的定时器,定时精度可调