Un ghid pentru crearea obiectelor în Java

1. Prezentare generală

Pur și simplu, înainte de a putea lucra cu un obiect pe JVM, acesta trebuie inițializat.

În secțiunile următoare, vom arunca o privire asupra diferitelor moduri în care putem inițializa tipurile și obiectele primitive.

2. Declarație vs. Inițializare

Să începem prin a ne asigura că suntem pe aceeași pagină.

Declarația este procesul de definire a variabilei împreună cu tipul și numele acesteia.

Aici declarăm variabila id :

int id;

Inițializarea, pe de altă parte, se referă la atribuirea unei valori; de exemplu:

id = 1;

Pentru a demonstra, vom crea o clasă de utilizator cu un nume și proprietăți id :

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

Apoi, vom vedea că inițializarea funcționează diferit în funcție de tipul de câmp pe care îl inițializăm.

3. Obiecte vs. Primitive

Java oferă două tipuri de reprezentare a datelor: tipuri primitive și tipuri de referință. În această secțiune, vom discuta diferențele dintre cele două în ceea ce privește inițializarea.

Java are opt tipuri de date încorporate, denumite tipuri primitive Java; variabilele de acest tip își păstrează valorile direct.

Tipurile de referințe conțin referințe la obiecte (instanțe de clase). Spre deosebire de tipurile primitive care își păstrează valorile în memoria în care este alocată variabila, referințele nu dețin valoarea obiectului la care se referă.

În schimb, o referință indică un obiect prin stocarea adresei de memorie în care se află obiectul.

Rețineți că Java nu ne permite să descoperim care este adresa memoriei fizice. Mai degrabă, putem folosi doar referința pentru a ne referi la obiect.

Să aruncăm o privire la un exemplu care declară și inițializează un tip de referință din clasa noastră de utilizator :

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

După cum putem vedea aici, o referință poate fi atribuită unui nou folosind cuvântul cheie new, care este responsabil pentru crearea noului obiect User .

Să continuăm să aflăm mai multe despre crearea obiectelor.

5. Crearea obiectelor

Spre deosebire de primitive, crearea obiectelor este puțin mai complexă. Acest lucru se datorează faptului că nu doar adăugăm valoare câmpului; în schimb, declanșăm inițializarea folosind noul cuvânt cheie. Aceasta, în schimb, invocă un constructor și inițializează obiectul în memorie.

Să discutăm în detaliu constructorii și noul cuvânt cheie.

Noul cuvânt cheie este responsabil pentru alocarea de memorie pentru noul obiect printr - un constructor.

Un constructor este de obicei folosit pentru a inițializa variabilele de instanță care reprezintă principalele proprietăți ale obiectului creat .

Dacă nu furnizăm în mod explicit un constructor, compilatorul va crea un constructor implicit care nu are argumente și doar alocă memorie pentru obiect.

O clasă poate avea mulți constructori atâta timp cât listele lor de parametri sunt diferite ( supraîncărcare ) . Fiecare constructor care nu apelează alt constructor din aceeași clasă are un apel către constructorul său părinte, indiferent dacă a fost scris explicit sau inserat de compilator prin super () .

Să adăugăm un constructor la clasa noastră de utilizator :

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

Acum putem folosi constructorul nostru pentru a crea un obiect User cu valori inițiale pentru proprietățile sale:

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

6. Domeniul de aplicare variabil

În secțiunile următoare, vom arunca o privire asupra diferitelor tipuri de domenii în care poate exista o variabilă în Java și modul în care aceasta afectează procesul de inițializare.

6.1. Variabile de instanță și de clasă

Variabilele de instanță și de clasă nu necesită inițializarea lor. De îndată ce declarăm aceste variabile, li se dă o valoare implicită după cum urmează:

Acum, să încercăm să definim câteva variabile legate de instanță și clase și să testăm dacă au sau nu o valoare implicită:

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

6.2. Variabile locale

Variabilele locale trebuie inițializate înainte de utilizare , deoarece nu au o valoare implicită, iar compilatorul nu ne permite să folosim o valoare neinițializată.

De exemplu, următorul cod generează o eroare a compilatorului:

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

7. Cuvântul cheie final

Cuvântul cheie final aplicat unui câmp înseamnă că valoarea câmpului nu mai poate fi modificată după inițializare. În acest fel, putem defini constante în Java.

Let's add a constant to our User class:

private static final int YEAR = 2000;

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

8. Initializers in 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 offers two types of initializers, static and instance initializers. Let's see how we can use each of them.

8.1. Instance Initializers

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:

{ id = 0; }

8.2. Static Initialization Block

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:

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

9. Order of Initialization

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:

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

10. Object Life Cycle

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.

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.

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.

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

  • 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.

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

11. Other Methods for Creating Objects

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.

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:

@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.

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:

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

Acum putem folosi metoda clone () pentru a crea un nou obiect clonedUser care are aceleași valori de proprietate ca obiectul utilizator :

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

Putem folosi și clasa sun.misc.Unsafe pentru a aloca memorie pentru un obiect fără a apela un constructor:

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

12. Concluzie

În acest tutorial, am acoperit inițializarea câmpurilor în Java. Am descoperit diferite tipuri de date în Java și cum să le folosim. De asemenea, am analizat mai multe moduri de a crea obiecte în Java.

Implementarea completă a acestui tutorial poate fi găsită pe Github.