Java编程陷阱-类成员初始化

如果你忽略Java的细节,恐怕你的代码会充满bug,下面讨论关于类成员初始化问题。

第一类,初始化成员变量在构造方法之前

主要参考TIJ中的代码,来说明问题!!

1. 新建一个类Tag

package mark.initial; public class Tag { /** * 构造方法 * * @param maker */ public Tag(int maker) { System.out.println("tag(" + maker + ")"); } }

2. 新建一个类Card

package mark.initial; public class Card { Tag tag1 = new Tag(1); /** * 构造方法 */ public Card() { System.out.println("card()"); Tag tag3 = new Tag(33); } Tag tag2 = new Tag(2); /** * 成员方法 */ public void f() { System.out.println("f()"); } Tag tag3 = new Tag(3); }

3. 新建测试类

package mark.initial; public class TestInitial { public static void main(String[] args) { Card c = new Card(); c.f(); } }

看结果之前,简单分析一下。在Card中,到处都有Tag对象,看起来比较乱。其实这是故意的,我们知道类的成员变量会被初始化为默认值比如引用初始化为null,int默认为0,float默认为0.0等,如果你没有指定这些成员变量的值时。

在测试类TestInitial中,new一个Card,这样就会初始化它的成员变量tag1、tag2、tag3先为null,(由于我们手动将这些成员变量赋予新值即new该对象),然后会指向堆里面的对象。最后Card调用自己的构造函数,所以结果如下所示:

tag(1) tag(2) tag(3) card() tag(33) f()

第二类,成员变量初始化在构造方法之后

子类成员变量初始化,父类Linux,子类User重写父类的print()方法,并在该方法中改变成员变量name值。

package my.test; public class Linux { int size = 10; String name = "Linux"; /** * 构造方法 */ public Linux() { System.out.println("I'm Linux OS!"); print(); } public void print() { System.out.println("父类Linux--print()"); } } class User extends Linux { String name = "ubuntu"; @Override public void print() { name = "ubuntu10.10"; System.out.println("子类User--print()"); } }

测试代码:

class TestLinux { public static void main(String[] args) { User user = new User(); System.out.println(user.name); } }

在main方法中创建User实例对象user,User会调用自己的构造方法,然而我们知道子类在其构造方法中会先调用父类的构造方法,这样一来User的父类Linux会执行自己的构造方法,从而调用print()方法,由于子类User覆写父类方法,所以调用的是子类的print()方法。将name的值变为ubuntu10.10,构造方法执行完毕,开始初始化User的成员变量即name为ubuntu,那么打印结果就是如下所示:

I'm Linux OS! 子类User--print() ubuntu

第三类,其实这是第二类的另一个实例

下面是单例模式:

package my.test; public class ClassloadTest { // 声明类成员变量并创建该对象 static final ClassloadTest test = new ClassloadTest(); public static int a = 5; public static int b = 8; /** * 私有构造方法 */ private ClassloadTest() { a++; b++; } /** * 获取类的实例对象 * * @return ClassloadTest实例对象 */ public static ClassloadTest getInstance() { return test; } }

好了,看懂上述代码之后,看看测试代码:

class Test { public static void main(String[] args) { System.out.println("a = " + ClassloadTest.getInstance().a); System.out.println("b = " + ClassloadTest.getInstance().b); } }

在公布答案之前,大部分人都会很自信的说结果是下面的样子:

a = 6 b = 9

呵呵,你太冲动啦!!!回答错误,好好想一想吧!!

下面这句代码是创建ClassloadTest对象test:

// 声明类成员变量并创建该对象 static final ClassloadTest test = new ClassloadTest();

在测试类中,获得类的实例即创建对象,就会调用构造方法,其实发生的时间是这样子的:

<1> 初始化test为null,a=0,b=0,这里的值是默认赋值,不是你手动所赋的值5和8

<2> test =new ClassloadTest(),会调用构造方法,从而使a=1,b=1

<3> 顺序执行代码,静态变量a=5,b=8,那么原来的a=1,b=1就被覆盖掉

ok,执行结果应该是:

a = 5 b = 8

如果,改变代码中static final ClassloadTest test = new ClassloadTest();的位置结果会不一样的。现在改变ClassloadTest类如下:

package my.test; public class ClassloadTest { public static int a = 5; public static int b = 8; // 声明类成员变量并创建该对象 static final ClassloadTest test = new ClassloadTest(); /** * 私有构造方法 */ private ClassloadTest() { a++; b++; } /** * 获取类的实例对象 * * @return ClassloadTest实例对象 */ public static ClassloadTest getInstance() { return test; } }

还是上使用上面的测试方法,结果如下:

a = 6 b = 9

分析如下:

<1>初始化test为null,a=0,b=0,这里的值是默认赋值,不是你手动所赋的值5和8

<2> a=5,b=8

<3> test =new ClassloadTest(),会调用构造方法,从而使a=6,b=9


小结:

说到这里,我们至少明白下面的道理(针对类成员变量):

<1> 面向对象编程,也需要考虑声明变量的顺序

<2>Java中的声明和初始化不是原子操作,即他们不是一体化的,也就是说声明后它会有一个默认值,初始化值是可以手动赋值的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值