在 Java 里定义一个类的时候,很多时候我们需要提供成员变量,成员变量专业叫法是 Memeber Variable 或者干脆的叫作 Field. 根据是否使用 static 关键字修饰,可以将 Field 分为两种:
- static field:也称作 class variable,这种 filed 属于 class,并不属于单个 instance,所有该 class 的 intance 共享内存中的同一份 class field。
- non-static field:也称作 instance variable,它属于每一个具体的 instance,class 的每一个 instance 独享一份 non-static field。
接下来进入本文的主题:java 中 field 的初始化方式。
从初始化代码所在的位置看,可以粗略的分为三种:
- 在声明 field 的地方进行初始化。
- 在类的构造方法(constructor) 里对 field 进行初始化
- 在初始化块(initialization block) 中对已声明的 field 进行初始化
第一种方式主要用于简单的赋值,使用这种方式的前提是作为初始化变量的值是已知的并且通常可以使用单行的赋值语句完成(例外?参见 Double Brace Initialization)。
public class Foo { // class variable initializer private static Logger logger = LoggerFactory.getLogger(Foo.class); // instance variable initializer private List<String> fooList = new ArrayList<String>(); }
对于复杂的初始化语句,如包含异常处理(try-catch)、使用循环结构初始化等,则需要考虑另外两种初始化方式:constructor 和 initialization block。其中 initialization block 根据是否由 static 关键字修饰,又可分为 static(class) initialization block 和 instance(object) initialization block,前一种只能初始化 class variable,用它进行 instance variable 的初始化会导致编译错误。
构造方法(constructor)可以用于初始化 instance variable。除此之外,少数情况下,instance variable 的初始化需要考虑使用 instance initialization block 完成。例如,在匿名类中的初始化(因匿名类无构造方法),或者类中包含了多个 constructor,而它们有公共的一些复杂初始化操作,此时可以考虑将这些操作提取到 instance initialization block 里。除了这两种情况,在你的代码中应该尽量少使用 instance initialization block。
static initialization block 用于处理带有 class variable 的初始化操作。
public class BarClass { private static Properties propTable; static { try { propTable.load(new FileInputStream("/data/user.prop")); } catch (Exception e) { propTable.put("user", System.getProperty("user")); propTable.put("password", System.getProperty("password")); } } }
static initialization block 的另一个功能与线程安全(thread safe)相关。JVM 保证使用同一个 ClassLoader 加载的类中的 static initialization block 只被执行一次,因而它是线程安全的。也正因为这一点,很多时候我们可以利用 static initialization block 执行一些初始化(write)操作,而无需对该 block 使用任何同步机制。
最后来看一下初始化代码的执行顺序问题。在此之前,先看下面这段代码,它可以完整执行,请试着分析一下最后的输出是什么。
/** * @author wxl24life * */ public class ClassInitializerOrderTest{ public static void main(String[] args) { B a = new B(); } } class A { static int a = initA(); static int initA() { System.out.println("call 1"); return 0; } { System.out.println("call 5"); } { System.out.println("call 6"); } static { System.out.println("call 2"); } static { System.out.println("call 3"); } static int b = initB(); static int initB() { System.out.println("call 4"); return 0; } A() { System.out.println("call 7"); } } class B extends A { { System.out.println("call 8"); } String str = initStr(); String initStr() { System.out.println("call 9"); return "call 8"; } static { System.out.println("call 10"); } B() { super(); System.out.println("call 12"); } { System.out.println("call 11"); } }
用几句话概括下初始化顺序规则(假设调用方式类似于上面代码,即使用 new 操作符 ):
- static 先于 non-static, non-static 先于 constructor。这里的 static 统指 static field 和 static initialization block 两种初始化方式,non-static 同上。
- static 初始化代码按照在源代码中定义的顺序从上往下以此执行,non-static 同上。
- 存在继承关系时,优先执行基类中的初始化语句。
执行顺序测试代码的输出结果:
call 1 call 2 call 3 call 4 call 10 call 5 call 6 call 7 call 8 call 9 call 11 call 12
参考阅读:
相关推荐
本文将详细探讨Java中类的初始化过程及其顺序,并通过具体的代码示例来帮助理解这一过程。 #### 二、基础知识 1. **静态成员变量(Static Fields)**:在类加载时初始化。 2. **实例成员变量(Instance Fields)**...
理解ClassLoader的工作机制以及类变量初始化的顺序对于深入理解Java运行时环境至关重要。这篇博文将探讨这两个主题。 首先,让我们深入了解Java的ClassLoader。ClassLoader是一个抽象类,它是Java中的一个关键组件...
这篇文章将深入探讨 Java 类加载器中的静态变量初始化机制,了解其背后的工作原理和载入过程。 静态变量初始化机制 ------------------ 在 Java 中,静态变量是指在类加载时初始化的变量。静态变量的初始化是由类...
总之,Java代码的初始化顺序是类加载的必然过程,涉及到静态和实例初始化块、构造函数、成员变量初始化以及继承关系的影响。这个demo是学习和理解这些概念的重要工具,通过实际操作可以加深对Java内存管理和对象生命...
在Java编程语言中,初始化块是程序执行时用于初始化对象或类的重要机制。这里我们将深入探讨两种类型的初始化块:静态初始化块(Static Initializer Block)和对象初始化块(Instance Initializer Block)。这两种...
8. **父类非静态成员变量初始化**:接下来,`basecode` 被设置为 `"父类非静态变量初始化"`。 9. **父类非静态初始化块**:执行输出 `"我输出的是父类非静态块内容→" + this.basecode`。此时 `basecode` 已经被...
本文将深入探讨Java虚拟机中的类初始化以及加载器的父委托机制。 一、类的加载 类加载是JVM启动时或运行中根据需要动态加载类到内存中的过程。这个过程分为三个阶段:加载、链接和初始化。 1. 加载:JVM通过类...
本文将深入探讨Java中的初始化过程、方法的定义与调用,以及与之相关的源码和工具应用。 首先,让我们从类的初始化开始。在Java中,类的初始化通常发生在以下几个时刻:当类被首次加载、创建类的实例、访问静态变量...
首先,我们来看第一种情况:类内的变量初始化。当声明一个类的成员变量(即实例变量)时,即使你没有显式地为它们赋值,Java编译器会自动为这些变量提供一个默认值。这些默认值是根据变量的数据类型来设定的: 1. ...
以下我们将详细探讨构造器的调用、初始化成员变量、默认构造器以及继承中的构造器调用。 1. **构造器的调用**: - 当我们创建一个类的实例时,会自动调用构造器来设置初始状态。例如: ```java class MyClass { ...
`static`修饰的方法是类方法,它在类加载时就被初始化,不依赖于类的实例,可以直接通过类名调用,但不能访问实例变量。实例方法则可以访问类变量和实例变量,可以操作对象的状态。方法重载允许在同一类中定义多个...
在Java中,静态成员(包括静态变量和静态方法)在任何类的实例创建之前都会被初始化。这意味着无论是否创建了该类的对象,静态成员都将在程序运行时首次访问该类时被初始化。此外,静态成员在整个程序的生命周期内只...
在Java编程语言中,初始化数据域(字段或成员变量)是程序设计的重要组成部分。它确保在对象被创建或类被加载时,变量拥有一个合理的初始值。本篇文章将详细探讨四种常见的初始化数据域的方法,结合给出的代码示例...
本篇文章将深入探讨C++和Java中变量和成员初始化的差异,并通过具体的代码示例进行对比。 在C++中,初始化顺序遵循以下规则: 1. 静态成员变量:无论它们在哪里定义,静态成员变量都按照声明的顺序初始化。 2. 非...
4. **初始化**:C++允许在类声明中直接初始化成员变量,Java则需要在构造函数中或使用`final`关键字实现。 5. **内存管理**:C++提供了更多的内存控制,如手动分配和释放内存(使用`new`和`delete`),而Java通过...
2. **静态初始化**:每个类的静态成员变量和静态初始化块(如果有的话)会在类加载时被初始化。在`Base`类中,静态变量`a`首先被赋值为10,然后执行静态初始化块,打印出"Static Init Base 10"。接下来,JVM继续加载...
在Java编程语言中,类的成员变量分为两种类型:实例变量和类变量(或静态变量)。实例变量是每个对象独有的,而类变量是所有对象共享的。本文将深入探讨这两种变量的区别以及如何在实际编程中使用它们。 首先,实例...
在Java编程语言中,`static`关键字扮演着极其重要的角色,它主要用于声明类的成员变量、方法以及代码块为静态。本文将深入探讨`static`在变量、方法和代码块中的应用,通过示例和解释帮助读者更深刻地理解其功能与...
`this`关键字在对象初始化中用于引用当前对象,而`super`关键字用于引用父类的实例。在子类的构造函数中,可以通过`super()`调用来调用父类的构造函数。 6. **初始化顺序** 初始化的顺序是:首先是静态初始化块,...
本篇文章将深入探讨“Java起航——类的初始化历程”,并结合JVM(Java虚拟机)的工作原理,帮助你更好地理解这个过程。 首先,我们需要了解Java类的生命周期,它包括加载、验证、准备、解析和初始化五个阶段。当一...