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
, protected
o private
sin modificador.
- Un constructor no puede tener los modificadores
abstract
, final
, o ;native
static
synchronized
- La palabra clave
this
hace referencia a otro constructor de la misma clase. Si se usa, la llamada debe ser la primera línea del constructor;
- La palabra clave
super
llama 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
super
constructor 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,
Object
por 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. Archivo
DefaultDemo.java
class DefaultDemo
{
DefaultDemo(String s)
{
System.out.print("DefaultDemo(String)");
}
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. Archivo
VoidDemo.java
class VoidDemo
{
VoidDemo()
{
System.out.println("Constructor");
}
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
,
private
o
protected
. Y el ejemplo ahora se verá así: Archivo
VoidDemo2.java
class VoidDemo2
{
public VoidDemo2()
{
System.out.println("Constructor");
}
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. Archivo
ReturnDemo.java
class ReturnDemo
{
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
Checking
con 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. Archivo
Empoyee.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
name1
y en este ejemplo
name2
. En realidad, el constructor no intercambiará las referencias a objetos almacenadas en las variables
name1
y
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
name1
continúan
name2
refirié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: Archivo
Salary1.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
value
sigue siendo igual a
1000
. Básicamente suceden tres cosas:
- La variable
x
se inicializa con una copia del valor del parámetro value
(es decir, un número 1000
).
- El valor de la variable
x
se triplica: ahora es igual a 3000
. Sin embargo, el valor de la variable value
sigue siendo igual a 1000
.
- El constructor termina y la variable
x
ya 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. Archivo
Salary2.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: archivo
Primitive.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
Primitive
no 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
i
se expandirá de un tipo
int
a otro
double
. La opción contraria, cuando la variable
i
sería de tipo
double
y 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: archivo
Salary3.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.value
almacenarían
s3.value
el mismo valor
1000
. En realidad en la línea
Salary3 s2 = new Salary3(s1)
; Se creará un nuevo objeto para la variable
s2
y el estado del objeto para la variable cambiará
s1
al 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
s3
y 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:
- Todos los campos de datos se inicializan con sus valores predeterminados (0, falso o nulo).
- Todos los inicializadores de campo y bloques de inicialización se ejecutan en el orden en que aparecen en la declaración de clase.
- Si se llama a otro constructor en la primera línea de un constructor, entonces se ejecuta el constructor llamado.
- 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
x
con
y
los valores predeterminados. A continuación, se ejecuta el bloque de inicialización estática. Luego, la variable se inicializa
i
al 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
SubClass
hereda 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 this
en constructores
Los constructores se utilizan
this
para 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: archivo
ThisDemo.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 super
en constructores
Los constructores se utilizan
super
para 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: Archivo
SuperClassDemo.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
super
que 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. Archivo
Call.java
class A
{
A()
{
System.out.println("Inside A constructor.");
}
}
class B extends A
{
B()
{
System.out.println("Inside B constructor.");
}
}
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
Object
a 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
GenConstructor
especifica un parámetro de tipo personalizado que debe ser una clase derivada de class
Number
, se puede llamar desde cualquier
GO TO FULL VERSION