写Java代码的时候很少去关注成员变量的声明和初始化顺序,今天借此机会抛出一些问题:语言的设计者们为什么会这样设计?比如说很常见的一个问题:abstract(抽象)类不能用final进行修饰。这个问题比较好理解:因为一个类一旦被修饰成了final,那么意味着这个类是不能被继承的,而abstract(抽象)类又不能被实例化。如果一个抽象类可以是final类型的,那么这个类又不能被继承也不能被实例化,就没有存在的意义。从语言的角度来讲一个类既然是抽象类,那么它就是为了继承,所以给它标识为final是没有意义的。语言的设计者们当然不可能让这么大的一个bug产生。对于开发者而言抽象类不能修饰final可能就是一种约定俗成的规定,并没有特殊意义。我们完全可以往前想一点:为什么这么设计?
下面我所展示的一些代码实例也同样会采用我上面的一些思考方法。有一些是一些”契约“,并没有特别的缘由,可能用别的方法也是合理的。下面的代码会讲到初始化的一些策略,从实际的执行结果中得出一些结论。
代码一
public class Test1 {
{
a = 1;
//System.out.println(a);//这里会抛错。
}
private int a=2;//这里初始化一次,上面的动态块中也对a进行了赋值,这个时候a=?,为什么可以对a进行赋值,而不可以对a进行输出
public static void main(String[] args){
Test1 test1 = new Test1();
System.out.println(test1.a);
}
}
看看上面的代码一,第一个问题就是这段代码能否编译通过。结果是能编译通过。这里说明一个问题就是变量的声明和赋值是两步操作?(这句话我先保留一半,在上面
的代码中有一行代码我注释掉了,这里会抛错,对于这个问题我也没有想明白为什么。)
第一个问题解决了。那下一个问题很显然最后输出的结果是什么?答案是“2”,这里可能会有些诧异。从直观上来讲就是说明在赋值的过程中是完全按照代码的前后顺序进
行。
代码二
public class Test2 {
{
a = 4;
}
private final int a;//这里我并没有对a做初始化。
public static void main(String[] args){
Test2 test2 = new Test2();
System.out.println(test2.a);
}
}
“代码二”只是在“代码一”的基础上对成员变量a多修饰了一个final,另外我并没有立即初始化。第一个问题就是这段代码能不能编译通过,答案是能。在这里展示这段代码是为了后面做铺垫,因为这段代码仍然符合上面的“契约”。
代码三
public class Test3 {
{
a = 4;
}
private static int a;
public static void main(String[] args){
Test3 test3 = new Test3();//注意:这里开始new了一个对象
System.out.println(test3.a);
}
}
代码三在代码一的的基础上对于成员变量a多修饰了一个static。这里同样可以编译通过,最后输出的结果也皆大欢喜为4。这里要注意的是我是new了一个对象,而不是直接访问静态变量。
推荐阅读: