JavaRush /Blogue Java /Random-PT /As 50 principais perguntas e respostas da entrevista sobr...
Roman Beekeeper
Nível 35

As 50 principais perguntas e respostas da entrevista sobre Java Core. Parte 1

Publicado no grupo Random-PT
Olá a todos, senhoras e senhores engenheiros de software! Vamos falar sobre perguntas da entrevista. Sobre o que você precisa se preparar e o que você precisa saber. Este é um excelente motivo para repetir ou estudar estes pontos do zero. As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 1Tenho uma coleção bastante extensa de perguntas frequentes sobre OOP, sintaxe Java, exceções em Java, coleções e multithreading, que por conveniência dividirei em várias partes. Importante:falaremos apenas sobre versões Java até 8. Todas as inovações de 9, 10, 11, 12, 13 não serão levadas em consideração aqui. Quaisquer ideias/comentários sobre como melhorar as respostas são bem-vindas . Boa leitura, vamos lá!

Entrevista Java: perguntas OOP

1. Quais recursos o Java possui?

Responder:
  1. Conceitos de OOP:

    1. orientação a objetos;
    2. herança;
    3. encapsulamento;
    4. polimorfismo;
    5. abstração.
  2. Plataforma cruzada: um programa Java pode ser executado em qualquer plataforma sem quaisquer modificações. A única coisa que você precisa é de uma JVM (máquina virtual Java) instalada.

  3. Alto desempenho: JIT (compilador Just In Time) permite alto desempenho. JIT converte o bytecode em código de máquina e então a JVM inicia a execução.

  4. Multithreading: Um thread de execução conhecido como Thread. A JVM cria um thread chamado main thread. Um programador pode criar vários threads herdando da classe Thread ou implementando uma interface Runnable.

2. O que é herança?

Herança significa que uma classe pode herdar (“ estender ”) outra classe. Dessa forma, você pode reutilizar o código da classe da qual você herdou. A classe existente é conhecida como superclass, e a que está sendo criada é conhecida como subclass. Eles também dizem parente child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
onde Animalestá parente Dog- child.

3. O que é encapsulamento?

Essa pergunta geralmente surge durante entrevistas com desenvolvedores Java. O encapsulamento oculta a implementação usando modificadores de acesso, usando getters e setters. Isso é feito para fechar o acesso para uso externo nos locais onde os desenvolvedores julgarem necessário. Um exemplo acessível da vida é um carro. Não temos acesso direto ao funcionamento do motor. Para nós, a tarefa é colocar a chave na ignição e ligar o motor. E quais processos ocorrerão nos bastidores não é da nossa conta. Além disso, a nossa interferência nesta atividade pode levar a uma situação imprevisível, pela qual podemos quebrar o carro e nos machucar. Exatamente a mesma coisa acontece na programação. Bem descrito na Wikipedia . Há também um artigo sobre encapsulamento no JavaRush .

4. O que é polimorfismo?

Polimorfismo é a capacidade de um programa usar objetos de forma idêntica com a mesma interface sem informações sobre o tipo específico desse objeto. Como se costuma dizer, uma interface – muitas implementações. Com o polimorfismo, você pode combinar e usar diferentes tipos de objetos com base em seu comportamento comum. Por exemplo, temos uma classe Animal, que possui dois descendentes - Cachorro e Gato. A classe genérica Animal tem um comportamento comum para todos - fazer um som. No caso em que precisamos reunir todos os descendentes da classe Animal e executar o método “fazer som”, utilizamos as possibilidades de polimorfismo. Será assim que será:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Então o polimorfismo nos ajuda. Além disso, isto também se aplica a métodos polimórficos (sobrecarregados). Prática de uso de polimorfismo

Perguntas da entrevista - Sintaxe Java

5. O que é um construtor em Java?

As seguintes características são válidas:
  1. Quando um novo objeto é criado, o programa utiliza o construtor apropriado para isso.
  2. Um construtor é como um método. Sua peculiaridade é que não há elemento de retorno (inclusive void), e seu nome é igual ao nome da classe.
  3. Se nenhum construtor for escrito explicitamente, um construtor vazio será criado automaticamente.
  4. O construtor pode ser substituído.
  5. Se um construtor com parâmetros foi criado, mas você também precisa de um sem parâmetros, é necessário escrevê-lo separadamente, pois ele não é criado automaticamente.

6. Quais são as duas classes que não herdam de Object?

Não se deixe enganar pelas provocações, tais classes não existem: todas as classes diretamente ou através de ancestrais são herdadas da classe Object!

7. O que é variável local?

Outra pergunta popular durante uma entrevista com um desenvolvedor Java. Uma variável local é uma variável definida dentro de um método e existe até o momento em que o método é executado. Assim que a execução terminar, a variável local deixará de existir. Aqui está um programa que usa a variável local helloMessage no método main():
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. O que é variável de instância?

Variável de instância é uma variável definida dentro de uma classe e existe até o momento em que o objeto existe. Um exemplo é a classe Bee, que possui duas variáveis ​​nectarCapacity e maxNectarCapacity:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. O que são modificadores de acesso?

Modificadores de acesso são uma ferramenta que permite personalizar o acesso a classes, métodos e variáveis. Existem os seguintes modificadores, ordenados em ordem crescente de acesso:
  1. private- usado para métodos, campos e construtores. O nível de acesso é apenas a classe dentro da qual é declarado.
  2. package-private(default)- pode ser usado para aulas. Acesso apenas em um pacote específico no qual é declarada uma classe, método, variável, construtor.
  3. protected— o mesmo acesso que package-private+ para as classes que herdam de uma classe com o modificador protected.
  4. public- também usado para aulas. Acesso total em todo o aplicativo.
  5. As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 2

10. O que são métodos de substituição?

A substituição de método ocorre quando o filho deseja alterar o comportamento da classe pai. Se você quiser que o que está no método pai seja executado, você pode usar uma construção como super.methodName() no filho, que fará o trabalho do método pai, e só então adicionar lógica. Requisitos a serem atendidos:
  • a assinatura do método deve ser a mesma;
  • o valor de retorno deve ser o mesmo.

11. O que é uma assinatura de método?

As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 3Uma assinatura de método é um conjunto do nome do método e dos argumentos que o método aceita. Uma assinatura de método é um identificador exclusivo para um método ao sobrecarregá-los.

12. O que é sobrecarga de método?

A sobrecarga de métodos é uma propriedade do polimorfismo em que, alterando a assinatura do método, é possível criar métodos diferentes para as mesmas ações:
  • mesmo nome de método;
  • argumentos diferentes;
  • pode haver um tipo de retorno diferente.
Por exemplo, o mesmo add()of ArrayListpode ser sobrecarregado da seguinte maneira e realizará a adição de maneira diferente, dependendo dos argumentos recebidos:
  • add(Object o)- simplesmente adiciona um objeto;
  • add(int index, Object o)— adiciona um objeto a um índice específico;
  • add(Collection<Object> c)— adiciona uma lista de objetos;
  • add(int index, Collection<Object> c)— adiciona uma lista de objetos, começando em um determinado índice.

13. O que é Interface?

Herança múltipla não é implementada em Java, então para superar esse problema, foram adicionadas interfaces como as conhecemos ;) Por muito tempo, as interfaces só tinham métodos sem implementá-los. Como parte desta resposta, falaremos sobre eles. Por exemplo:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Algumas nuances decorrem disso:
  • todos os métodos da interface são públicos e abstratos;
  • todas as variáveis ​​são public static final;
  • as classes não os herdam (estende), mas os implementam (implementam). Além disso, você pode implementar quantas interfaces desejar.
  • classes que implementam uma interface devem fornecer implementações de todos os métodos que a interface possui.
Assim:
public class Cat implements Animal {
   public void makeSound() {
       // method implementation
   }

   public void eat() {
       // implementation
   }

   public void sleep() {
       // implementation
   }
}

14. Qual é o método padrão na Interface?

Agora vamos falar sobre métodos padrão. Para quê, para quem? Esses métodos foram adicionados para tornar tudo “seu e nosso”. Do que estou falando? Sim, por um lado foi necessário adicionar novas funcionalidades: lambdas, Stream API, por outro lado foi necessário deixar o que o Java é famoso - a retrocompatibilidade. Para isso, foi necessário introduzir soluções prontas nas interfaces. Foi assim que os métodos padrão chegaram até nós. Ou seja, o método padrão é um método implementado na interface que possui a palavra-chave default. Por exemplo, o método bem conhecido stream()no Collection. Confira, essa interface não é tão simples quanto parece ;). Ou também um método igualmente conhecido forEach()do Iterable. Também não existia até que os métodos padrão foram adicionados. A propósito, você também pode ler sobre isso no JavaRush .

15. Como então herdar dois métodos padrão idênticos?

Com base na resposta anterior sobre qual é o método padrão, você pode fazer outra pergunta. Se você pode implementar métodos em interfaces, então teoricamente você pode implementar duas interfaces com o mesmo método, e como fazer isso? Existem duas interfaces diferentes com o mesmo método:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
E existe uma classe que implementa essas duas interfaces. Para evitar incertezas e compilar o código, precisamos substituir o método foo()na classe Ce podemos simplesmente chamar um método foo()de qualquer uma das interfaces nela contidas - Aou B. Mas como escolher um método de interface específico Аou В? Existe uma estrutura como esta para isso A.super.foo():
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
ou:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
Assim, um método foo()de classe Cusará o método padrão foo()da interface Aou um método foo()da interface B.

16. O que são métodos e classes abstratos?

Java possui uma palavra reservada abstractque é usada para denotar classes e métodos abstratos. Primeiro, algumas definições. Um método abstrato é um método criado sem implementação com uma palavra-chave abstractem uma classe abstrata. Ou seja, este é um método como na interface, apenas com a adição de uma palavra-chave, por exemplo:
public abstract void foo();
Uma classe abstrata é uma classe que também possui abstracta palavra:
public abstract class A {

}
Uma classe abstrata possui vários recursos:
  • um objeto não pode ser criado com base nele;
  • pode ter métodos abstratos;
  • pode não ter métodos abstratos.
Classes abstratas são necessárias para generalizar algum tipo de abstração (desculpe pela tautologia), que não existe na vida real, mas contém muitos comportamentos e estados comuns (ou seja, métodos e variáveis). Existem exemplos mais do que suficientes da vida. Tudo está ao nosso redor. Poderia ser “animal”, “carro”, “figura geométrica” e assim por diante.

17. Qual é a diferença entre String, String Builder e String Buffer?

Os valores Stringsão armazenados em um conjunto de strings constante. Depois que uma linha for criada, ela aparecerá neste pool. E não será possível excluí-lo. Por exemplo:
String name = "book";
...a variável se referirá ao pool de strings Pool de strings constante As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 4 Se você definir o nome da variável com um valor diferente, obterá o seguinte:
name = "pen";
Conjunto de strings constante As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 5Portanto, esses dois valores permanecerão lá. Buffer de string:
  • os valores Stringsão armazenados na pilha. Se o valor for alterado, o novo valor será substituído pelo antigo;
  • String Buffersincronizado e, portanto, thread-safe;
  • Devido à segurança da rosca, a velocidade de operação deixa muito a desejar.
Exemplo:
StringBuffer name = "book";
As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 6Assim que o valor do nome muda, o valor na pilha muda: As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 7StringBuilder Exatamente o mesmo que StringBuffer, só que não é thread-safe. Portanto, sua velocidade é claramente maior que em StringBuffer.

18. Qual é a diferença entre uma classe abstrata e uma interface?

Classe abstrata:
  • classes abstratas possuem um construtor padrão; é chamado toda vez que um filho desta classe abstrata é criado;
  • contém métodos abstratos e não abstratos. Em geral, pode não conter métodos abstratos, mas ainda assim ser uma classe abstrata;
  • uma classe que herda de uma classe abstrata deve implementar apenas métodos abstratos;
  • uma classe abstrata pode conter uma variável de instância (veja a pergunta nº 5).
Interface:
  • não tem construtor e não pode ser inicializado;
  • apenas métodos abstratos devem ser adicionados (sem contar os métodos padrão);
  • classes que implementam uma interface devem implementar todos os métodos (sem contar os métodos padrão);
  • interfaces só podem conter constantes.

19. Por que acessar um elemento em um array leva O(1)?

Esta pergunta é literalmente da última entrevista. Como aprendi mais tarde, esta pergunta é feita para ver como uma pessoa pensa. É claro que há pouco significado prático neste conhecimento: basta conhecer este facto. Primeiramente, precisamos esclarecer que O(1) é uma designação para a complexidade de tempo de um algoritmo quando a operação ocorre em tempo constante. Ou seja, esta designação é a execução mais rápida. Para responder a esta pergunta, precisamos entender o que sabemos sobre arrays. Para criar um array int, devemos escrever o seguinte:
int[] intArray = new int[100];
Várias conclusões podem ser tiradas desta gravação:
  1. Ao criar um array, seu tipo é conhecido. Se o tipo for conhecido, fica claro qual será o tamanho de cada célula do array.
  2. Sabe-se qual será o tamanho do array.
Segue-se disso: para entender em qual célula escrever, basta calcular em qual área da memória escrever. Para um carro não poderia ser mais fácil. A máquina tem um início de memória alocada, vários elementos e um único tamanho de célula. A partir disso fica claro que o espaço de gravação será igual ao local inicial do array + o tamanho da célula multiplicado pelo seu tamanho.

Como você obtém acesso O(1) a objetos em um ArrayList?

Esta questão segue imediatamente a anterior. É verdade que quando trabalhamos com um array e nele existem primitivos, sabemos de antemão qual é o tamanho desse tipo no momento de sua criação. Mas e se houver um esquema como o da imagem: As 50 principais perguntas e respostas da entrevista sobre Java Core.  Parte 1 - 8e quisermos criar uma coleção com elementos do tipo A, e adicionar diferentes implementações - B, C, D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Nessa situação, como você pode entender qual será o tamanho de cada célula, pois cada objeto será diferente e poderá ter campos adicionais diferentes (ou ser completamente diferentes). O que fazer? Aqui a questão é colocada de forma a confundir e confundir. Sabemos que na verdade a coleção não armazena objetos, mas apenas links para esses objetos. E todos os links têm o mesmo tamanho, e isso é conhecido. Portanto, contar o espaço aqui funciona da mesma forma que na questão anterior.

21. Autoboxing e unboxing

Antecedentes históricos: autoboxing e autounboxing são uma das principais inovações do JDK 5. Autoboxing é o processo de conversão automática de um tipo primitivo para a classe wrapper apropriada. Auto-unboxing - faz exatamente o oposto do auto-boxing - converte uma classe wrapper em uma primitiva. Mas se houver um valor de wrapper null, uma exceção será lançada durante a descompactação NullPointerException.

Primitivo correspondente - wrapper

Primitivo Wrapper de classe
boleano boleano
interno Inteiro
byte Byte
Caracteres Personagem
flutuador Flutuador
longo Longo
curto Curto
dobro Dobro

A embalagem automática ocorre:

  • ao atribuir a uma primitiva uma referência à classe wrapper:

    ANTES do Java 5:

    //manual packaging or how it was BEFORE Java 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // and so on to other types
    }
    
    после Java 5:
    //automatic packaging or how it became in Java 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // and so on to other types
    }
  • ao passar um primitivo como argumento para um método que espera um wrapper:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

A descompactação automática ocorre:

  • quando atribuímos uma variável primitiva à classe wrapper:

    //before Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    //and after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • Em casos com operações aritméticas. Eles se aplicam apenas a tipos primitivos; para isso você precisa fazer o unboxing do primitivo.

    // Before Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // for comparison it was necessary to do this:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • quando passado para um wrapper em um método que aceita a primitiva correspondente:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22. Qual é a palavra-chave final e onde usá-la?

A palavra-chave finalpode ser usada para variáveis, métodos e classes.
  1. Uma variável final não pode ser reatribuída a outro objeto.
  2. a classe final é estéril)) não pode ter herdeiros.
  3. O método final não pode ser substituído em um ancestral.
Já cobrimos o topo, agora vamos discuti-lo com mais detalhes.

variáveis ​​finais

;Java nos dá duas maneiras de criar uma variável e atribuir algum valor a ela:
  1. Você pode declarar uma variável e inicializá-la posteriormente.
  2. Você pode declarar uma variável e atribuí-la imediatamente.
Exemplo usando variável final para estes casos:
public class FinalExample {

   //final static variable, which is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final is a variable that is not initialized, but will only work if
   //initialize this in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // final field Config.creationTime cannot be assigned
//    finalExample.creationTime = 1L;
   }
}

A variável Final pode ser considerada uma constante?

Como não poderemos atribuir um novo valor a uma variável final, parece que estas são variáveis ​​constantes. Mas isso é apenas à primeira vista. Se o tipo de dados ao qual a variável se refere for immutable, então sim, é uma constante. Mas se o tipo de dado mutablefor mutável, através de métodos e variáveis ​​será possível alterar o valor do objeto ao qual finala variável se refere, e neste caso não pode ser chamada de constante. Portanto, o exemplo mostra que algumas das variáveis ​​finais são realmente constantes, mas outras não, e podem ser alteradas.
public class FinalExample {

   //immutable final variables:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // mutable filter variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

Variáveis ​​finais locais

Quando finaluma variável é criada dentro de um método, ela é chamada local finalde variável:
public class FinalExample {

   public static void main(String[] args) {
       // This is how you can
       final int minAgeForDriveCar = 18;

       // or you can do it this way, in the foreach loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Podemos usar a palavra-chave finalem loop estendido forporque após a conclusão da iteração do loop, foruma nova variável é criada a cada vez. Mas nada disso se aplica a um loop for normal, então o código abaixo gerará um erro em tempo de compilação.
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

Aula final

Você não pode estender uma classe declarada como final. Simplificando, nenhuma classe pode herdar desta. Um ótimo exemplo finalde classe no JDK é String. O primeiro passo para criar uma classe imutável é marcá-la como final, para que não possa ser estendida:
public final class FinalExample {
}

// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}

Métodos finais

Quando um método é marcado como final, ele é chamado de método final (lógico, certo?). O método Final não pode ser substituído em uma classe descendente. A propósito, os métodos da classe Object - wait() e notify() - são finais, portanto não temos a oportunidade de substituí-los.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // compile error here
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

Como e onde usar final em Java

  • use a palavra-chave final para definir algumas constantes em nível de classe;
  • crie variáveis ​​finais para objetos quando não quiser que eles sejam modificados. Por exemplo, propriedades específicas do objeto que podemos usar para fins de registro;
  • se não quiser que a aula seja estendida, marque-a como final;
  • se você precisar criar uma classe imutável<, precisará torná-la final;
  • se você deseja que a implementação de um método não mude em seus descendentes, designe o método como final. Isto é muito importante para garantir que a implementação não mude.

23. O que é mutável e imutável?

Mutável

Mutáveis ​​são objetos cujos estados e variáveis ​​podem ser alterados após a criação. Por exemplo, classes como StringBuilder, StringBuffer. Exemplo:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // this setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("first address");
       System.out.println(obj.getAddress());

       // update the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

Imutável

Imutáveis ​​são objetos cujos estados e variáveis ​​não podem ser alterados após a criação do objeto. Por que não uma ótima chave para um HashMap, certo?) Por exemplo, String, Integer, Double e assim por diante. Exemplo:
// make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Therefore, do not change this field in any way, so this is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24. Como escrever uma classe imutável?

Depois de descobrir o que são objetos mutáveis ​​​​e imutáveis, a próxima pergunta é natural - como escrevê-los? Para escrever uma classe imutável e imutável, você precisa seguir etapas simples:
  • tornar a aula final.
  • torne todos os campos privados e crie apenas getters para eles. Os setters, é claro, não são necessários.
  • Torne todos os campos mutáveis ​​finais para que o valor só possa ser definido uma vez.
  • inicialize todos os campos através do construtor, realizando uma cópia profunda (ou seja, copiando o próprio objeto, suas variáveis, variáveis ​​​​de variáveis, e assim por diante)
  • clonar objetos variáveis ​​mutáveis ​​em getters para retornar apenas cópias de valores, não referências a objetos reais.
Exemplo:
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Clone the object before returning it.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * In the constructor, deep copy the mutable objects.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println("Performing a deep copy in the constructor");
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION