引用 :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();
- }
- }
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("子类--静态初始化块");
- }
- // 初始化块 http://ini.iteye.com/
- {
- System.out.println(s_Field);
- System.out.println("子类--初始化块");
- }
- // 构造器
- public SubClass() {
- System.out.println("子类--构造器");
- }
- // 程序入口
- public static void main(String[] args) {
- new SubClass();
- }
- }
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("子类--静态初始化块"); } // 初始化块 http://ini.iteye.com/ { 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");
- }
- }
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();
- }
- }
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
8
8
父类无参构造方法在执行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");
- }
- }
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 类中静态域、块,非静态域、块,构造函数的初始化顺序 #### 一、概述 在 Java 编程语言中,类的初始化顺序对于理解程序的行为至关重要。特别是当涉及到静态域(静态变量)、非静态域(实例变量)、静态块...
静态代码块是用来初始化类的成员变量或者是执行类的一些初始化操作。它通常定义在类体中,并且前面必须有`static`关键字修饰。静态代码块的特点是: 1. **执行时机**:静态代码块会在类首次被加载到JVM时执行,也...
从提供的【部分内容】中,我们可以提炼出Java语言中面向对象编程的几个核心概念,包括类的继承、构造函数的执行顺序、静态属性的初始化顺序、以及变量的作用域等方面的知识点。以下是详细解释: 1. 类的继承与构造...
12. **构造函数**:特殊类型的函数,用于初始化新对象。类可以有多个构造函数,根据参数列表进行重载。 13. **函数式编程**:Java 8引入了对函数式编程的支持,如Stream API,使得处理集合数据变得更加简洁和高效。...
3. **Java程序初始化顺序**:初始化顺序遵循以下原则: - 首先初始化静态变量和静态代码块,按定义顺序执行。 - 其次是父类的非静态部分,包括非静态变量和非静态代码块,再是父类构造函数。 - 最后是子类的非...
6. **构造函数**:特殊类型的函数,用于对象的初始化。类名与构造函数相同,没有返回类型。 7. **匿名函数**:Java 8引入了Lambda表达式,允许创建没有名字的函数,常用于函数式接口。 8. **递归**:函数调用自身...
3. **静态初始化块**:当类被加载到JVM(Java虚拟机)时,这些代码块被执行。它们用于初始化类级别的变量,不依赖于类的实例。 4. **finally代码块**:用于异常处理,无论是否抛出异常,finally块中的代码总会被...
例如,静态代码块用于类级别的设置,构造器代码块用于对象的初始化,而局部代码块则提供了一种在特定逻辑范围内控制变量的方式。 在提供的`main.java`文件中,可能包含了对这些代码块的实践和示例,通过阅读和理解...
默认构造函数没有参数,而自定义构造函数可以带有参数以进行初始化。例如,`Circle`类可以定义不同的构造函数,以处理不同半径的圆。 9.3 构造函数: 构造函数没有返回类型,名称与类名相同,主要用于对象初始化。...
26. **创建对象的内部过程**:分配内存、调用构造函数初始化、返回对象引用。 27. **值传递与引用传递**:Java中,对象作为参数传递实际上是引用传递,但不能改变对象本身,只能修改对象的内容。 28. **常用类、包...
在Java中,一个类的构造函数用于初始化该类的对象,并且在创建对象时自动调用。然而,如果构造函数中使用了与类中的变量同名的局部变量,则构造函数内部的变量将隐藏类中的变量。因此,在试题2中,尽管构造函数中...
4. **初始化顺序**:先初始化基类的属性,然后是派生类自己的属性。 5. **构造方法的重载**:构造函数可以被重载,即在一个类中可以有多个构造函数,但它们的参数列表不同。 #### 七、类的修饰符 1. **Public类**...
- 对象的创建和初始化,包括构造函数的使用规则,以及如何使用静态代码块和实例代码块进行复杂的初始化操作。 - 数组和集合的初始化方式,例如数组可以在声明时直接赋值,而集合则可以在构造函数中传入初始元素进行...
11. **继承时类的执行顺序**:首先执行父类的静态初始化块,然后执行子类的静态初始化块,接着执行父类的非静态初始化块和构造函数,最后执行子类的非静态初始化块和构造函数。 12. **内部类的实现方式**:包括成员...
7. **静态初始化器**:静态初始化器(静态块)在类加载时执行,用于初始化静态成员。 8. **对象赋值**:Java中对象赋值实际上是创建了一个新对象的引用,并非复制整个对象。赋值操作不会生成属性相同的“新对象”。...
25. 初始化成员顺序:成员初始化顺序由声明顺序决定,与初始化列表中顺序无关。 26. 类与对象关系:类是对象的蓝图,对象是类的具体实例。 27. 访问属性:public(公开)、protected(保护)、private(私有)。 ...
5. **构造函数**:构造函数是类的一个特殊方法,用于初始化新创建的对象。默认情况下,如果程序员没有定义构造函数,JVM会提供一个无参构造函数,不做任何操作。但一旦程序员自定义了构造函数,JVM就不会再生成默认...
5. **子类非静态初始化块**:接下来执行子类中的非静态初始化块。 6. **子类构造函数**:最后执行子类的构造函数。 注意:在第二次创建子类对象时,父类和子类的静态初始化块不会再次执行,因为它们只在类加载时...
6. 构造函数:Java 中,构造函数用于初始化类的新实例。A 错误,因为类可以没有显式定义的构造函数,Java 会提供一个默认的无参构造函数。B 错误,构造函数没有返回类型。C 错误,构造函数可以带参数。D 正确,类...
如果类中有多个静态初始化块,它们将按照出现顺序依次执行。 ##### 实例变量初始化 实例变量在创建对象时初始化,可以是显式的初始化语句,也可以是在构造函数中初始化。值得注意的是,如果一个实例变量没有显式...