原文:http://ini.iteye.com/blog/2007835

面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,

构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的
结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。 
我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是 
(静态变量、静态初始化块)>(变量、初始化块)>构造器。 
我们也可以通过下面的测试代码来验证这一点:  
Java代码

public class InitialOrderTest {
// 静态变量
public static String staticField = "静态变量";
// 变量
public String field = "变量";
// 静态初始化块
static {
System.out.println(staticField);
System.out.println("静态初始化块");
}
// 初始化块
{
System.out.println(field);
System.out.println("初始化块");
}
// 构造器
public InitialOrderTest() {
System.out.println("构造器");
}
public static void main(String[] args) {
new InitialOrderTest();
}
}

运行以上代码,我们会得到如下的输出结果: 
静态变量  
静态初始化块  
变量  
初始化块  
构造器 
这与上文中说的完全符合。 
那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果: 
Java代码

class Parent {
// 静态变量
public static String p_StaticField = "父类--静态变量";
// 变量
public String p_Field = "父类--变量";
// 静态初始化块
static {
System.out.println(p_StaticField);
System.out.println("父类--静态初始化块");
}
// 初始化块
{
System.out.println(p_Field);
System.out.println("父类--初始化块");
}
// 构造器
public Parent() {
System.out.println("父类--构造器");
}
}
public class SubClass extends Parent {
// 静态变量
public static String s_StaticField = "子类--静态变量";
// 变量
public String s_Field = "子类--变量";
// 静态初始化块
static {
System.out.println(s_StaticField);
System.out.println("子类--静态初始化块");
}
// 初始化块
{
System.out.println(s_Field);
System.out.println("子类--初始化块");
}
// 构造器
public SubClass() {
System.out.println("子类--构造器");
}
// 程序入口
public static void main(String[] args) {
new SubClass();
}
}

运行一下上面的代码,结果马上呈现在我们的眼前:

父类--静态变量  
父类--静态初始化块  
子类--静态变量  
子类--静态初始化块  
父类--变量  
父类--初始化块  
父类--构造器  
子类--变量  
子类--初始化块  
子类--构造器 
现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化, 
实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。 
那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢? 
是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。 
我们以静态变量和静态初始化块为例来进行说明。 同样,我们还是写一个类来进行测试:

Java代码

public class TestOrder {
// 静态变量
public static TestA a = new TestA();
// 静态初始化块
static {
System.out.println("静态初始化块");
}
// 静态变量
public static TestB b = new TestB();
public static void main(String[] args) {
new TestOrder();
}
}
class TestA {
public TestA() {
System.out.println("Test--A");
}
}
class TestB {
public TestB() {
System.out.println("Test--B");
}
}

运行上面的代码,会得到如下的结果:

Test--A  
静态初始化块  
Test--B 
大家可以随意改变变量a、变量b以及静态初始化块的前后位置,就会发现输出结果随着它们在类中出现的前后顺序而改变, 
这就说明静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的。同样,变量和初始化块也遵循这个规律。 
了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。

测试函数:

public class TestStaticCon {
public static int a = 0;
static {
a = 10;
System.out.println("父类的静态代码块在执行a=" + a);
}
{
a = 8;
System.out.println("父类的非静态代码块在执行a=" + a);
}
public TestStaticCon() { this("a在父类带参构造方法中的值:" + TestStaticCon.a); // 调用另外一个构造方法
System.out.println(a);
System.out.println("父类无参构造方法在执行a=" + a);
}
public TestStaticCon(String n) {
System.out.println(n);
System.out.println(a);
}
public static void main(String[] args) {
TestStaticCon tsc = null;
System.out.println("!!!!!!!!!!!!!!!!!!!!!");
tsc = new TestStaticCon();
}
}

运行结果:

父类的非静态代码块在执行a=10 
!!!!!!!!!!!!!!!!!!!!! 
父类的非静态代码块在执行a=8 
a在父类带参构造方法中的值:10 


父类无参构造方法在执行a=8

结论:静态代码块是在类加载时自动执行的,非静态代码块是在创建对象时自动执行的代码,不创建对象不执行该类的非静态代码块。且执行顺序为静态代码块------非静态代码块----构造函数。 
扩展:静态代码块  与  静态方法: 
一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的; 
需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的.  
两者的区别就是:静态代码块是自动执行的;  静态方法是被调用的时候才执行的.

作用:静态代码块可用来初始化一些项目最常用的变量或对象;静态方法可用作不创建对象也可能需要执行的代码

阿里笔试题:

求下面这段代码的输出:

public class Test1 {  

    public static int k = 0;
public static Test1 t1 = new Test1("t1");
public static Test1 t2 = new Test1("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("构造块");
} static{
print("静态块");
} public Test1(String str){
System.out.println((++k)+":"+str+" i="+i+" n="+n);
++i;++n;
} public static int print(String str){
System.out.println((++k)+":"+str+" i="+i+" n="+n);
++n;
return ++i;
} public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 t = new Test1("init");
} }

运行结果:

1:j    i=0    n=0
2:构造块    i=1    n=1
3:t1    i=2    n=2
4:j    i=3    n=3
5:构造块    i=4    n=4
6:t2    i=5    n=5
7:i    i=6    n=6
8:静态块    i=7    n=99
9:j    i=8    n=100
10:构造块    i=9    n=101
11:init    i=10    n=102

java类静态域、块,非静态域、块,构造函数的初始化顺序的更多相关文章

  1. java 子类、父类中静态代码块、字段,非静态代码块、字段以及构造函数的初始化顺序和次数

    一个类中的数据初始化顺序是面试官非常喜欢出的面试题之一,本文用一个实例来介绍java中子类.父类中静态代码块.字段,非静态代码块.字段以及构造函数的执行顺序和次数. 一.包结构

  2. Java基础 静态块、非静态块、构造函数的执行顺序

    Java中经常有一些静态块,这是用来在生成类之前进行的初始化,无论java还C++语言中的static,都是最先初始化好的.结构如下: static { 静态语句代码块 } { 非静态语句代码块 }  ...

  3. java类成员变量与代码块初始化

    首先根据下面的这个一段代码:引入关于java初始化顺序的问题public class InitationTest extends Person { public InitationTest() { S ...

  4. C++成员变量、构造函数的初始化顺序 [转]

    C++成员变量.构造函数的初始化顺序 一.C++成员变量初始化 1.普通的变量:一般不考虑啥效率的情况下 可以在构造函数中进行赋值.考虑一下效率的可以再构造函数的初始化列表中进行 2.static 静 ...

  5. java 类、方法、代码块修饰式关键字总结

    super 关键字 this和super的区别 访问成员的区别 this关键字 this特点 this使用场景 static关键字 例子 访问权限修饰符 特点 总结: 四个修饰符的特点 访问权限修饰符 ...

  6. java类定义、变量类型、构造函数

    1.java类class的定义 所有java程序都以类class为组织单元,java类由属性和方法组成,下面看例子: public  class  Phone{ //属性 String company ...

  7. 类的定义与实例化、构造函数和初始化表(day04)

    十三 类的定义与实例化 类的一般形式 class/struct 类名:继承表{ 访问控制限定符: 类名(形参表):初始化表{}//构造函数 ~类名(void){}//析构函数 返回类型 函数名(形参表 ...

  8. C++成员变量、构造函数的初始化顺序

    一.C++成员变量初始化 1.普通的变量:一般不考虑啥效率的情况下 可以在构造函数中进行赋值.考虑一下效率的可以再构造函数的初始化列表中进行 2.static 静态变量(本地化数据和代码范围): st ...

  9. Java类成员变量、普通成员变量、初始化块、构造方法的初始化和执行顺序

    结论:执行的大致顺序如下, (1) 在一个不存在继承的类中:初始化static变量,执行static初始化块-->初始化普通成员变量(如果有赋值语句),执行普通初始化块-->构造方法 (2 ...

随机推荐

  1. 在Ubuntu 12.04 - 64bit中安装CodeSourcery时提示错误

    安装时提示错误,Your 64-bit Linux host is missing the 32-bit libraries requied to install and use Sourcery C ...

  2. JavaScript一个类继承中实现

    JavaScript类是默认原型对象继承: var Person = function() { this.name = "people"; this.hello = functio ...

  3. leetcode[158] Read N Characters Given Read4 II - Call multiple times

    想了好一会才看懂题目意思,应该是: 这里指的可以调用更多次,是指对一个文件多次操作,也就是对于一个case进行多次的readn操作.上一题是只进行一次reandn,所以每次返回的是文件的长度或者是n, ...

  4. 【PLSQL】过程procedure形参和参数

    ************************************************************************   ****原文:blog.csdn.net/clar ...

  5. 如何使用SetTimer MFC 不够具体

    转会:http://blog.csdn.net/ellor/article/details/1714741 Timer事件,即定时器事件,是在游戏编程中.常常使用的一个事件.借助它能够产生定时运行动作 ...

  6. 领域驱动设计(DDD)

    领域驱动设计(DDD)实现之路 2004年,当Eric Evans的那本<领域驱动设计——软件核心复杂性应对之道>(后文简称<领域驱动设计>)出版时,我还在念高中,接触到领域驱 ...

  7. 求解轨道力学二体意义下的Lambert方程(兰伯特方程)的Fortran程序

    轨道力学中二体问题下求解兰伯特方程. 老外写的Matlab程序,我把它转成了Fortran程序. !************************************************** ...

  8. Python3.4入门之ifelse错误解决方案

    笔者用的是Python3.4 开始接触到ifelse语句 发现这样---C:\Users\Administrator>python e:\Python34\ifelse.py--去执行的时候老是 ...

  9. 我的Mac应用

    笔记内容 我的Mac软件 用Mac已经2年+,主要用来看电影.听音乐.写日记,其实也是因为偶像uSi在用,选择Mac不仅仅是因为Mac编程特别好用,更是一种生活方式 办公软件 iWork超爱iWork ...

  10. javascript继承之借用构造函数与原型

    javascript继承之借用构造函数与原型 在js中,关于继承只有利用构造函数和原型链两种来现实.以前所见到的种种方法与模式,只不过是变种罢了. 借用构造函数 1 2 3 4 5 6 7 8 9 1 ...