1. 概述

简单来说,在JVM上操作对象前,必须先完成初始化。本教程将探讨Java中初始化基本类型和对象的各种方式。

2. 声明与初始化

先明确两个概念:

声明是定义变量的过程,包含类型和名称:

int id;

初始化则是为变量赋值:

id = 1;

为演示区别,创建一个包含nameid属性的User类:

public class User {
    private String name;
    private int id;
    
    // 标准构造函数、getter、setter
}

接下来我们将看到,不同类型字段的初始化方式存在差异。

3. 对象与基本类型

Java提供两种数据表示:基本类型和引用类型。本节讨论两者在初始化上的区别。

Java有八种内置数据类型(基本类型),变量直接存储值。

引用类型存储对象的引用(类实例)。与基本类型不同,引用类型不直接存储对象值,而是通过存储对象所在内存地址指向对象

注意:Java不允许直接获取物理内存地址,我们只能通过引用操作对象。

以下示例声明并初始化User引用类型:

@Test
public void whenIntializedWithNew_thenInstanceIsNotNull() {
    User user = new User();
 
    assertThat(user).isNotNull();
}

可见,使用new关键字可创建新对象并赋值给引用。下面深入探讨对象创建过程。

4. 创建对象

与基本类型不同,对象创建更复杂。因为不只是赋值,而是通过new关键字触发初始化,进而调用构造函数并在内存中初始化对象。

new关键字负责通过构造函数为新对象分配内存

构造函数通常用于初始化表示对象主要属性的实例变量

若未显式提供构造函数,编译器会创建无参默认构造函数,仅分配内存。

类可拥有多个构造函数(重载),只要参数列表不同。每个未调用同类其他构造函数的构造函数,都会隐式或显式调用父类构造函数(通过super())。

User类添加构造函数:

public User(String name, int id) {
    this.name = name;
    this.id = id;
}

现在可用构造函数创建带初始值的User对象:

User user = new User("Alice", 1);

5. 变量作用域

以下章节探讨Java变量的不同作用域及其对初始化的影响。

5.1. 实例变量与类变量

实例变量和类变量无需显式初始化,声明时会自动获得默认值:

init1

测试实例变量和类变量的默认值:

@Test
public void whenValuesAreNotInitialized_thenUserNameAndIdReturnDefault() {
    User user = new User();
 
    assertThat(user.getName()).isNull();
    assertThat(user.getId() == 0);
}

5.2. 局部变量

局部变量必须在使用前初始化,它们没有默认值,编译器禁止使用未初始化的局部变量。

例如以下代码会触发编译错误:

public void print(){
    int i;
    System.out.println(i); // 编译错误
}

6. final关键字

final修饰的字段表示初始化后值不可再修改,可用于定义常量。

User类添加常量:

private static final int YEAR = 2000;

常量必须在声明时或构造函数中初始化。

7. Java中的初始化块

Java中初始化块是无名称、无数据类型的代码块,位于方法、构造函数或其他代码块之外。

Java提供两种初始化块:静态初始化块和实例初始化块。

7.1. 实例初始化块

用于初始化实例变量。在User类中通过实例初始化块设置id

{
    id = 0;
}

7.2. 静态初始化块

静态初始化块(静态块)用于初始化static字段,即带static关键字的简单初始化块:

private static String forum;
static {
    forum = "Java";
}

8. 初始化顺序

编写初始化代码时需注意初始化顺序:

  1. 静态变量和静态初始化块(按声明顺序)
  2. 实例变量和实例初始化块(按声明顺序)
  3. 构造函数

9. 对象生命周期

掌握对象创建后,我们来看看不再使用的对象会发生什么。

与其他语言不同,Java通过垃圾回收器自动管理对象销毁。

所有Java对象都存储在堆内存中,堆是为Java应用分配的大块未使用内存池。

垃圾回收器是Java程序,负责自动内存管理,通过删除不可达对象回收内存。

对象不可达的情况包括:

  • ✅ 无任何引用指向对象
  • ✅ 所有引用超出作用域

对象生命周期总结:通过new创建 → 提供方法/字段访问 → 垃圾回收器销毁。

10. 其他创建对象的方法

new关键字外,还有反射、克隆和序列化等创建对象的方式。

反射是运行时检查类、字段和方法的机制。以下用反射创建User对象:

@Test
public void whenInitializedWithReflection_thenInstanceIsNotNull() 
  throws Exception {
    User user = User.class.getConstructor(String.class, int.class)
      .newInstance("Alice", 2);
 
    assertThat(user).isNotNull();
}

克隆是创建对象精确副本的方式User类需实现Cloneable接口:

public class User implements Cloneable { //... }

使用clone()创建副本:

@Test
public void whenCopiedWithClone_thenExactMatchIsCreated() 
  throws CloneNotSupportedException {
    User user = new User("Alice", 3);
    User clonedUser = (User) user.clone();
 
    assertThat(clonedUser).isEqualTo(user);
}

也可使用sun.misc.Unsafe类分配内存而不调用构造函数

User u = (User) unsafeInstance.allocateInstance(User.class);

⚠️ 注意:Unsafe是内部API,生产环境应谨慎使用。

11. 总结

本文全面探讨了Java字段初始化机制,分析了基本类型与引用类型的区别,展示了多种对象创建方式,并揭示了初始化顺序和对象生命周期。掌握这些知识能助你编写更健壮的Java代码。


原始标题:A Guide to Java Initialization | Baeldung

« 上一篇: Java中的软引用
» 下一篇: Java Cipher 类详解