面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,
构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的
结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。
我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是
(静态变量、静态初始化块)>(变量、初始化块)>构造器。
我们也可以通过下面的测试代码来验证这一点:
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("子类--静态初始化块");
- }
- // 初始化块 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");
- }
- }
运行上面的代码,会得到如下的结果:
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
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");
- }
- }
运行结果:
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 编程语言中,类的初始化顺序对于理解程序的行为至关重要。特别是当涉及到静态域(静态变量)、非静态域(实例变量)、静态块...
在Java中,初始化顺序依次为:静态初始化块 -> 实例初始化块 -> 构造方法。 5.5 this 关键字: `this`关键字引用当前对象,常用于区分成员变量和局部变量,以及在构造方法中调用其他构造方法。 6.1 继承(extends...
- 类加载时,首先初始化静态变量,然后初始化非静态变量,最后执行构造方法。 **3.2. 封装** - **封装**是指隐藏对象的实现细节,只暴露必要的接口。封装提高了代码的安全性和可维护性。 **3.3. 继承extends** ...
12. **构造函数**:特殊类型的函数,用于初始化新对象。类可以有多个构造函数,根据参数列表进行重载。 13. **函数式编程**:Java 8引入了对函数式编程的支持,如Stream API,使得处理集合数据变得更加简洁和高效。...
Java 基础知识面试题系列一主要涵盖了Java程序的入口方法、静态块的执行、类及对象初始化的顺序以及变量的作用域等核心概念。以下是这些知识点的详细说明: 1. **公共静态void主(String[] args)**:Java程序的入口...
静态代码块是用来初始化类的成员变量或者是执行类的一些初始化操作。它通常定义在类体中,并且前面必须有`static`关键字修饰。静态代码块的特点是: 1. **执行时机**:静态代码块会在类首次被加载到JVM时执行,也...
26. **创建对象的内部过程**:分配内存、调用构造函数初始化、返回对象引用。 27. **值传递与引用传递**:Java中,对象作为参数传递实际上是引用传递,但不能改变对象本身,只能修改对象的内容。 28. **常用类、包...
从提供的【部分内容】中,我们可以提炼出Java语言中面向对象编程的几个核心概念,包括类的继承、构造函数的执行顺序、静态属性的初始化顺序、以及变量的作用域等方面的知识点。以下是详细解释: 1. 类的继承与构造...
6. **构造函数**:特殊类型的函数,用于对象的初始化。类名与构造函数相同,没有返回类型。 7. **匿名函数**:Java 8引入了Lambda表达式,允许创建没有名字的函数,常用于函数式接口。 8. **递归**:函数调用自身...
- **声明与初始化**:声明变量时需要指定其类型,例如 `int num;` 表示声明了一个整型变量 `num`;初始化则为变量赋初值,例如 `int num = 10;`。 #### 2. 语句 - **控制语句**:包括条件语句 (`if`, `switch`) 和...
- `final`变量必须在声明时或构造函数中初始化。 #### 6.3 方法的重写 - 声明为`final`的方法不能被子类重写。 - 如果一个类被声明为`final`,则该类不能被继承。 ### 7. 接口与实现 #### 7.1 接口中的常量 - ...
- `super()`用于调用父类的构造器,确保父类的初始化。 - `this()`用于在同一个类中调用本类的其他构造器。 2. 作用域:`public`, `protected`, `private`和默认的区别: - `public`:任何地方都可以访问。 - `...
例如,静态代码块用于类级别的设置,构造器代码块用于对象的初始化,而局部代码块则提供了一种在特定逻辑范围内控制变量的方式。 在提供的`main.java`文件中,可能包含了对这些代码块的实践和示例,通过阅读和理解...
- **静态初始化**:在声明后赋值。 - **数组中元素的访问** 数组元素通过索引访问,索引从0开始。例如,数组`int[] arr = {1, 2, 3};`,访问第一个元素为`arr[0]`。 - **数组的`length`属性** `length`属性可以...
- **局部变量**:在方法、构造函数或块内声明的变量。 - **成员变量**:在类体中声明的变量。 ##### 4.2 操作符 Java支持多种操作符,包括算术操作符、比较操作符、逻辑操作符等。 ##### 4.3 数字类型之间的转换 ...
- 静态代码块:用于类级别的初始化。 - 实例初始化代码块:用于对象级别的初始化。 - 局部代码块:用于限制变量的作用域。 #### 24. 类的实例化过程 - **步骤**: - 加载类。 - 分配内存。 - 调用构造方法...
- 初始化顺序:实例变量、构造方法、子类构造方法。 - 使用构造方法进行对象初始化。 **3.9 static关键字** - 定义类变量和静态方法。 - 静态方法可以直接通过类名调用。 **3.10 对象的清理** - 使用垃圾回收...
如果类中有多个静态初始化块,它们将按照出现顺序依次执行。 ##### 实例变量初始化 实例变量在创建对象时初始化,可以是显式的初始化语句,也可以是在构造函数中初始化。值得注意的是,如果一个实例变量没有显式...
构造函数用于初始化类的新实例,虽然不是必需的,但每个类都有默认的无参构造函数。构造函数可以重载,即在同一个类中可以有多个构造函数,它们的参数列表不同。 6. **类的修饰符** 类、属性和方法可以同时受到多...
3. **静态初始化块**:当类被加载到JVM(Java虚拟机)时,这些代码块被执行。它们用于初始化类级别的变量,不依赖于类的实例。 4. **finally代码块**:用于异常处理,无论是否抛出异常,finally块中的代码总会被...