Java编程陷阱-类成员初始化
原文地址:http://blog.csdn.net/veryitman/article/details/6450523
如果你忽略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中的声明和初始化不是原子操作,即他们不是一体化的,也就是说声明后它会有一个默认值,初始化值是可以手动赋值的。
Java编程陷阱-类成员初始化的更多相关文章
- 《 Java 编程思想》CH05 初始化与清理
< Java 编程思想>CH05 初始化与清理 用构造器确保初始化 在 Java 中,通过提供构造器,类的设计者可确保每个对象都会得到初始化.Java 会保证初始化的进行.构造器采用与类相 ...
- VS C++工程类成员初始化检测脚本
最近项目中出现由类成员未初始化而进行读写而造成的问题,于是想将项目中所有的为初始化的地方找出来,优化一下代码,维护了这么多年的程序已有百万余行且VS2015还尚未支持检查类成员初始化的方法.,于是想写 ...
- Java笔记 #04# 类的初始化顺序补充
参考java中的类的初始化顺序详解 package org.sample; class Bread { Bread() { System.out.println("Bread()" ...
- java小心机(5)| 浅谈类成员初始化顺序
类成员什么时候会被初始化呢?一般来说:"类的代码在初次使用时才被加载",加载过程包括了初始化. 比如说new A()调用构造函数时,类中全部成员都会被初始化. 但对于static域 ...
- Java类成员初始化顺序
类中包含7中成员:1.静态变量 static2.final静态常量 final static3.静态代码块 static{} //多个代码块顺序执行 4.普通变量5.普通代码块 {} //多个代码 ...
- Java编程思想学习笔记——初始化与清理(二)
成员初始化 Java尽力保证:所有变量在使用前都能得到适当的初始化. 方法的局部变量:未初始化,编译错误. void f(){ int i; // System.out.println(i);//编译 ...
- C#的类成员初始化顺序
C#的类成员的定义和声明如下 using UnityEngine; using System.Collections; public class TestController : ECControll ...
- C++: 类成员初始化列表语法
类的成员初始化列表的初始化的基本语法,类的构造函数还可以运用此语法为其变量初始化: class Class { private: int a; int b; char ch; public: Cl ...
- Java编程思想学习笔记——初始化与清理
初始化 构造器保证初始化 构造器采用与类相同的名称. 默认构造器(default constructor):不接受任何参数的构造器,也叫无参构造器. 构造器也能带有形式参数,就能在初始化对象时提供实际 ...
随机推荐
- phalcon做日报告提交平台总结
总结:通过开发日报告提交系统,掌握了基本的phalcon框架原理和PHP语言.也了解了一些linux常用指令,收获颇丰. 下面对项目中所遇到的问题进行总结: 1.前台数据传往后台所用的三种方法: (1 ...
- 用VS2010编写的C++程序,在其他电脑上无法运行,提示缺少mfc100.dll的解决办法
问题: 在自己电脑上用VS2010编写的VC++程序(使用MFC库),不能在其他电脑上运行.双击提示: "无法启动此程序,因为计算机中丢失mfc100.dll 尝试重新安装该程序以解决此问题 ...
- java中的信号量Semaphore
Semaphore(信号量)充当了操作系统概念下的“信号量”.它提供了“临界区中可用资源信号量”的相同功能.以一个停车场运作为例.为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的.这时如果 ...
- 用F340 GPIO做I2C
在和Qinheng开发小尺寸点灯治具中,F340和FPGA采用I2C通信,其中F340作为I2C的主机,I2C端口用自己的GPIO编写,总结遇到的问题及注意事项: 1. F340端口及上拉电阻设置: ...
- 【CF】304 E. Soldier and Traveling
基础网络流,增加s和t,同时对于每个结点分裂为流入结点和流出结点.EK求最大流,判断最大流是否等于当前总人数. /* 304E */ #include <iostream> #includ ...
- 使用bacula实现Linux的远程备份和还原
Bacula,被誉为开源软件中最好的备份还原软件,它提供了企业级的客户机/服务器的备份解决方案,能够通过网络来管理文件的备份,恢复和核实工作.Bacula,既有windows版本的,也有Linux,U ...
- ECHO.js 纯javascript轻量级延迟加载
演示 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf ...
- Dynamic系列--Dynamic 与反序列化
通常在调用其他站点的api时,如果返回的结果为 json数据,而我们又不想再重新定义实体类时,可以使用dynamic类型. 但是有以下需要注意的地方. 当内容为空时,反序列化结果为null 当内容格式 ...
- Linux学习笔记8——VIM编辑器的使用
在ubuntu中,敲入命令行:sudo apt-get install vim,然后输入系统密码,确认Y,即可下载vim 按下vim,在后面跟上文件的路径,即可进入文件到编辑模式,如果不存在该文件,将 ...
- JavaScript高级程序设计15.pdf
组合继承的问题是会调用2次超类型构造函数 寄生组合式继承 即通过借用构造函数来继承属性,通过原型链的形式来继承方法,思路:不必为了指定子类型的原型而调用超类型的原型,我们所需要的无非是超类型原型的一个 ...