首页 > 代码库 > 【初学者常见问题】Java中关于static的谜团
【初学者常见问题】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 = http://www.mamicode.com/123;> 这个例子,输出时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的不平凡 ,请勿用于任何商业用途)