JavaRush /Blog Java /Random-ES /Constructores de clases. JavaJDK 1.5
articles
Nivel 15

Constructores de clases. JavaJDK 1.5

Publicado en el grupo Random-ES
Constructores de clases.  Java JDK 1.5 - 1

Información general sobre constructores.

КонструкторEs una estructura similar a un método, cuyo propósito es crear una instancia de una clase. Características del diseñador:
  • El nombre del constructor debe coincidir con el nombre de la clase (por convención, la primera letra está en mayúscula, normalmente un sustantivo);
  • Hay un constructor en cualquier clase. Incluso si no escribe uno, el compilador de Java creará un constructor predeterminado, que estará vacío y no hará nada más que llamar al constructor de la superclase.
  • Un constructor es similar a un método, pero no es un método, ni siquiera se considera miembro de la clase. Por lo tanto, no se puede heredar ni anular en una subclase;
  • Los constructores no se heredan;
  • Puede haber varios constructores en una clase. En este caso, se dice que los constructores están sobrecargados;
  • Si una clase no define un constructor, el compilador agrega automáticamente un constructor sin parámetros al código;
  • Un constructor no tiene un tipo de retorno; ni siquiera puede ser un tipo void; si se devuelve un tipo void, entonces ya no es un constructor sino un método, a pesar de la coincidencia con el nombre de la clase.
  • El operador está permitido en el constructor return, pero solo está vacío, sin ningún valor de retorno;
  • El constructor permite el uso de modificadores de acceso; puede establecer uno de los modificadores: public, protectedo privatesin modificador.
  • Un constructor no puede tener los modificadores abstract, final, o ;nativestaticsynchronized
  • La palabra clave thishace referencia a otro constructor de la misma clase. Si se usa, la llamada debe ser la primera línea del constructor;
  • La palabra clave superllama al constructor de la clase padre. Si se utiliza, la referencia debe ser la primera línea del constructor;
  • Si el constructor no realiza una llamada al superconstructor de la clase ancestral (con o sin argumentos), el compilador agrega automáticamente código para llamar al constructor de la clase ancestral sin argumentos;

Constructor predeterminado

Hay un constructor en cualquier clase. Incluso si no escribe uno, el compilador de Java creará un constructor predeterminado. Este constructor está vacío y no hace nada más que llamar al constructor de la superclase. Aquellos. Si tú escribes:
public class Example {}
entonces esto equivale a escribir:
public class Example
{
     Example()
     {
          super;
     }
}
En este caso, la clase antecesora no se especifica explícitamente y, de forma predeterminada, todas las clases Java heredan la clase, Objectpor lo que se llama al constructor de la clase Object. Si una clase define un constructor parametrizado, pero no hay un constructor sin parámetros sobrecargado, entonces llamar al constructor sin parámetros es un error. Sin embargo, en Java desde la versión 1.5, es posible utilizar constructores con argumentos de longitud variable. Y si hay un constructor que tiene un argumento de longitud variable, llamar al constructor predeterminado no será un error. No lo hará porque el argumento de longitud variable puede estar vacío. Por ejemplo, el siguiente ejemplo no se compilará, pero si descomentas el constructor con un argumento de longitud variable, se compilará y ejecutará correctamente y dará como resultado una línea de código en ejecución DefaultDemo dd = new DefaultDemo(); Se llamará al constructor DefaultDemo(int ... v). Naturalmente, en este caso es necesario utilizar JSDK 1.5. ArchivoDefaultDemo.java
class DefaultDemo
{
 DefaultDemo(String s)
 {
  System.out.print("DefaultDemo(String)");
 }
 /*
 DefaultDemo(int ... v)
 {
  System.out.println("DefaultDemo(int ...)");
 }
 */

 public static void main(String args[])
 {
  DefaultDemo dd = new DefaultDemo();
 }
}
El resultado de la salida del programa con el constructor descomentado:
DefaultDemo(int ...)
Sin embargo, en el caso común en el que la clase no define ningún constructor, será necesario llamar al constructor predeterminado (sin parámetros), ya que la sustitución del constructor predeterminado se produce automáticamente.

Creación de objetos y constructores.

Al crear un objeto, las siguientes acciones se realizan secuencialmente:
  • La clase de objeto se busca entre las clases ya utilizadas en el programa. Si no está allí, se busca en todos los catálogos y bibliotecas disponibles para el programa. Una vez que se descubre una clase en un directorio o biblioteca, los campos estáticos de la clase se crean e inicializan. Aquellos. Para cada clase, los campos estáticos se inicializan sólo una vez.
  • Se asigna memoria para el objeto.
  • Los campos de clase se están inicializando.
  • Se ejecuta el constructor de la clase.
  • Se forma un enlace al objeto creado e inicializado. Esta referencia es el valor de la expresión que crea el objeto. También se puede crear un objeto llamando a un método newInstance()de clase java.lang.Class. En este caso, se utiliza un constructor sin lista de parámetros.

Sobrecarga de constructores

Los constructores de la misma clase pueden tener el mismo nombre y firma diferente. Esta propiedad se llama combinación o sobrecarga. Si una clase tiene varios constructores, entonces hay una sobrecarga de constructores.

Constructores parametrizados

La firma de un constructor es el número y los tipos de parámetros, así como la secuencia de sus tipos en la lista de parámetros del constructor. El tipo de devolución no se tiene en cuenta. El constructor no devuelve ningún parámetro. Esta declaración explica, en cierto sentido, cómo Java distingue entre constructores o métodos sobrecargados. Java distingue los métodos sobrecargados no por su tipo de retorno, sino por el número, tipos y secuencia de tipos de parámetros de entrada. Un constructor ni siquiera puede devolver un tipo void, de lo contrario se convertirá en un método normal, aunque sea similar al nombre de la clase. El siguiente ejemplo demuestra esto. ArchivoVoidDemo.java
class VoidDemo
{
 /**
  * Это конструктор
  */
 VoidDemo()
 {
  System.out.println("Constructor");
 }

 /**
  * А это уже обычный метод, даже не смотря на сходство с
  * именем класса, поскольку имеется возвращаемый тип void
  */
 void VoidDemo()
 {
  System.out.println("Method");
 }

 public static void main(String s[])
 {
  VoidDemo m = new VoidDemo();
 }
}
Como resultado, el programa generará:
Constructor
Esto demuestra una vez más que un constructor es un método sin parámetros de retorno. Sin embargo, al constructor se le puede dar uno de tres modificadores public, privateo protected. Y el ejemplo ahora se verá así: ArchivoVoidDemo2.java
class VoidDemo2
{
 /**
  * Это конструктор
  */
 public VoidDemo2()
 {
  System.out.println("Constructor");
 }

 /**
  * А это уже обычный метод, даже не смотря на сходство с
  * именем класса, поскольку имеется возвращаемый тип void
  */
 private void VoidDemo2()
 {
  System.out.println("Method");
 }

 public static void main(String s[])
 {
  VoidDemo2 m = new VoidDemo2();
 }
}
Está permitido escribir un operador en un constructor return, pero sólo uno vacío, sin ningún valor de retorno. ArchivoReturnDemo.java
class ReturnDemo
{
 /**
  * В конструкторе допускается использование оператора
  * return без параметров.
  */
 public ReturnDemo()
 {
  System.out.println("Constructor");
  return;
 }

 public static void main(String s[])
 {
  ReturnDemo r = new ReturnDemo();
 }
}

Constructores parametrizados con argumentos de longitud variable.

Java SDK 1.5 introdujo una herramienta muy esperada: argumentos de longitud variable para constructores y métodos. Anteriormente, un número variable de documentos se procesaba de dos maneras inconvenientes. El primero de ellos fue diseñado para asegurar que el número máximo de argumentos se limite a un número pequeño y se conozca de antemano. En este caso, fue posible crear versiones sobrecargadas del método, una para cada versión de la lista de argumentos pasada al método. El segundo método está diseñado para algo desconocido de antemano y una gran cantidad de argumentos. En este caso, los argumentos se colocaron en una matriz y esta matriz se pasó al método. Los argumentos de longitud variable suelen estar involucrados en manipulaciones posteriores con inicializaciones de variables. Es conveniente reemplazar la ausencia de algunos de los argumentos esperados del constructor o del método con valores predeterminados. El argumento de longitud variable es una matriz y se trata como una matriz. Por ejemplo, el constructor de una clase Checkingcon un número variable de argumentos tendría este aspecto:
class Checking
{
 public Checking(int ... n)
 {
 }
}
La combinación de caracteres... le dice al compilador que se utilizará un número variable de argumentos y que estos argumentos se almacenarán en una matriz cuyo valor de referencia está contenido en la variable n. Se puede llamar al constructor con un número diferente de argumentos, incluido ningún argumento. Los argumentos se colocan automáticamente en una matriz y se pasan por n. Si no hay argumentos, la longitud de la matriz es 0. La lista de parámetros, junto con los argumentos de longitud variable, también puede incluir parámetros obligatorios. En este caso, un parámetro que contiene un número variable de argumentos debe ser necesariamente el último de la lista de parámetros. Por ejemplo:
class Checking
{
 public Checking(String s, int ... n)
 {
 }
}
Una limitación muy obvia se refiere al número de parámetros de longitud variable. Sólo debe haber un parámetro de longitud variable en la lista de parámetros. Dados dos parámetros de longitud variable, es imposible para el compilador determinar dónde termina un parámetro y comienza el otro. Por ejemplo:
class Checking
{
 public Checking(String s, int ... n, double ... d) //ОШИБКА!
 {
 }
}
Archivo Checking.java Por ejemplo, existen equipos capaces de reconocer las matrículas de los coches y recordar los números de las casillas de la zona por donde pasó cada uno de los coches durante el día. De la masa total de vehículos registrados es necesario seleccionar aquellos que durante el día visitaron dos plazas determinadas, digamos 22 y 15, según el mapa de la zona. Es bastante natural que un coche pueda visitar muchas plazas durante el día, o quizá sólo una. Evidentemente, el número de plazas visitadas está limitado por la velocidad física del coche. Creemos un pequeño programa donde el constructor de la clase tomará como argumentos el número de coche como parámetro obligatorio y el número de plazas visitadas del área, cuyo número puede ser variable. El constructor comprobará si un coche ha aparecido en dos cuadrados y, si es así, mostrará su número en la pantalla.

Pasar parámetros al constructor.

Existen principalmente dos tipos de parámetros en los lenguajes de programación:
  • tipos básicos (primitivos);
  • referencias a objetos.
El término llamada por valor significa que el constructor recibe el valor que le pasa el módulo que llama. Por el contrario, llamar por referencia significa que el constructor recibe la dirección de la variable del llamante. Java usa llamadas solo por valor. Por valor de parámetro y por valor de enlace de parámetro. Java no utiliza llamadas por referencia para objetos (aunque muchos programadores y autores de algunos libros lo afirman). Al pasar objetos a Java, los parámetros no se pasan por referencia , sino por el valor de la referencia del objeto . En cualquier caso, el constructor recibe copias de los valores de todos los parámetros. El constructor no puede hacer con sus parámetros de entrada:
  • el constructor no puede cambiar los valores de los parámetros de entrada de los tipos principales (primitivos);
  • el constructor no puede cambiar las referencias de los parámetros de entrada;
  • el constructor no puede reasignar referencias de parámetros de entrada a nuevos objetos.
El constructor puede hacer con sus parámetros de entrada:
  • cambiar el estado del objeto pasado como parámetro de entrada.
El siguiente ejemplo demuestra que en Java, los parámetros de entrada a un constructor se pasan por valor de referencia de objeto. Este ejemplo también refleja que el constructor no puede cambiar las referencias de los parámetros de entrada, sino que en realidad cambia las referencias de las copias de los parámetros de entrada. ArchivoEmpoyee.java
class Employee
{
 Employee(String x, String y)
 {
  String temp = x;
  x = y;
  y = temp;
 }
 public static void main(String args[])
 {
  String name1 = new String("Alice");
  String name2 = new String("Mary");
  Employee a = new Employee(name1, name2);
  System.out.println("name1="+name1);
  System.out.println("name2="+name2);
 }
}
La salida del programa es:
name1=Alice
name2=Mary
Si Java usara llamada por referencia para pasar objetos como parámetros, el constructor intercambiaría name1y en este ejemplo name2. En realidad, el constructor no intercambiará las referencias a objetos almacenadas en las variables name1y name2. Esto sugiere que los parámetros del constructor se inicializan con copias de estas referencias. Luego el constructor intercambia las copias. Cuando el constructor completa su trabajo, las variables xey se destruyen y las variables originales name1continúan name2refiriéndose a los objetos anteriores.

Cambiar los parámetros pasados ​​al constructor.

El constructor no puede modificar los parámetros pasados ​​de tipos básicos. Sin embargo, el constructor puede modificar el estado del objeto pasado como parámetro. Por ejemplo, considere el siguiente programa: ArchivoSalary1.java
class Salary1
{
 Salary1(int x)
 {
  x = x * 3;
  System.out.println("x="+x);
 }
 public static void main(String args[])
 {
  int value = 1000;
  Salary1 s1 = new Salary1(value);
  System.out.println("value="+value);
 }
}
La salida del programa es:
x=3000
value=1000
Obviamente, este método no cambiará el parámetro de tipo principal. Por lo tanto, después de llamar al constructor, el valor de la variable valuesigue siendo igual a 1000. Básicamente suceden tres cosas:
  1. La variable xse inicializa con una copia del valor del parámetro value(es decir, un número 1000).
  2. El valor de la variable xse triplica: ahora es igual a 3000. Sin embargo, el valor de la variable valuesigue siendo igual a 1000.
  3. El constructor termina y la variable xya no se utiliza.
En el siguiente ejemplo, el salario del empleado se triplica con éxito porque el valor de una referencia de objeto se pasa como parámetro al método. ArchivoSalary2.java
class Salary2
{
 int value = 1000;
 Salary2()
 {
 }
 Salary2(Salary2 x)
 {
  x.value = x.value * 3;
 }
 public static void main(String args[])
 {
  Salary2 s1 = new Salary2();
  Salary2 s2 = new Salary2(s1);
  System.out.println("s1.value=" +s1.value);
  System.out.println("s2.value="+s2.value);
 }
}
La salida del programa es:
s1.value=3000
s2.value=1000
El valor de la referencia del objeto se utiliza como parámetro. Al ejecutar la línea Salary2 s2 = new Salary2(s1); al constructor Salary2(Salary x)se le pasará el valor de una referencia al objeto variable s1, y el constructor efectivamente triplicará el salario s1.value, ya que incluso la copia (Salary x)creada dentro del constructor apunta al objeto variable s1.

Constructores parametrizados por primitivas.

Si los parámetros de un constructor sobrecargado utilizan una primitiva que se puede limitar (por ejemplo int <- double), entonces es posible llamar a un método con un valor limitado, a pesar de que no hay ningún método sobrecargado con dicho parámetro. Por ejemplo: archivoPrimitive.java
class Primitive
{
 Primitive(double d)
 {
  d = d + 10;
  System.out.println("d="+d);
 }
 public static void main(String args[])
 {
  int i = 20;
  Primitive s1 = new Primitive(i);
 }
}
La salida del programa es:
d=30.0
A pesar de que la clase Primitiveno tiene un constructor que tenga un parámetro de tipo int, un constructor con un parámetro de entrada funcionará double. Antes de llamar al constructor, la variable ise expandirá de un tipo inta otro double. La opción contraria, cuando la variable isería de tipo doubley el constructor solo tendría un parámetro int, en esta situación daría lugar a un error de compilación.

Llamada de constructor y operador.new

El constructor siempre es llamado por el operador new. Cuando se llama a un constructor con el operador new, el constructor siempre genera una referencia a un nuevo objeto. Es imposible forzar al constructor a formar una referencia a un objeto ya existente en lugar de una referencia a un objeto nuevo, excepto sustituyendo el objeto que se está deserializar. Y con el nuevo operador, en lugar de una referencia a un nuevo objeto, es imposible formar una referencia a un objeto ya existente. Por ejemplo: archivoSalary3.java
class Salary3
{
 int value = 1000;
 Salary3()
 {
 }
 Salary3(Salary3 x)
 {
  x.value = x.value * 3;
 }
 public static void main(String args[])
 {
  Salary3 s1 = new Salary3();
  System.out.println("First object creation: "+s1.value);

  Salary3 s2 = new Salary3(s1);
  System.out.println("Second object creation: "+s2.value);
  System.out.println("What's happend with first object?:"+s1.value);

  Salary3 s3 = new Salary3(s1);
  System.out.println("Third object creation: "+s3.value);
  System.out.println("What's happend with first object?:"+s1.value);
 }
}
La salida del programa es:
First object creation: 1000
Second object creation: 1000
What's happend with first object?: 3000
Third object creation: 1000
What's happend with first object?: 9000
Primero, usando la línea Salary3 s1 = new Salary3(); se crea un nuevo objeto. A continuación, si usa la línea Salary3 s2 = new Salary3(s1); o cuerdas Salary3 s3 = new Salary3(s1); sería posible crear un enlace a un objeto ya existente, luego s1.value s2.valuealmacenarían s3.valueel mismo valor 1000. En realidad en la línea Salary3 s2 = new Salary3(s1); Se creará un nuevo objeto para la variable s2y el estado del objeto para la variable cambiará s1al pasar su valor de referencia al objeto en el parámetro constructor. Esto se puede verificar mediante los resultados de salida. Y al ejecutar la línea Salary3 s3 = new Salary3(s1); Se creará un NUEVO objeto para la variable s3y el estado del objeto para la variable cambiará nuevamente s1.

Constructores y bloques de inicialización, secuencia de acciones al llamar a un constructor

La sección Creación de un objeto y constructores enumera las acciones generales que se realizan al crear un objeto. Entre ellos se encuentran los procesos de inicialización de campos de clase y elaboración del constructor de clases, que a su vez también tienen un orden interno:
  1. Todos los campos de datos se inicializan con sus valores predeterminados (0, falso o nulo).
  2. Todos los inicializadores de campo y bloques de inicialización se ejecutan en el orden en que aparecen en la declaración de clase.
  3. Si se llama a otro constructor en la primera línea de un constructor, entonces se ejecuta el constructor llamado.
  4. Se ejecuta el cuerpo del constructor.
El constructor está relacionado con la inicialización porque en Java hay tres formas de inicializar un campo en una clase:
  • asignar un valor en la declaración;
  • asignar valores en el bloque de inicialización;
  • establece su valor en el constructor.
Naturalmente, es necesario organizar el código de inicialización para que sea fácil de entender. La siguiente clase se da como ejemplo:
class Initialization
{
 int i;
 short z = 10;
 static int x;
 static float y;
 static
 {
  x = 2000;
  y = 3.141;
 }
 Initialization()
 {
  System.out.println("i="+i);
  System.out.println("z="+z);
  z = 20;
  System.out.println("z="+z);
 }
}
En el ejemplo anterior, las variables se inicializan en el siguiente orden: las variables estáticas se inicializan primero xcon ylos valores predeterminados. A continuación, se ejecuta el bloque de inicialización estática. Luego, la variable se inicializa ial valor predeterminado y se inicializa la variable z. A continuación, el diseñador se pone a trabajar. La llamada a constructores de clases no debería depender del orden en que se declaran los campos. Esto puede dar lugar a errores.

Constructores y herencia

Los constructores no se heredan. Por ejemplo:
public class Example
{
 Example()
 {
 }
 public void sayHi()
 {
  system.out.println("Hi");
 }
}

public class SubClass extends Example
{
}
La clase SubClasshereda automáticamente el método sayHi()definido en la clase principal. Al mismo tiempo, el constructor Example()de la clase padre no lo hereda su descendiente SubClass.

Palabra clave thisen constructores

Los constructores se utilizan thispara hacer referencia a otro constructor de la misma clase, pero con una lista diferente de parámetros. Si el constructor usa la palabra clave this, entonces debe estar en la primera línea; ignorar esta regla resultará en un error del compilador. Por ejemplo: archivoThisDemo.java
public class ThisDemo
{
 String name;
 ThisDemo(String s)
 {
  name = s;
     System.out.println(name);
 }
 ThisDemo()
 {
  this("John");
 }
 public static void main(String args[])
 {
  ThisDemo td1 = new ThisDemo("Mary");
  ThisDemo td2 = new ThisDemo();
 }
}
La salida del programa es:
Mary
John
En este ejemplo hay dos constructores. El primero recibe un argumento de cadena. El segundo no recibe ningún argumento, simplemente llama al primer constructor usando el nombre predeterminado "John". Por lo tanto, puede utilizar constructores para inicializar los valores de los campos de forma explícita y predeterminada, lo que suele ser necesario en los programas.

Palabra clave superen constructores

Los constructores se utilizan superpara llamar a un constructor de superclase. Si el constructor usa super, entonces esta llamada debe estar en la primera línea; de lo contrario, el compilador arrojará un error. A continuación se muestra un ejemplo: ArchivoSuperClassDemo.java
public class SuperClassDemo
{
 SuperClassDemo()
 {
 }
}

class Child extends SuperClassDemo
{
 Child()
 {
  super();
 }
}
En este ejemplo simple, el constructor Child()contiene una llamada super()que crea una instancia de la clase SuperClassDemo, además de la clase Child. Debido a superque debe ser la primera declaración ejecutada en un constructor de subclase, este orden es siempre el mismo y no depende de si super(). Si no se utiliza, primero se ejecutará el constructor predeterminado (sin parámetros) de cada superclase, comenzando con la clase base. El siguiente programa demuestra cuándo se ejecutan los constructores. ArchivoCall.java
//Создать суперкласс A
class A
{
 A()
 {
  System.out.println("Inside A constructor.");
 }
}

//Создать подкласс B, расширяющий класс A
class B extends A
{
 B()
 {
  System.out.println("Inside B constructor.");
 }
}

//Создать класс (C), расширяющий класс В
class C extends B
{
 C()
 {
  System.out.println("Inside C constructor.");
 }
}

class Call
{
 public static void main(String args[])
 {
  C c = new C();
 }
}
Salida de este programa:
Inside A constructor.
Inside B constructor.
Inside C constructor.
Los constructores se llaman en orden de subordinación de clase. Esto tiene cierto sentido. Dado que la superclase no tiene conocimiento de ninguna subclase, cualquier inicialización que necesite realizar es independiente. Si es posible, debe preceder a cualquier inicialización realizada por la subclase. Por eso hay que hacerlo primero.

Constructores personalizables

El mecanismo de identificación de tipos en tiempo de ejecución es uno de los poderosos principios centrales del lenguaje Java que implementa el polimorfismo. Sin embargo, dicho mecanismo no protege al desarrollador de la conversión de tipos incompatibles en algunos casos. El caso más común es la manipulación de un grupo de objetos, cuyos distintos tipos se desconocen de antemano y se determinan en tiempo de ejecución. Dado que los errores asociados con la incompatibilidad de tipos sólo pueden aparecer en la etapa de ejecución, esto hace que sean difíciles de encontrar y eliminar. La introducción de tipos personalizados en Java 2 5.0 traslada algunos de estos errores del tiempo de ejecución al tiempo de compilación y proporciona parte de la seguridad de tipos que falta. No es necesario realizar una conversión de tipos explícita al pasar de un tipo Objecta un tipo concreto. Debe tenerse en cuenta que las herramientas de personalización de tipos funcionan sólo con objetos y no se aplican a tipos de datos primitivos que se encuentran fuera del árbol de herencia de clases. Con tipos personalizados, todos los castings se realizan automáticamente y detrás de escena. Esto le permite protegerse contra discrepancias de tipos y reutilizar el código con mucha más frecuencia. Se pueden utilizar tipos personalizados en los constructores. Los constructores pueden ser personalizados incluso si su clase no es un tipo personalizado. Por ejemplo:
class GenConstructor
{
 private double val;
 <T extends Number> GenConstructor(T arg)
 {
   val = arg.doubleValue();
 }

 void printValue()
 {
  System.out.println("val: "+val);
 }
}

class GenConstructorDemo
{
 public static void main(String args[])
 {
  GenConstructor gc1 = new GenConstructor(100);
  GenConstructor gc2 = new GenConstructor(123.5F);

  gc1.printValue();
  gc2.printValue();
 }
}
Debido a que el constructor GenConstructorespecifica un parámetro de tipo personalizado que debe ser una clase derivada de class Number, se puede llamar desde cualquier
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION