面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。
我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是(静态变量、静态初始化块)>(变量、初始化块)>构造器。我们也可以通过下面的测试代码来验证这一点:
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();
}
}
运行以上代码,我们会得到如下的输出结果:
静态变量
静态初始化块
变量
初始化块
构造器
这与上文中说的完全符合。那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果:
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();
}
}
运行一下上面的代码,结果马上呈现在我们的眼前:
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。
那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢?是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。我们以静态变量和静态初始化块为例来进行说明。
同样,我们还是写一个类来进行测试:
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以及静态初始化块的前后位置,就会发现输出结果随着它们在类中出现的前后顺序而改变,这就说明静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的。同样,变量和初始化块也遵循这个规律。
了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。
分享到:
相关推荐
### Java 类中静态域、块,非静态域、块,构造函数的初始化顺序 #### 一、概述 在 Java 编程语言中,类的初始化顺序对于理解程序的行为至关重要。特别是当涉及到静态域(静态变量)、非静态域(实例变量)、静态块...
静态构造函数在整个应用程序域中只执行一次,无论类的实例创建多少次。 6. 继承特性: 静态构造函数不被子类继承。每个类都有自己的静态构造函数,即使子类没有显式定义。 7. 编译器生成: 如果类中包含有初始...
- **非托管资源加载**:在封装非托管代码的类中,静态构造函数可能调用如`LoadLibrary`这样的方法来加载库。 下面是一个展示静态构造函数行为的`Bus`类示例: ```csharp public class Bus { static int counter =...
2. **执行顺序**:静态代码块的执行优先于构造代码块和构造函数。这是因为类的加载发生在对象创建之前。 3. **适用场景**:静态代码块主要用于初始化类级别的资源或者数据,比如数据库连接、日志配置等,这些通常是...
在C#编程语言中,函数方法、构造函数与析构函数是对象导向编程的重要组成部分,它们各自承担着不同的角色,并且对于程序的正确运行和优化性能具有重要作用。下面将详细介绍这三个概念及其用法。 ### 函数方法 在C#...
- **问题分析**:这种规定使得无法在同一个作用域内定义多个具有不同行为但名字相同的构造函数。此外,如果类名更改,则所有相关的构造函数也需要随之更改,这增加了维护成本。 - **解决方案**:考虑允许构造函数...
**析构函数**与构造函数相反,它在对象生命周期结束时(例如,对象的作用域结束或者动态分配的对象被delete)被调用,用于清理对象资源。析构函数的名字是类名前加上一个波浪线`~`。尽管析构函数没有返回值,但它...
Java 代码块是指在类中使用大括号 `{}` 包围的代码块,这些代码块可以出现在类的任何位置,包括构造函数、静态代码块、实例代码块等。Java 代码块的执行顺序取决于其所在的位置和上下文环境。 在上面的实例代码中,...
3. 复制构造函数:复制构造函数是一种特殊的构造函数,它用于基于一个已存在的相同类的对象创建一个新的对象。复制构造函数的参数是对已存在的对象的引用。 4. 析构函数:与构造函数相对应,析构函数是在对象生命...
- 静态对象:只在首次定义时调用构造函数,之后不再调用,因为静态对象在整个程序运行期间都存在。 - 全局对象:在main函数执行之前调用构造函数。 在多层继承和类层次结构中,构造函数和析构函数的调用顺序遵循...
本文将详细介绍静态变量与非静态变量的区别、静态变量的使用方式、静态构造函数的使用等方面的知识点。 一、静态变量与非静态变量的区别 静态变量和非静态变量是 C# 语言中两种不同的变量类型。静态变量是在应用...
在这个课件中,我们将深入理解类与对象的定义、使用,以及相关的构造函数、析构函数、访问权限、成员函数、静态成员、友元等关键知识点。 首先,类(Class)是C++中定义的一种数据结构,它包含了数据成员(Data ...
同时,我们定义了一个公有的静态域INSTANCE,它是Elvs类的唯一实例。这样,在其他类中,我们可以通过静态变量INSTANCE来获取Elvs类的实例。 public class Main { public static void main(String[] args) { Elvs ...
C++是一种静态类型、编译式的面向对象编程语言,它提供了丰富的特性,其中包括构造函数和析构函数。构造函数和析构函数在类对象的生命周期中扮演着关键角色。 **构造函数**是C++中一个特殊的类成员函数,用于初始化...
- `point`类的构造函数接受一个整数参数`m`,用于初始化非静态数据成员`x`。 - 在构造函数中,每创建一个新对象时,`count`都会增加1,这使得我们可以跟踪已经创建了多少个`point`对象。 3. **析构函数**: - 析...
构造器代码块内的代码会先于构造函数中的代码执行。示例如下: ```java public class MyClass { { System.out.println("构造器代码块执行"); // 实例变量初始化 } public MyClass() { // 构造函数代码 } } ...
在C++编程中,构造函数和析构函数是类的重要组成部分,它们负责对象的初始化和清理工作。理解它们的调用顺序是编写高效且无内存泄漏的代码的关键。以下是关于C++构造函数与析构函数调用顺序的详细解释。 首先,构造...
(3)类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数 (4)静态构造函数是不可继承的,而且不能被直接调用。 (5)如果类中包含用来开始执行的...