Olá pessoal! Hoje continuo minha revisão das perguntas da entrevista com desenvolvedores Java. Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 1

29. O return pode ser usado em um construtor?

Sim, mas apenas sem um valor à direita da palavra-chave return . Você pode usar retorno; como uma instrução auxiliar em um construtor para encerrar (interromper) urgentemente a execução de código adicional e finalizar a inicialização do objeto. Por exemplo, suponha que temos uma classe Cat , e se um Cat é um morador de rua ( isHomeless = true , então queremos encerrar a inicialização e não preencher os demais campos (afinal, eles nos são desconhecidos, já que o gato é um morador de rua) :
public Cat(int age, String name, boolean isHomeless) {
   if (isHomeless){
       this.isHomeless = isHomeless;
       return;
   }
   this.isHomeless = isHomeless;
   this.age = age;
   this.name = name;
}
Mas se estamos falando de valores concretos, então a palavra-chave return não pode retornar um valor específico porque:
  • ao declarar um construtor, você não terá nada parecido com o tipo de retorno;
  • via de regra, o construtor é chamado implicitamente durante a instanciação;
  • o construtor não é um método: é um mecanismo separado cujo único propósito é inicializar variáveis ​​de instância, ou seja, estamos usando o operador new para criar um objeto.
Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 2

30. Uma exceção pode ser lançada de um construtor?

Os construtores trabalham com exceções da mesma forma que os métodos. Os métodos nos permitem lançar exceções escrevendo throws <ExceptionType> no cabeçalho do método. E os construtores nos permitem fazer o mesmo. Quando herdamos e definimos o construtor de uma classe filha, podemos ampliar o tipo de exceção - por exemplo, IOException -> Exception (mas não vice-versa). Vamos usar o construtor da classe Cat como exemplo de construtor que lança uma exceção. Digamos que quando criamos um objeto, queremos inserir o nome e a idade no console:
public Cat() throws IOException {
   BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
   this.name = reader.readLine();
   this.age = Integer.parseInt(reader.readLine());
}
Como reader.readLine() lança uma IOException, nós a escrevemos no cabeçalho como uma possível exceção lançada.

31. Quais são os elementos de um cabeçalho de classe? Escreva um exemplo

Para ilustrar os elementos que compõem um cabeçalho de classe, vejamos um pequeno esquema:
  • elementos obrigatórios aparecem entre colchetes <>
  • elementos opcionais estão em {}
{modificador de acesso}{estático}{final}{resumo}<nome da classe>{herança da classe pai}{implementação de interfaces} Então , o que temos: {modificador de acesso} — apenas os modificadores de acesso público e padrão estão disponíveis para o aula. {static} — o modificador estático indica que esta classe é estática; aplica-se apenas a classes internas (classes dentro de outras classes). {final} — este é o modificador final , é claro, que torna a classe não herdável (um exemplo pronto para uso é String ). {abstract} — o modificador abstrato , que indica que a classe pode ter métodos não implementados. Este modificador entra em conflito com o modificador final . O cabeçalho da classe só pode ter um deles, pois o modificador abstrato significa que a classe será herdada e seus elementos abstratos serão implementados. Mas final indica que esta é a versão final da classe e que não pode ser herdada. Na verdade, usar simultaneamente os dois modificadores seria um absurdo. O compilador não nos permitirá fazer isso. <class> é uma palavra-chave obrigatória que indica uma declaração de classe. <nome da classe> é um nome de classe simples que se torna o identificador de uma classe Java específica. O nome completo da classe consiste no nome do pacote qualificado mais '.' mais o nome da classe simples. {herança da classe pai} é uma indicação da classe pai (se houver) usando a palavra-chave extends . Por exemplo, ... estende ParentClass . {implementação de interfaces} — uma lista das interfaces que esta classe implementa (se houver), usando a palavra-chave implements . Por exemplo: ... implementa FirstInterface, SecondInterface ... Como exemplo, considere o título da classe Lion , que herda Cat e implementa a interface WildAnimal :
public final class Lion extends Cat implements WildAnimal
Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 3

32. Quais são os elementos de um cabeçalho de método? Escreva um exemplo

Ao considerar os elementos que compõem um cabeçalho de método, consideremos novamente um pequeno esquema:
  • elementos obrigatórios aparecem entre colchetes <>
  • elementos opcionais estão em {}
{modificador de acesso}{estático}{resumo}{final}{sincronizado} {nativo} <valor de retorno><nome do método> <(>{parâmetros do método}<}>{lançar exceções} {modificador de acesso} — todos os modificadores de acesso são disponível para o método — public , protected , default , private . {static} — o modificador estático , que indica que o método é estático e, portanto, associado à classe, não a um objeto. {abstract} — o modificador abstrato , que indica que o método não tem implementação (corpo). Para funcionar corretamente, a classe que declara o método também deve ter o modificador abstrato . Assim como no cabeçalho da classe, esse modificador entra em conflito com o modificador final , e também entra em conflito com o modificador estático , pois um método abstrato implica substituir o método em um descendente, e métodos estáticos não podem ser substituídos. {finale} — o modificador final , que indica que este método não pode ser substituído. {synchronized} — o modificador sincronizado , o que significa que o método está protegido de acesso simultâneo a ele a partir de diferentes threads. Se o método não for estático, ele será fechado para o this mutex do objeto. Se o método for estático, ele será fechado para o mutex da classe atual. {native} — o modificador nativo indica que o método foi escrito em outra linguagem de programação. <tipo de retorno> — o tipo do valor que o método deve retornar. Se o método não retornar nada, então void . <nome do método> — o nome do método, ou seja, seu identificador no sistema. {parâmetros do método} — os parâmetros que o método aceita: são necessários para implementar sua funcionalidade. {lançada exceções}lança <ExceptionType> — uma lista das exceções verificadas que este método pode lançar. Oferecerei o seguinte como exemplo de cabeçalho de método:
public static void main(String[] args) throws IOException

33. Crie um construtor padrão em uma classe filha se ainda não estiver definido na classe base (mas um construtor diferente estiver definido)

Não tenho certeza se entendi completamente a pergunta, mas talvez isso signifique que temos algum construtor como este na classe pai:
public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
Nesse caso, na classe pai, definitivamente precisamos definir um construtor que irá inicializar o pai (ou seja, chamar o construtor pai):
public class Lion extends Cat {

   public Lion(int age, String name) {
       super(age, name);
   }
}
Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 4

34. Quando a palavra-chave this é usada?

Em Java, isso tem dois significados diferentes. 1. É uma referência ao objeto atual, por exemplo, this.age = 9 . Ou seja, this refere-se ao objeto no qual é utilizado e ao qual o código com this se refere. O objetivo principal é melhorar a legibilidade do código e evitar ambiguidades. Por exemplo, se um campo de instância e um argumento de método tiverem o mesmo nome:
public void setName(String name) {
   this.name = name;
}
Ou seja, this.name é o campo do objeto, enquanto name é o parâmetro do método. A referência this não pode ser usada em métodos estáticos. 2. No construtor, this pode ser chamado como um método, por exemplo, this(value) . Neste caso, será uma chamada para outro construtor da mesma classe. Basicamente, você pode chamar dois construtores durante o processo de criação de um objeto:
public Cat(int age, String name) {
   this(name);
   this.age = age;
}

public Cat(String name) {
   this.name = name;
}
Ao chamar o primeiro construtor para criar um objeto Cat , ambos os campos de instância serão inicializados com sucesso. Existem algumas nuances aqui:
  1. this() só funciona em um construtor.
  2. Uma referência a outro construtor deve estar na primeira linha do bloco construtor (corpo). Isso significa que um construtor não pode chamar mais de um (outro) construtor de sua classe.
Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 5

35. O que é um inicializador?

Pelo que entendi, esta questão é sobre blocos de inicialização comuns e estáticos. Vamos primeiro lembrar o que é inicialização. A inicialização é a criação, ativação, preparação e definição de campos. Preparar um programa ou componente para estar pronto para uso. Você deve se lembrar que quando você cria um objeto, uma variável de classe pode ser inicializada imediatamente quando é declarada:
class Cat {
   private int age = 9;
   private String name = "Tom";
Ou definido após o fato por meio do construtor:
class Cat {
   private int age;
   private String name;

   public Cat(int age, String name) {
       this.age = age;
       this.name = name;
   }
Mas há outra maneira: você pode definir uma variável de instância usando um bloco de inicialização, que assume a forma de chaves {} dentro de uma classe, sem nome (como um método ou construtor sem nome):
class Cat {
   private int age;
   private String name;

   {
       age = 10;
       name = "Tom";
   }
Um bloco de inicialização é um trecho de código carregado quando um objeto é criado. Esses blocos são normalmente usados ​​para realizar certos cálculos complexos necessários quando uma classe é carregada. Os resultados desses cálculos podem ser definidos como valores de variáveis. Além dos blocos de inicialização comuns, existem os estáticos. Eles têm a mesma aparência, mas têm a palavra-chave estática na frente da chave de abertura:
class Cat {
   private static int age;
   private static String name;

   static{
       age = 10;
       name = "Tom";
   }
Este bloco é igual ao anterior. Mas se o comum é executado quando cada objeto é inicializado, então o estático é executado apenas uma vez, quando a classe é carregada. Via de regra, certos cálculos complexos são realizados em um bloco estático, usado para inicializar variáveis ​​de classe estáticas. As mesmas restrições se aplicam a um bloco estático que se aplica a métodos estáticos: você não pode usar dados não estáticos, como uma referência ao objeto atual ( this ) em um bloco estático. Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 6Agora podemos observar a ordem de inicialização da classe (juntamente com sua classe pai) para entender melhor quando exatamente os blocos de inicialização são invocados.

36. Dada uma classe pública Child que estende Parent, escreva a ordem de inicialização do objeto

Ao carregar a classe Child , a ordem de inicialização será a seguinte:
  1. Campos de classe estática da classe Pai .
  2. Bloco de inicialização estática da classe Parent .
  3. Campos estáticos da classe Сhild .
  4. Bloco de inicialização estática da classe Child .
  5. Campos não estáticos da classe Parent .
  6. Bloco de inicialização não estático da classe Parent .
  7. Construtor da classe pai .
  8. Campos não estáticos da classe Сhild .
  9. Bloco de inicialização não estático da classe Сhild .
  10. O construtor da classe Сhild .
Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 4 - 7

37. Que tipo de relacionamento entre classes (objetos) você conhece?

Existem dois tipos de variáveis ​​em Java: tipos primitivos e referências a objetos completos.
  • Relacionamentos IS-A
O princípio IS-A da OOP é baseado na herança de classe ou implementação de interfaces. Por exemplo, se a classe Lion herda Cat , então dizemos que Lion é um Cat :
Lion IS-A Cat
(mas nem todo gato é um leão ) A mesma situação existe com interfaces. Se a classe Lion implementa a interface WildAnimal , então eles também existem no relacionamento:
Lion IS-A WildAnimal
  • TEM-Um relacionamento
Esse tipo de relacionamento ocorre onde uma classe utiliza outras classes, também chamada de “associação”. Uma associação é uma classe que se refere a outra classe (ou referências mútuas entre si). Por exemplo, a classe Carro pode fazer referência à classe Passageiro , o que constituiria o seguinte relacionamento:
Car HAS-A Passenger
E vice-versa: se Passenger tiver referência para Car , então esse será o relacionamento:
Passenger HAS-A Car

38. Que relações objetais associativas você conhece?

Agregação e composição nada mais são do que casos especiais de associação. Agregação é um relacionamento em que um objeto faz parte de outro. Por exemplo, um passageiro pode estar localizado em um carro. Além do mais, pode haver vários passageiros ou nenhum (e se estivermos falando de Tesla, pode não haver motorista). Por exemplo:
public class Car {
   private List passengers = new ArrayList<>();

 void setPassenger(Passenger passenger) {
     passengers.add(passenger);
 }

   void move() {
       for (Passenger passenger : passengers) {
           System.out.println("Transporting passenger - " + passenger.toString());
       }
       passengers.clear();
   }
}
Ou seja, o número de passageiros (em qualquer) não é importante para nós: a funcionalidade da classe Carro não depende disso. A agregação também implica que quando outro objeto usa um objeto, o primeiro objeto pode ser usado por outros objetos. Por exemplo, o mesmo aluno pode fazer parte de um clube de tricô e de uma banda de rock e frequentar simultaneamente uma aula de espanhol. Como você pode imaginar, a agregação é um relacionamento associativo mais flexível entre classes. A composição é uma relação ainda mais estreita onde um objeto não é apenas parte de outro objeto, mas o trabalho de um objeto é muito dependente de outro. Por exemplo, um carro tem motor. Um motor pode existir sem carro, mas é inútil fora dele. E um carro não pode funcionar sem motor:
public class Car {
   private Engine engine;

   public Car(Engine engine) {
       this.engine = engine;
   }

   void startMoving() {
       engine.start();
           ...
   }
A composição também implica que quando outro objeto usa um objeto, o primeiro objeto não pode pertencer a nenhum outro objeto. Voltando ao nosso exemplo, um motor só pode pertencer a um carro, e não a dois ou mais ao mesmo tempo. Acho que isso é o suficiente por hoje, então vamos parar por aqui.