JavaRush /Blogue Java /Random-PT /Construtores de classe. JavaJDK 1.5
articles
Nível 15

Construtores de classe. JavaJDK 1.5

Publicado no grupo Random-PT
Construtores de classe.  JavaJDK 1.5-1

Informações gerais sobre construtores

Конструкторé uma estrutura semelhante a um método, cujo objetivo é criar uma instância de uma classe. Características do designer:
  • O nome do construtor deve corresponder ao nome da classe (por convenção, a primeira letra é maiúscula, geralmente um substantivo);
  • Existe um construtor em qualquer classe. Mesmo que você não escreva um, o compilador Java criará um construtor padrão, que estará vazio e não fará nada além de chamar o construtor da superclasse.
  • Um construtor é semelhante a um método, mas não é um método, nem mesmo é considerado membro da classe. Portanto, não pode ser herdado ou substituído em uma subclasse;
  • Os construtores não são herdados;
  • Pode haver vários construtores em uma classe. Neste caso, diz-se que os construtores estão sobrecarregados;
  • Se uma classe não definir um construtor, o compilador adicionará automaticamente um construtor sem parâmetros ao código;
  • Um construtor não tem um tipo de retorno; nem pode ser um tipo void; se um tipo for retornado void, então ele não é mais um construtor, mas um método, apesar da coincidência com o nome da classe.
  • O operador é permitido no construtor return, mas apenas vazio, sem nenhum valor de retorno;
  • O construtor permite o uso de modificadores de acesso; você pode definir um dos modificadores: public, protectedou privatesem modificador.
  • Um construtor não pode ter os modificadores abstract, final, ou ;nativestaticsynchronized
  • A palavra-chave thisrefere-se a outro construtor na mesma classe. Se usado, a chamada deve ser a primeira linha do construtor;
  • A palavra-chave superchama o construtor da classe pai. Se utilizado, a referência a ele deverá ser a primeira linha do construtor;
  • Se o construtor não fizer uma chamada ao superconstrutor da classe ancestral (com ou sem argumentos), o compilador adicionará automaticamente código para chamar o construtor da classe ancestral sem argumentos;

Construtor padrão

Existe um construtor em qualquer classe. Mesmo que você não escreva um, o compilador Java criará um construtor padrão. Este construtor está vazio e não faz nada além de chamar o construtor da superclasse. Aqueles. se você escrever:
public class Example {}
então isso é equivalente a escrever:
public class Example
{
     Example()
     {
          super;
     }
}
Nesse caso, a classe ancestral não é especificada explicitamente e, por padrão, todas as classes Java herdam a classe, Objectportanto, o construtor da classe é chamado Object. Se uma classe define um construtor com parâmetros, mas não há nenhum construtor sobrecarregado sem parâmetros, então chamar o construtor sem parâmetros é um erro. Porém, em Java desde a versão 1.5, é possível usar construtores com argumentos de comprimento variável. E se houver um construtor que tenha um argumento de comprimento variável, chamar o construtor padrão não será um erro. Não acontecerá porque o argumento de comprimento variável pode estar vazio. Por exemplo, o exemplo a seguir não será compilado, mas se você descomentar o construtor com um argumento de comprimento variável, ele será compilado e executado com êxito e resultará em uma linha de código em execução DefaultDemo dd = new DefaultDemo(); o construtor será chamado DefaultDemo(int ... v). Naturalmente, neste caso é necessário utilizar JSDK 1.5. ArquivoDefaultDemo.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();
 }
}
O resultado da saída do programa com o construtor não comentado:
DefaultDemo(int ...)
Porém, no caso comum em que a classe não define nenhum construtor, será necessário chamar o construtor padrão (sem parâmetros), uma vez que a substituição do construtor padrão ocorre automaticamente.

Criação de objetos e construtores

Ao criar um objeto, as seguintes ações são executadas sequencialmente:
  • A classe do objeto é pesquisada entre as classes já utilizadas no programa. Caso não esteja, é pesquisado em todos os catálogos e bibliotecas disponíveis para o programa. Depois que uma classe é descoberta em um diretório ou biblioteca, os campos estáticos da classe são criados e inicializados. Aqueles. Para cada classe, os campos estáticos são inicializados apenas uma vez.
  • A memória é alocada para o objeto.
  • Os campos da classe estão sendo inicializados.
  • O construtor da classe é executado.
  • Um link para o objeto criado e inicializado é formado. Esta referência é o valor da expressão que cria o objeto. Um objeto também pode ser criado chamando um método newInstance()de classe java.lang.Class. Neste caso, é utilizado um construtor sem lista de parâmetros.

Sobrecarregando construtores

Construtores da mesma classe podem ter o mesmo nome e assinaturas diferentes. Esta propriedade é chamada de combinação ou sobrecarga. Se uma classe tiver vários construtores, a sobrecarga do construtor estará presente.

Construtores parametrizados

A assinatura de um construtor é o número e os tipos de parâmetros, bem como a sequência de seus tipos na lista de parâmetros do construtor. O tipo de retorno não é levado em consideração. O construtor não retorna nenhum parâmetro. Esta declaração explica, de certa forma, como Java distingue entre construtores ou métodos sobrecarregados. Java distingue métodos sobrecarregados não pelo tipo de retorno, mas pelo número, tipos e sequência de tipos de parâmetros de entrada. Um construtor não pode nem retornar um type void, caso contrário ele se transformará em um método regular, mesmo sendo semelhante ao nome da classe. O exemplo a seguir demonstra isso. ArquivoVoidDemo.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, o programa produzirá:
Constructor
Isso prova mais uma vez que um construtor é um método sem parâmetros de retorno. No entanto, o construtor pode receber um dos três modificadores public, privateou protected. E o exemplo agora ficará assim: ArquivoVoidDemo2.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();
 }
}
É permitido escrever um operador em um construtor return, mas apenas vazio, sem nenhum valor de retorno. ArquivoReturnDemo.java
class ReturnDemo
{
 /**
  * В конструкторе допускается использование оператора
  * return без параметров.
  */
 public ReturnDemo()
 {
  System.out.println("Constructor");
  return;
 }

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

Construtores parametrizados com argumentos de comprimento variável

Java SDK 1.5 introduziu uma ferramenta há muito esperada - argumentos de comprimento variável para construtores e métodos. Anteriormente, um número variável de documentos era processado de duas maneiras inconvenientes. O primeiro deles foi concebido para garantir que o número máximo de argumentos seja limitado a um pequeno número e seja conhecido antecipadamente. Neste caso foi possível criar versões sobrecarregadas do método, uma para cada versão da lista de argumentos passada ao método. O segundo método é projetado para algo desconhecido antecipadamente e um grande número de argumentos. Neste caso, os argumentos foram colocados em um array, e esse array foi passado para o método. Argumentos de comprimento variável estão mais frequentemente envolvidos em manipulações subsequentes com inicializações de variáveis. É conveniente substituir a ausência de alguns dos argumentos esperados do construtor ou do método por valores padrão. O argumento de comprimento variável é uma matriz e é tratado como uma matriz. Por exemplo, o construtor de uma classe Checkingcom um número variável de argumentos ficaria assim:
class Checking
{
 public Checking(int ... n)
 {
 }
}
A combinação de caracteres ... informa ao compilador que um número variável de argumentos será usado e que esses argumentos serão armazenados em um array cujo valor de referência está contido na variável n. O construtor pode ser chamado com um número diferente de argumentos, incluindo nenhum argumento. Os argumentos são automaticamente colocados em um array e passados ​​por n. Se não houver argumentos, o comprimento da matriz será 0. A lista de parâmetros, juntamente com argumentos de comprimento variável, também pode incluir parâmetros obrigatórios. Neste caso, um parâmetro contendo um número variável de argumentos deve ser o último da lista de parâmetros. Por exemplo:
class Checking
{
 public Checking(String s, int ... n)
 {
 }
}
Uma limitação muito óbvia diz respeito ao número de parâmetros de comprimento variável. Deve haver apenas um parâmetro de comprimento variável na lista de parâmetros. Dados dois parâmetros de comprimento variável, é impossível para o compilador determinar onde um parâmetro termina e o outro começa. Por exemplo:
class Checking
{
 public Checking(String s, int ... n, double ... d) //ОШИБКА!
 {
 }
}
Arquivo Checking.java Por exemplo, existem equipamentos capazes de reconhecer placas de automóveis e lembrar os números das praças da área por onde cada um dos carros passou durante o dia. É necessário selecionar da massa total de carros registrados aqueles que durante o dia visitaram duas praças determinadas, digamos 22 e 15, de acordo com o mapa da área. É bastante natural que um carro possa visitar muitas praças durante o dia, ou talvez apenas uma. Obviamente, o número de praças visitadas é limitado pela velocidade física do carro. Vamos criar um pequeno programa onde o construtor da classe tomará como argumentos o número do carro como parâmetro obrigatório e os números de quadrados visitados da área, cujo número pode ser variável. O construtor verificará se um carro apareceu em dois quadrados; se sim, exibirá seu número na tela.

Passando parâmetros para o construtor

Existem basicamente dois tipos de parâmetros em linguagens de programação:
  • tipos básicos (primitivos);
  • referências a objetos.
O termo chamada por valor significa que o construtor recebe o valor passado a ele pelo módulo de chamada. Por outro lado, chamar por referência significa que o construtor recebe o endereço da variável do chamador. Java usa chamada apenas por valor. Por valor de parâmetro e por valor de link de parâmetro. Java não usa chamada por referência para objetos (embora muitos programadores e autores de alguns livros afirmem isso). Ao passar objetos para Java, os parâmetros são passados ​​não por referência , mas pelo valor da referência do objeto ! Em ambos os casos, o construtor recebe cópias dos valores de todos os parâmetros. O construtor não pode fazer nada com seus parâmetros de entrada:
  • o construtor não pode alterar os valores dos parâmetros de entrada dos tipos principais (primitivos);
  • o construtor não pode alterar as referências dos parâmetros de entrada;
  • o construtor não pode reatribuir referências de parâmetros de entrada a novos objetos.
O construtor pode fazer isso com seus parâmetros de entrada:
  • alterar o estado do objeto passado como parâmetro de entrada.
O exemplo a seguir prova que em Java, os parâmetros de entrada para um construtor são passados ​​pelo valor de referência do objeto. Este exemplo também reflete que o construtor não pode alterar as referências dos parâmetros de entrada, mas na verdade altera as referências das cópias dos parâmetros de entrada. ArquivoEmpoyee.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);
 }
}
A saída do programa é:
name1=Alice
name2=Mary
Se Java usasse chamada por referência para passar objetos como parâmetros, o construtor trocaria name1e neste exemplo name2. O construtor não irá realmente trocar as referências de objetos armazenadas nas variáveis name1​​e name2. Isto sugere que os parâmetros do construtor são inicializados com cópias destas referências. Então o construtor troca as cópias. Quando o construtor conclui seu trabalho, as variáveis ​​x e y são destruídas e as variáveis ​​originais name1continuam name2a se referir aos objetos anteriores.

Alterando os parâmetros passados ​​ao construtor.

O construtor não pode modificar os parâmetros passados ​​de tipos básicos. Porém, o construtor pode modificar o estado do objeto passado como parâmetro. Por exemplo, considere o seguinte programa: ArquivoSalary1.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);
 }
}
A saída do programa é:
x=3000
value=1000
Obviamente, este método não alterará o parâmetro de tipo principal. Portanto, após chamar o construtor, o valor da variável valuepermanece igual a 1000. Essencialmente três coisas acontecem:
  1. A variável xé inicializada com uma cópia do valor do parâmetro value(ou seja, um número 1000).
  2. O valor da variável xé triplicado – agora é igual a 3000. No entanto, o valor da variável valuepermanece igual a 1000.
  3. O construtor termina e a variável xnão é mais usada.
No exemplo a seguir, o salário do funcionário é triplicado com sucesso porque o valor de uma referência de objeto é passado como parâmetro para o método. ArquivoSalary2.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);
 }
}
A saída do programa é:
s1.value=3000
s2.value=1000
O valor da referência do objeto é usado como parâmetro. Ao executar a linha Salary2 s2 = new Salary2(s1); o construtor Salary2(Salary x)receberá o valor de uma referência ao objeto variável s1, e o construtor efetivamente triplicará o salário de s1.value, já que até mesmo a cópia (Salary x)criada dentro do construtor aponta para o objeto variável s1.

Construtores parametrizados por primitivas.

Se os parâmetros de um construtor sobrecarregado usam um primitivo que pode ser reduzido (por exemplo int <- double), então é possível chamar um método com um valor restrito, apesar do fato de não haver nenhum método sobrecarregado com tal parâmetro. Por exemplo: ArquivoPrimitive.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);
 }
}
A saída do programa é:
d=30.0
Apesar de a classe Primitivenão possuir um construtor que possua um parâmetro de tipo int, um construtor com um parâmetro de entrada funcionará double. Antes de o construtor ser chamado, a variável iserá expandida de tipo intpara tipo double. A opção oposta, quando a variável iseria do tipo double, e o construtor teria apenas um parâmetro int, nesta situação levaria a um erro de compilação.

Chamada e operadora do construtornew

O construtor é sempre chamado pelo operador new. Quando um construtor é chamado com o operador new, o construtor sempre gera uma referência a um novo objeto. É impossível forçar o construtor a formar uma referência a um objeto já existente em vez de uma referência a um novo objeto, exceto substituindo o objeto que está sendo desserializado. E com o operador new, em vez de uma referência a um novo objeto, é impossível formar uma referência a um objeto já existente. Por exemplo: ArquivoSalary3.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);
 }
}
A saída do programa é:
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
Primeiro, usando a linha Salary3 s1 = new Salary3(); um novo objeto é criado. A seguir, se estiver usando a linha Salary3 s2 = new Salary3(s1); ou cordas Salary3 s3 = new Salary3(s1); seria possível criar um link para um objeto já existente, então s1.value s2.valueeles s3.valuearmazenariam o mesmo valor 1000. Na verdade na fila Salary3 s2 = new Salary3(s1); um novo objeto para a variável será criado s2e o estado do objeto para a variável mudará s1passando seu valor de referência para o objeto no parâmetro do construtor. Isso pode ser verificado pelos resultados de saída. E ao executar a linha Salary3 s3 = new Salary3(s1); um NOVO objeto para a variável será criado s3e o estado do objeto para a variável mudará novamente s1.

Construtores e blocos de inicialização, sequência de ações ao chamar um construtor

A seção Criando um Objeto e Construtores lista as ações gerais que são executadas ao criar um objeto. Entre eles estão os processos de inicialização dos campos da classe e elaboração do construtor da classe, que por sua vez também possui uma ordem interna:
  1. Todos os campos de dados são inicializados com seus valores padrão (0, falso ou nulo).
  2. Todos os inicializadores de campo e blocos de inicialização são executados na ordem em que são listados na declaração de classe.
  3. Se outro construtor for chamado na primeira linha de um construtor, então o construtor chamado será executado.
  4. O corpo do construtor é executado.
O construtor está relacionado à inicialização porque em Java existem três maneiras de inicializar um campo em uma classe:
  • atribuir um valor na declaração;
  • atribuir valores no bloco de inicialização;
  • defina seu valor no construtor.
Naturalmente, você precisa organizar o código de inicialização para que seja fácil de entender. A seguinte classe é dada como exemplo:
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);
 }
}
No exemplo acima, as variáveis ​​são inicializadas na seguinte ordem: variáveis ​​estáticas são inicializadas primeiro xcom yvalores padrão. A seguir, o bloco de inicialização estática é executado. Em seguida, a variável é inicializada icom o valor padrão e a variável é inicializada z. Em seguida, o designer começa a trabalhar. A chamada de construtores de classe não deve depender da ordem em que os campos são declarados. Isso pode levar a erros.

Construtores e herança

Construtores não são herdados. Por exemplo:
public class Example
{
 Example()
 {
 }
 public void sayHi()
 {
  system.out.println("Hi");
 }
}

public class SubClass extends Example
{
}
A classe SubClassherda automaticamente o método sayHi()definido na classe pai. Ao mesmo tempo, o construtor Example()da classe pai não é herdado pelo seu descendente SubClass.

Palavra-chave thisem construtores

Construtores são usados this​​para se referir a outro construtor na mesma classe, mas com uma lista de parâmetros diferente. Se o construtor usar a palavra-chave this, ela deverá estar na primeira linha; ignorar esta regra resultará em um erro do compilador. Por exemplo: ArquivoThisDemo.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();
 }
}
A saída do programa é:
Mary
John
Neste exemplo existem dois construtores. O primeiro recebe um argumento de string. O segundo não recebe nenhum argumento, simplesmente chama o primeiro construtor usando o nome padrão "John". Assim, você pode usar construtores para inicializar os valores dos campos de forma explícita e por padrão, o que geralmente é necessário em programas.

Palavra-chave superem construtores

Construtores são usados super​​para chamar um construtor de superclasse. Se o construtor usar super, então esta chamada deverá estar na primeira linha, caso contrário o compilador gerará um erro. Abaixo está um exemplo: ArquivoSuperClassDemo.java
public class SuperClassDemo
{
 SuperClassDemo()
 {
 }
}

class Child extends SuperClassDemo
{
 Child()
 {
  super();
 }
}
Neste exemplo simples, o construtor Child()contém uma chamada super()que cria uma instância da classe SuperClassDemo, além da classe Child. Como superdeve ser a primeira instrução executada em um construtor de subclasse, essa ordem é sempre a mesma e não depende se super(). Se não for usado, o construtor padrão (sem parâmetros) de cada superclasse, começando pela classe base, será executado primeiro. O programa a seguir demonstra quando os construtores são executados. ArquivoCall.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();
 }
}
Saída deste programa:
Inside A constructor.
Inside B constructor.
Inside C constructor.
Os construtores são chamados em ordem de subordinação de classe. Isso faz algum sentido. Como a superclasse não tem conhecimento de nenhuma subclasse, qualquer inicialização que ela precise realizar é separada. Se possível, deve preceder qualquer inicialização realizada pela subclasse. É por isso que deve ser feito primeiro.

Construtores personalizáveis

O mecanismo de identificação de tipo em tempo de execução é um dos princípios básicos poderosos da linguagem Java que implementa o polimorfismo. No entanto, tal mecanismo não protege o desenvolvedor da conversão de tipos incompatíveis em alguns casos. O caso mais comum é a manipulação de um grupo de objetos, cujos vários tipos são previamente desconhecidos e determinados em tempo de execução. Como os erros associados à incompatibilidade de tipo só podem aparecer no estágio de tempo de execução, isso os torna difíceis de encontrar e eliminar. A introdução de tipos customizados no Java 2 5.0 move alguns desses erros do tempo de execução para o tempo de compilação e fornece parte da segurança de tipo ausente. Não há necessidade de conversão de tipo explícita ao passar de um tipo Objectpara um tipo concreto. Deve-se ter em mente que as ferramentas de customização de tipos funcionam apenas com objetos e não se aplicam a tipos de dados primitivos que estão fora da árvore de herança de classes. Com tipos personalizados, todas as conversões são realizadas automaticamente e nos bastidores. Isso permite proteger contra incompatibilidades de tipo e reutilizar código com muito mais frequência. Tipos personalizados podem ser usados ​​em construtores. Os construtores podem ser personalizados mesmo que sua classe não seja um tipo personalizado. Por exemplo:
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();
 }
}
Como o construtor GenConstructorespecifica um parâmetro de tipo personalizado que deve ser uma classe derivada de class Number, ele pode ser chamado de qualquer
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION