1. Propiedades: getters y setters

Cuando docenas de programadores están desarrollando un proyecto grande al mismo tiempo, a menudo surgen problemas si manejan los datos almacenados en los campos de clase de manera diferente.

Tal vez la gente no estudie la documentación de la clase en detalle, o tal vez no describa todos los casos. Como resultado, hay situaciones frecuentes en las que los datos internos de un objeto pueden "corromperse", lo que hace que el objeto no sea válido.

Para evitar estas situaciones, se acostumbra hacer que todos los campos de clase sean privados en Java . Solo los métodos de la clase pueden modificar las variables de la clase. Ningún método de otras clases puede acceder directamente a las variables.

Si desea que otras clases puedan obtener o cambiar los datos dentro de los objetos de su clase, debe agregar dos métodos a su clase: un método de obtención y un método de configuración. Ejemplo:

Código Nota
class Person
{
   private String name;

   public Person(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }
}


privatecampo de nombre



Inicialización del campo a través del constructor


getName()— Este método devuelve el valor del campo de nombre




setName()— Este método cambia el valor del campo de nombre

Ninguna otra clase puede cambiar directamente el valor del campo de nombre. Si alguien necesita obtener el valor del campo de nombre, tendrá que llamar al getName() método en un Personobjeto. Si algún código quiere cambiar el valor del campo de nombre, deberá llamar al setName() método en un Personobjeto.

El getName()método también se denomina " getter for the name field", y el setName()método se denomina " setter for the name field".

Este es un enfoque muy común. En el 80-90% de todo el código Java, nunca verá variables públicas en una clase. En su lugar, se declararán private(o protected), y cada variable tendrá captadores y definidores públicos.

Este enfoque hace que el código sea más largo, pero más confiable.

Acceder a una variable de clase directamente es como girar tu coche a través de dobles líneas amarillas : es más fácil y rápido, pero si todo el mundo lo hace, entonces las cosas empeoran para todos.

Digamos que desea crear una clase que describa un punto ( x, y). Así es como lo haría un programador novato:

class Point
{
   public int x;
   public int y;
}

Así es como lo haría un programador Java experimentado:

Código
class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y;
   }
}

¿El código es más largo? Indudablemente.

Pero puede agregar validación de parámetros a getters y setters. Por ejemplo, puede asegurarse de que xy ysean siempre mayores que cero (o no menores que cero). Ejemplo:

Código Nota
class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x < 0 ? 0 : x;
      this.y = y < 0 ? 0 : y;
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x < 0 ?  0 : x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y < 0 ? 0 : y;
   }
}


2. Duración del objeto

Ya sabes que los objetos se crean usando el newoperador, pero ¿cómo se eliminan los objetos? No existen para siempre. No hay suficiente memoria para eso.

En muchos lenguajes de programación, como C++, existe un deleteoperador especial para eliminar objetos. Pero, ¿cómo funciona esto en Java?

En Java, todo está organizado de forma un poco diferente. Java no tiene operador de eliminación. ¿Significa esto que los objetos no se eliminan en Java? No, se eliminan, por supuesto. De lo contrario, las aplicaciones Java se quedarían rápidamente sin memoria y no se hablaría de programas que se ejecutan sin interrupción durante meses.

En Java, la eliminación de objetos está completamente automatizada. La propia máquina Java maneja la eliminación de objetos. Este proceso se llama recolección de basura, y el mecanismo que recolecta basura se llama recolector de basura ( GC ).

Entonces, ¿cómo sabe la máquina Java cuándo eliminar un objeto?

El recolector de basura divide todos los objetos en "alcanzables" e "inalcanzables". Si hay al menos una referencia a un objeto, se considera alcanzable. Si no hay ninguna variable que haga referencia a un objeto, el objeto se considera inalcanzable y se declara basura, lo que significa que se puede eliminar.

En Java, no puede crear una referencia a un objeto existente; solo puede asignar referencias que ya tiene. Si borramos todas las referencias a un objeto, entonces se pierde para siempre.

Referencias circulares

Esa lógica suena genial hasta que nos encontramos con un simple contraejemplo: supongamos que tenemos dos objetos que se referencian entre sí (almacenan referencias entre sí). Ningún otro objeto almacena referencias a estos objetos.

No se puede acceder a estos objetos desde el código, pero aún se hace referencia a ellos.

Esta es la razón por la cual el recolector de basura divide los objetos en alcanzables e inalcanzables en lugar de "referenciados" y "no referenciados".

Objetos alcanzables

Primero, los objetos que están 100% activos se agregan a la lista alcanzable. Por ejemplo, el subproceso actual ( Thread.current()) o la consola InputStream ( System.in).

Luego, la lista de objetos accesibles se expande para incluir objetos a los que hace referencia el conjunto inicial de objetos accesibles. Luego se expande nuevamente para incluir objetos a los que hace referencia este conjunto ampliado, y así sucesivamente.

Eso significa que si hay algunos objetos que solo se refieren entre sí, pero no hay forma de llegar a ellos desde los objetos accesibles, esos objetos se considerarán basura y se eliminarán.


3. Recolección de basura

Fragmentación de la memoria

Otro punto importante relacionado con la eliminación de objetos es la fragmentación de la memoria. Si crea y elimina objetos constantemente, pronto la memoria estará muy fragmentada: las áreas de memoria ocupada se intercalarán con áreas de memoria desocupada.

Como resultado, podemos llegar fácilmente a una situación en la que no podemos crear un objeto grande (por ejemplo, una matriz con un millón de elementos), porque no hay una gran cantidad de memoria libre. En otras palabras, puede haber memoria libre, incluso mucha, pero puede que no haya un gran bloque contiguo de memoria libre.

Optimización de memoria (desfragmentación)

La máquina Java resuelve este problema de una manera específica. Se ve algo como esto:

La memoria se divide en dos partes. Todos los objetos se crean (y eliminan) en solo la mitad de la memoria. Cuando llega el momento de limpiar los agujeros en la memoria, todos los objetos de la primera mitad se copian en la segunda mitad. Pero se copian uno al lado del otro para que no haya agujeros.

El proceso se ve más o menos así:

Paso 1: Después de crear objetos

Recolección de basura en Java

Paso 2: Aparición de "agujeros"

Recolección de basura en Java 2

Paso 3: Eliminación de "agujeros"

Recolección de basura en Java 3

Y es por eso que no necesita eliminar objetos. La máquina Java simplemente copia todos los objetos accesibles a una nueva ubicación y libera toda el área de memoria donde solían almacenarse los objetos.