一个Java类中会定义各种变量、方法和代码块。其中方法和代码块从存在的意义上来讲,都是为了操作这些变量的。所以就笔者个人理解,类的主体该是变量,方法和代码块是为变量而存在的。
变量值的改变有两种情形,一是在进行初始化的时候,而是在方法中对其进行操作的时候。变量值改变的先后顺序将直接影响到程序运行的正确与否,所以理正确理解变量值在Java中如何改变对于一个优秀码农来说,显得尤为重要。就以上两种改变变量值的情形来说,第二种情形比较直观,即方法何时调用,便何时改变相应变量值。当然,其中的特殊方法——构造器除外。构造器可以认为是一种特殊的初始化过程,但由于其特殊性,其初始化过程又必须单独考虑。
本文主要以包括“构造器”在内的一些初始化操作对变量值改变的先后顺序为线索,进行了一些代码实验和思考。
由于该主题的情况较复杂,笔者打算先从几个点出发,讨论变量值改变的先后顺序,最后再将这些点中的情况进行一个统一的讨论。
一、先初始化静态(static)变量,再初始化非静态变量。
其中涉及到类的加载机制,因为静态变量是在类加载的过程中便开辟空间,而非静态变量在实例化对象的时候才会开辟空间,具体代码实验如下:
class A
{
int y=printInit("non-static");
static int x= printInit("static");
private static int printInit(String s)
{
System.out.println(s);
return 1;
}
}
class InitSequStatic
{
public static void main(String[] args)
{
A a= new A();
}
}
其运行结果如下:
可以发现,尽管A类中的非静态变量先于静态变量定义,但在初始化的过程中(准确来说是类的加载和对象的实例化过程中),静态变量先于非静态变量。这一点,我们在类继承中将会理解得更加清楚
二、对于有类继承关系的代码中,先初始化(加载)父类静态变量,然后初始化(加载)子类静态变量,继而初始化父类非静态变量,调用父类构造器,最后初始化子类非静态变量,调用子类构造器。
若存在多重继承关系,也同理。之所以此处静态变量“初始化”后面的括号中有“加载”一词,是因为静态变量在类加载的过程中首先会进行默认初始化,但非静态变量在对象实例化的过程中就不会进行默认初始化。此处需要提及:初始化分为默认初始化、显示初始化、构造代码块初始化和构造函数初始化,这将在第三点“初始化过程”中具体提及。
以下图为例:
先依次初始化(加载)A类、B类和C类的静态变量,再依次初始化A类的非静态变量,调用A类的构造器;然后依次初始化B类的非静态变量,调用B类的构造器;最后依次初始化C类的非静态变量,调用C类的构造器。具体代码实验如下:
class A
{
A()
{
System.out.println("A constructor");
}
static int ax=printInit("A static");
int ay=printInit("A nontatic");
private static int printInit(String s)
{
System.out.println(s);
return 1;
}
}
class B extends A
{
B()
{
System.out.println("B constructor");
}
static int bx=printInit("B static");
int by=printInit("B nontatic");
private static int printInit(String s)
{
System.out.println(s);
return 1;
}
}
class C extends B
{
C()
{
System.out.println("C constructor");
}
static int cx=printInit("C static");
int cy=printInit("C nontatic");
private static int printInit(String s)
{
System.out.println(s);
return 1;
}
public static void main(String[] args)
{
C c=new C();
}
}
其运行结果如下: