A Guide to Creating Objects in Java – 在Java中创建对象的指南

最后修改: 2017年 12月 26日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Simply put, before we can work with an object on the JVM, it has to be initialized.

简单地说,在我们能够使用JVM上的一个对象之前,它必须被初始化。

In the following sections, we’ll take a look at various ways we can initialize primitive types and objects.

在下面几节中,我们将看看我们可以用什么方式来初始化原始类型和对象。

2. Declaration vs. Initialization

2.声明与初始化

Let’s start by making sure that we’re on the same page.

让我们从确保我们处于同一水平开始。

Declaration is the process of defining the variable along with its type and name.

声明是定义变量的过程以及其类型和名称。

Here, we’re declaring the id variable:

这里,我们要声明的是id变量。

int id;

Initialization, on the other hand, is all about assigning a value; for example:

另一方面,初始化是关于赋值;例如:

id = 1;

To demonstrate, we’ll create a User class with a name and id properties:

为了演示,我们将创建一个具有nameid属性的User类。

public class User {
    private String name;
    private int id;
    
    // standard constructor, getters, setters,
}

Next, we’ll see that initialization works differently depending on the type of field we’re initializing.

接下来,我们将看到,初始化的工作方式因我们要初始化的字段类型而不同。

3. Objects vs. Primitives

3.对象与基元

Java provides two types of data representation: primitive types and reference types. In this section, we’ll discuss the differences between the two with regards to initialization.

Java提供了两种类型的数据表示:原始类型和引用类型。在本节中,我们将讨论两者在初始化方面的区别。

Java has eight built-in data types, referred to as Java primitive types; variables of this type hold their values directly.

Java有八个内置的数据类型,被称为Java原始类型;这种类型的变量直接持有它们的值。

Reference types hold references to objects (instances of classes). Unlike primitive types that hold their values in the memory where the variable is allocated, references don’t hold the value of the object they refer to.

引用类型持有对对象(类的实例)的引用。与原始类型在分配变量的内存中持有其值不同,引用并不持有它们所指向的对象的值。

Instead, a reference points to an object by storing the memory address where the object is located.

相反,引用通过存储对象所在的内存地址指向一个对象。

Note that Java doesn’t allow us to discover what the physical memory address is. Rather, we can only use the reference to refer to the object.

注意,Java不允许我们发现物理内存地址是什么。相反,我们只能使用引用来引用该对象。

Let’s take a look at an example that declares and initializes a reference type out of our User class:

让我们来看看一个例子,它声明并初始化了我们的User类中的一个引用类型。

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

As we can see here, a reference can be assigned to a new by using the keyword new, which is responsible for creating the new User object.

正如我们在这里看到的,通过使用关键字new,可以将一个引用分配给一个new,它负责创建新的User对象。

Let’s continue with learning more about object creation.

让我们继续学习更多关于对象创建的知识。

5. Creating Objects

5.创建对象

Unlike with primitives, objects creation is a bit more complex. This is because we’re not just adding the value to the field; instead, we trigger the initialization using the new keyword. This, in return, invokes a constructor and initializes the object in memory.

与基元不同,对象的创建要复杂一些。这是因为我们不只是向字段添加值;相反,我们使用new关键字触发初始化。作为回报,这将调用一个构造函数并在内存中初始化该对象。

Let’s discuss constructors and the new keyword in further detail.

让我们进一步详细讨论构造函数和new关键字。

The new keyword is responsible for allocating memory for the new object through a constructor.

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

A constructor is typically used to initialize instance variables representing the main properties of the created object.

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

If we don’t supply a constructor explicitly, the compiler will create a default constructor which has no arguments and just allocates memory for the object.

如果我们没有明确地提供一个构造函数,编译器将创建一个默认的构造函数,它没有参数,只是为对象分配内存。

A class can have many constructors as long as their parameters lists are different (overload). Every constructor that doesn’t call another constructor in the same class has a call to its parent constructor whether it was written explicitly or inserted by the compiler through super().

一个类可以有许多构造函数,只要它们的参数列表不同(overload。每一个不调用同一类中另一个构造函数的构造函数都有一个对其父级构造函数的调用,无论它是明确编写的还是由编译器通过super()插入的。

Let’s add a constructor to our User class:

让我们给我们的User类添加一个构造函数。

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

Now we can use our constructor to create a User object with initial values for its properties:

现在我们可以使用我们的构造函数来创建一个User对象,并为其属性提供初始值。

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

6. Variable Scope

6.可变的范围

In the following sections, we’ll take a look at the different types of scopes that a variable in Java can exist within and how this affects the initialization process.

在下面的章节中,我们将看一下Java中变量可以存在的不同类型的作用域,以及这对初始化过程的影响。

6.1. Instance and Class Variables

6.1.实例和类变量

Instance and class variables don’t require us to initialize them. As soon as we declare these variables, they are given a default value as follows:

实例和类变量不需要我们初始化它们。只要我们声明这些变量,它们就会被赋予一个默认值,如下所示。

init1

Now, let’s try to define some instance and class-related variables and test whether they have a default value or not:

现在,让我们试着定义一些与实例和类相关的变量,并测试它们是否有一个默认值。

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

6.2. Local Variables

6.2.本地变量

Local variables must be initialized before use, as they don’t have a default value and the compiler won’t let us use an uninitialized value.

本地变量在使用前必须被初始化,因为它们没有默认值,而且编译器不会让我们使用一个未初始化的值。

For example, the following code generates a compiler error:

例如,以下代码会产生一个编译器错误。

public void print(){
    int i;
    System.out.println(i);
}

7. The Final Keyword

7、最后的关键词

The final keyword applied to a field means that the field’s value can no longer be changed after initialization. In this way, we can define constants in Java.

应用于字段的final关键字意味着该字段的值在初始化后不能再被改变。通过这种方式,我们可以在Java中定义常量。

Let’s add a constant to our User class:

让我们给我们的User类添加一个常量。

private static final int YEAR = 2000;

Constants must be initialized either when they’re declared or in a constructor.

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

8. Initializers in Java

8.Java中的初始化器

In Java, an initializer is a block of code that has no associated name or data type and is placed outside of any method, constructor, or another block of code.

在Java中,初始化器是一个没有相关名称或数据类型的代码块,被置于任何方法、构造函数或其他代码块之外。

Java offers two types of initializers, static and instance initializers. Let’s see how we can use each of them.

Java提供两种类型的初始化器,静态和实例初始化器。让我们看看如何使用它们中的每一种。

8.1. Instance Initializers

8.1.实例初始化器

We can use these to initialize instance variables.

我们可以用这些来初始化实例变量。

To demonstrate, let’s provide a value for a user id using an instance initializer in our User class:

为了演示,让我们在User类中使用实例初始化器为用户id提供一个值。

{
    id = 0;
}

8.2. Static Initialization Block

8.2.静态初始化块

A static initializer or static block – is a block of code which is used to initialize static fields. In other words, it’s a simple initializer marked with the keyword static:

静态初始化器或静态块–是一个用于初始化静态字段的代码块。换句话说,它是一个简单的初始化器,标有关键字static:

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

9. Order of Initialization

9.初始化的顺序

When writing code that initializes different types of fields, of course, we have to keep an eye on the order of initialization.

当然,在编写初始化不同类型字段的代码时,我们必须注意初始化的顺序。

In Java, the order for initialization statements is as follows:

在Java中,初始化语句的顺序如下。

  • static variables and static initializers in order
  • instance variables and instance initializers in order
  • constructors

10. Object Life Cycle

10.对象生命周期

Now that we’ve learned how to declare and initialize objects, let’s discover what happens to objects when they’re not in use.

现在我们已经学会了如何声明和初始化对象,让我们来发现当对象不被使用时,它们会发生什么。

Unlike other languages where we have to worry about object destruction, Java takes care of obsolete objects through its garbage collector.

与其他语言不同的是,我们必须担心对象的销毁问题,Java通过其垃圾收集器来处理过时的对象。

All objects in Java are stored in our program’s heap memory. In fact, the heap represents a large pool of unused memory, allocated for our Java application.

Java中的所有对象都存储在我们程序的堆内存中。事实上,堆代表了一个大的未使用的内存池,为我们的Java程序分配。

On the other hand, the garbage collector is a Java program that takes care of automatic memory management by deleting objects that are no longer reachable.

另一方面,垃圾收集器是一个Java程序,它通过删除不再可触及的对象来负责自动内存管理

For a Java object to become unreachable, it has to encounter one of the following situations:

对于一个Java对象来说,它必须遇到以下情况之一才会变得无法到达。

  • The object no longer has any references pointing to it
  • All reference pointing to the object are out of scope

In conclusion, an object is first created from a class, usually using the keyword new. Then the object lives its life and provides us with access to its methods and fields.

总之,一个对象首先是由一个类创建的,通常使用关键字new。然后,该对象就开始了它的生活,并为我们提供了对其方法和字段的访问。

Finally, when it’s no longer needed, the garbage collector destroys it.

最后,当它不再需要时,垃圾收集器就会将其销毁。

11. Other Methods for Creating Objects

11.创建对象的其他方法

In this section, we’ll take a brief look at methods other than new keyword to create objects and how to apply them, specifically reflection, cloning, and serialization.

在这一节中,我们将简要介绍new keyword以外的创建对象的方法以及如何应用这些方法,特别是反射、克隆和序列化

Reflection is a mechanism we can use to inspect classes, fields, and methods at run-time. Here’s an example of creating our User object using reflection:

反射是一种机制,我们可以用来在运行时检查类、字段和方法。下面是一个使用反射创建我们的User对象的例子。

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

In this case, we’re using reflection to find and invoke a constructor of the User class.

在这种情况下,我们使用反射来寻找并调用User类的构造函数。

The next method, cloning, is a way to create an exact copy of an object. For this, our User class must implement the Cloneable interface:

下一个方法,克隆,是创建一个对象的精确副本的方法。为此,我们的User类必须实现Cloneable接口。

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

Now we can use the clone() method to create a new clonedUser object which has the same property values as the user object:

现在我们可以使用clone()方法来创建一个新的clonedUser对象,它的属性值与user对象相同。

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

We can also use the sun.misc.Unsafe class to allocate memory for an object without calling a constructor:

我们也可以使用sun.misc.Unsafe类来为一个对象分配内存而不调用构造函数:

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

12. Conclusion

12.结论

In this tutorial, we covered initialization of fields in Java. We discovered different data types in Java and how to use them. We also took an in-depth on several ways of creating objects in Java.

在本教程中,我们介绍了Java中字段的初始化。我们发现了Java中不同的数据类型以及如何使用它们。我们还深入探讨了在Java中创建对象的几种方法。

The full implementation of this tutorial can be found over on Github.

本教程的完整实现可以在Github上找到over