CodeGym /Blogue Java /Random-PT /As 50 principais perguntas e respostas de entrevistas de ...
John Squirrels
Nível 41
San Francisco

As 50 principais perguntas e respostas de entrevistas de emprego para Java Core. Parte 1

Publicado no grupo Random-PT
Olá a todos, senhoras e senhores, engenheiros de software! Vamos falar sobre perguntas de entrevista. Sobre o que você precisa se preparar e o que você precisa saber. Este é um ótimo momento para revisar ou estudar esses pontos pela primeira vez. As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 1 Acabei com uma coleção bastante extensa de perguntas frequentes sobre OOP, sintaxe Java, exceções Java, coleções e multithreading, que dividirei em várias partes por conveniência. É difícil cobrir tudo de uma vez, mas espero que este material forneça uma boa base para aqueles que se preparam para encontrar seu primeiro emprego como programador. Para melhor compreensão e retenção, aconselho vasculhar outras fontes também. Você pode obter uma compreensão mais profunda de um conceito abordando-o de vários ângulos diferentes. Importante:Estaremos falando apenas do Java antes da versão 8. Todas as inovações que vieram nas versões 9, 10, 11, 12 e 13 não serão consideradas aqui. Quaisquer ideias/comentários sobre como melhorar as respostas são bem-vindos . Aproveite sua leitura. Vamos!

Entrevista Java: perguntas sobre OOP

1. Quais são as características do Java?

Responder:
  1. Conceitos de POO:

    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 nenhuma alteração. Obviamente, isso requer uma JVM (máquina virtual Java) instalada.

  3. Alto desempenho: O compilador Just-In-Time (JIT) torna possível o alto desempenho. O compilador JIT converte o bytecode em código de máquina e, em seguida, a JVM inicia a execução.

  4. Multithreading: A JVM cria um thread de execução chamado main thread. Um programador pode criar vários threads derivando da classe Thread ou implementando a Runnableinterface.

2. O que é herança?

Herança significa que uma classe pode herdar outra classe (usando a palavra-chave extends ). Isso significa que você pode reutilizar o código da classe herdada. A classe existente é conhecida como the superclasse a classe recém-criada é a subclass. As pessoas também dizem usar os termos pai e child.

public class Animal {
   private int age;
}

public class Dog extends Animal {

}
onde Animalé o parente Dogé o child.

3. O que é encapsulamento?

Essa pergunta costuma ser feita em entrevistas para cargos de desenvolvedor Java. O encapsulamento está ocultando a implementação usando modificadores de acesso, getters e setters. Isso é feito para impedir o acesso externo sempre que os desenvolvedores acharem necessário. Um exemplo simples da vida real é o carro. Não temos acesso direto ao funcionamento do motor. Tudo o que precisamos fazer é colocar a chave na ignição e ligar o motor. Os processos que ocorrem sob o capô não são da nossa conta. Além disso, se interferirmos na atividade do motor, isso pode levar a uma situação imprevisível, possivelmente danificando o carro e resultando em lesões corporais. Exatamente a mesma coisa acontece na programação. Isso é bem descrito na Wikipedia. Há também um artigo sobre encapsulamento no CodeGym .

4. O que é polimorfismo?

O polimorfismo é a capacidade de um programa tratar objetos com a mesma interface da mesma maneira, sem informações sobre o tipo específico do objeto. Como diz o ditado, "uma interface — muitas implementações". Com o polimorfismo, você pode combinar e usar diferentes tipos de objetos com base em comportamentos compartilhados. Por exemplo, temos uma classe Animal que possui dois descendentes: Dog e Cat. A classe genérica Animal tem um comportamento compartilhado por todos, a habilidade de fazer um som. Usamos recursos polimórficos quando precisamos reunir tudo o que herda a classe Animal e executar o método "fazer som". Veja como fica:

List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Em outras palavras, o polimorfismo é útil. E isso também se aplica a métodos polimórficos (sobrecarregados). Como usar polimorfismo

Perguntas da entrevista sobre sintaxe Java

5. O que é um construtor em Java?

Os construtores têm as seguintes características:
  1. Quando um novo objeto é criado, o programa usa o construtor apropriado para criá-lo.
  2. Um construtor é como um método. Suas características distintivas residem no fato de não haver valor de retorno (inclusive void) e de seu nome ser igual ao nome da classe.
  3. Se nenhum construtor for criado explicitamente, um construtor vazio será criado automaticamente.
  4. Um construtor pode ser substituído.
  5. Se você declara um construtor com parâmetros, mas também precisa de um sem parâmetros, deve criá-lo separadamente, pois não será criado automaticamente.

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

Não se deixe enganar por perguntas capciosas - não existem tais classes. Todas as classes herdam a classe Object diretamente ou por meio de ancestrais!

7. O que é uma variável local?

Esta é outra pergunta popular em entrevistas para desenvolvedores Java. Uma variável local é uma variável que é definida dentro de um método e existe enquanto o método estiver sendo executado. Assim que a execução termina, a variável local deixa de existir. Aqui está um programa que usa uma variável local chamada helloMessage no método main():

public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

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

Uma variável de instância é uma variável declarada dentro de uma classe. Ele existe enquanto um objeto existe. Por exemplo, temos uma classe Bee, que possui duas variáveis ​​de instância — nectarLoad e maxNectarLoad:

public class Bee {

   /**
    * Current nectar load
    */
   private double nectarLoad;

   /**
    * Maximum nectar that can the bee can collect.
    */
   private double maxNectarLoad = 20.0;
 
  ...
}

9. O que são modificadores de acesso?

Os modificadores de acesso são um mecanismo para personalizar o acesso a classes, métodos e variáveis. Existem os seguintes modificadores, listados em ordem crescente de acesso:
  1. private— Este modificador de acesso é usado em métodos, campos e construtores. O acesso é limitado à classe em que são declarados.
  2. package-private (default)— Este é o nível de acesso padrão para as aulas. O acesso é limitado ao pacote específico no qual uma classe, método, variável ou construtor é declarado.
  3. protected— Este modificador de acesso oferece o mesmo nível de acesso com package-privatea adição de acesso para classes que herdam uma classe com o protectedmodificador.
  4. public— Este nível de acesso também é usado para aulas. Esse nível de acesso significa que há acesso total em todo o aplicativo.
As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 2

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

Substituímos métodos quando uma classe filha deseja alterar o comportamento de sua classe pai. Se também precisarmos fazer o que está no método pai, podemos usar super.methodName() no filho, que executará o método pai. Podemos adicionar nossa lógica adicional depois disso. Requisitos que devem ser observados:
  • a assinatura do método deve ser a mesma
  • o valor de retorno deve ser o mesmo

11. O que são assinaturas de método?

As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 3Uma assinatura de método é a combinação do nome do método e os argumentos que o método usa. A assinatura do método é o identificador exclusivo de um método ao sobrecarregar métodos.

12. O que é sobrecarga de método?

A sobrecarga de método é um recurso do polimorfismo no qual alteramos a assinatura do método para criar vários métodos que executam a mesma ação:
  • o mesmo nome
  • argumentos diferentes
  • pode haver diferentes tipos de retorno
Por exemplo, o método ArrayListda classe add()pode ser sobrecarregado, permitindo adicionar de maneiras diferentes dependendo dos argumentos de entrada:
  • add(Object o)— Este método simplesmente adiciona um objeto
  • add(int index, Object o)— Este método adiciona um objeto em um índice específico
  • add(Collection<Object> c)— Este método adiciona uma lista de objetos
  • add(int index, Collection<Object> c)— Este método adiciona uma lista de objetos a partir de um índice específico.

13. O que é uma interface?

Java não suporta herança múltipla. Para superar essa limitação, foram adicionadas interfaces na forma que conhecemos e amamos ;) Por muito tempo, as interfaces tinham apenas métodos sem nenhuma implementação. No contexto desta resposta, vamos falar sobre eles. Por exemplo:


public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Alguns detalhes decorrem disso:
  • Todos os métodos em uma interface são públicos e abstratos
  • Todas as variáveis ​​são public static final
  • Classes não herdam interfaces (ou seja, não usamos a palavra-chave extends). Em vez disso, as classes os implementam (ou seja, usamos a palavra-chave implements). Além disso, você pode implementar quantas interfaces quiser.
  • As classes que implementam uma interface devem fornecer uma implementação de todos os métodos que estão na interface.
Assim:

public class Cat implements Animal {
   public void makeSound() {
       // Method implementation
   }

   public void eat() {
       // Implementation
   }

   public void sleep() {
       // Implementation
   }
}

14. O que é um método padrão em uma interface?

Agora vamos falar sobre métodos padrão. Para que servem? Para quem são? Esses métodos foram adicionados para servir "ambas as mãos". Do que estou falando? Bem, por um lado, havia a necessidade de adicionar novas funcionalidades: lambdas e a API Stream. Por outro lado, era necessário manter aquilo pelo qual o Java é famoso — a compatibilidade com versões anteriores. Para fazer isso, as interfaces precisavam de algumas novas soluções prontas. Foi assim que os métodos padrão chegaram até nós. Um método padrão é um método implementado em uma interface, marcado com a defaultpalavra-chave. Por exemplo, o conhecido stream()método na Collectioninterface. Acredite, essa interface não é tão simples quanto parece. Ou também o forEach()método igualmente famoso noIterableinterface. Também não existia até que os métodos padrão fossem adicionados. A propósito, você também pode ler sobre isso no CodeGym aqui .

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

A resposta anterior sobre o que é um método padrão levanta outra questão. Se você pode implementar métodos em interfaces, então teoricamente você pode implementar duas interfaces com o mesmo método. Como fazemos isso? Aqui estão 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 temos uma classe que implementa essas duas interfaces. Mas como escolhemos um método específico na interface A ou B? A seguinte construção especial permite isso: A.super.foo():

public class C implements A, B {
   public void fooA() {
       A.super.foo();
   }

   public void fooB() {
       B.super.foo();
   }
}
Assim, o fooA()método usará o foo()método padrão da Ainterface, enquanto o fooB()método usará o foo()método da Binterface.

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

Em Java, abstracté uma palavra reservada. É usado para denotar classes e métodos abstratos. Primeiro, precisamos de definições. Um método abstrato é um método declarado usando a abstractpalavra-chave sem uma implementação em uma classe abstrata. Ou seja, trata-se de um método como em uma interface, mas com a adição de uma palavra-chave, por exemplo:

public abstract void foo();
Uma classe abstrata é uma classe também marcada com a abstractpalavra-chave:

public abstract class A {

}
Uma classe abstrata tem vários recursos:
  • você não pode criar um objeto de uma classe abstrata
  • pode ter métodos abstratos
  • também pode não ter métodos abstratos
Classes abstratas são necessárias para a abstração (desculpe a tautologia) que possui um conjunto de comportamentos e estados comuns (ou seja, métodos e variáveis). A vida real é cheia de exemplos. Tudo ao nosso redor. "Animal", "Carro", "Figura geométrica" ​​e assim por diante.

17. Qual a diferença entre String, StringBuilder e StringBuffer?

Stringos valores são armazenados em um conjunto de strings constantes. Assim que uma string é criada, ela aparece neste pool. E você não pode excluí-lo. Por exemplo:

String name = "book";
A variável apontará para o conjunto de strings constantes As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 4Definindo a variável name para um valor diferente, temos:

name = "pen";
O conjunto de strings constantes se parece com isto: As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 5Em outras palavras, ambos os valores permanecem lá. Buffer de Cadeia:
  • Stringos valores são armazenados em uma pilha. Se um valor for alterado, o novo valor substituirá o antigo.
  • String Bufferé sincronizado e, portanto, é thread-safe.
  • Devido à segurança da rosca, seu desempenho é ruim.
Exemplo:

StringBuffer name = “book”;
As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 6Assim que o valor da variável name muda, o valor na pilha muda: As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 7StringBuilder é exatamente o mesmo que StringBuffer, só que não é thread-safe. Como resultado, é visivelmente mais rápido do que StringBuffer.

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

Classe abstrata:
  • As classes abstratas têm um construtor padrão. Ele é chamado toda vez que um descendente da classe abstrata é criado.
  • Eles podem incluir métodos abstratos e não abstratos. Em geral, uma classe abstrata não precisa ter métodos abstratos.
  • Uma classe que herda uma classe abstrata deve implementar apenas métodos abstratos.
  • Uma classe abstrata pode ter variáveis ​​de instância (consulte a pergunta nº 5).
Interface:
  • Uma interface não tem construtor e não pode ser inicializada.
  • Somente métodos abstratos podem ser adicionados (exceto para métodos padrão).
  • As classes que implementam a interface devem implementar todos os métodos (exceto os métodos padrão).
  • As interfaces só podem ter constantes.

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

Esta pergunta foi feita literalmente na minha última entrevista. Como aprendi mais tarde, o objetivo desta pergunta é ver como uma pessoa pensa. Claramente, há pouco valor prático neste conhecimento. Apenas saber é o suficiente. Primeiro, precisamos esclarecer que O(1) é a notação para a complexidade de tempo de um algoritmo de "tempo constante". Em outras palavras, essa designação indica o tempo de execução mais rápido. Para responder a essa pergunta, precisamos considerar o que sabemos sobre arrays. Para criar um intarray, devemos escrever o seguinte:

int[] intArray = new int[100];
Várias conclusões podem ser tiradas dessa sintaxe:
  1. Quando um array é declarado, seu tipo é conhecido. Se o tipo for conhecido, o tamanho de cada célula na matriz também será conhecido.
  2. O tamanho de toda a matriz é conhecido.
Conclui-se, portanto, que para entender em qual célula escrever, basta calcular em qual área da memória gravar. Para um computador, isso é fácil. O computador sabe onde começa a memória alocada, o número de elementos e o tamanho de cada célula. Tudo isso significa que o local a ser escrito será igual ao local inicial do array + o tamanho de cada célula multiplicado pelo índice.

Então, como chegamos a O(1) ao acessar objetos em um ArrayList?

Esta pergunta segue imediatamente a anterior. A verdade é que, ao trabalhar com um array que contém primitivas, sabemos de antemão (no momento da criação) o tamanho do tipo de elemento. Mas o que fazemos se tivermos esse tipo de hierarquia de herança e As 50 principais perguntas e respostas de entrevistas de emprego para Java Core.  Parte 1 - 8quisermos criar uma coleção para elementos do tipo A e adicionar diferentes implementações (B, C e D):

List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Nesta situação, como calculamos o tamanho de cada célula? Afinal, cada objeto será diferente, possivelmente com campos adicionais diferentes. O que fazer? Aqui a questão é colocada de uma forma que pretende confundi-lo. Sabemos que a coleção não armazena objetos diretamente. Ele armazena apenas referências a objetos. E todas as referências têm o mesmo tamanho, e isso é conhecido. Como resultado, calculamos os endereços aqui da mesma forma que na pergunta anterior.

21. Autoboxing e unboxing

Histórico: autoboxing e unboxing são algumas das principais inovações do JDK 5. Autoboxing é o processo de conversão automática de um tipo primitivo para uma classe wrapper correspondente. Unboxing é exatamente o oposto de autoboxing. É o processo de converter uma classe wrapper em uma primitiva. Mas se o valor de um wrapper for null, um NullPointerExceptionserá lançado durante o unboxing.

Primitivos e seus wrappers correspondentes

Primitivo classe wrapper
boleano boleano
int inteiro
byte Byte
Caracteres Personagem
flutuador Flutuador
longo Longo
curto Curto
dobro Dobro

// Autoboxing acontece:

  • ao atribuir um primitivo a uma referência a uma classe wrapper:

    ANTES do Java 5:

    
    // Manual boxing (the way it was BEFORE Java 5).
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // And so on for other types
    }
    
    After Java 5:
    // Automatic boxing (the way it became in Java 5).
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // And so on for other types
    }
    
  • quando um primitivo é passado como um 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;
    }
    

// Unboxing acontece:

  • quando atribuímos uma instância de uma classe wrapper a uma variável primitiva:

    
    // 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;
    
  • Durante operações aritméticas. As operações se aplicam apenas a tipos primitivos, portanto, unboxing para primitivos é necessário.

    
    // BEFORE Java 5:
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // A comparison used to require this:
    integerBox1.intValue() > integerBox2.intValue()
          
    // In Java 5
    integerBox1 > integerBox2
    
  • ao passar uma instância de uma classe wrapper para um método que usa 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 é usada?

A finalpalavra-chave pode ser usada em variáveis, métodos e classes.
  1. O valor de uma variável final não pode ser alterado após sua inicialização.
  2. Uma aula final é estéril :) Não pode ter filhos.
  3. Um método final não pode ser substituído por um descendente.
Nós cobrimos o material de alto nível. Agora vamos mergulhar mais fundo.

variáveis ​​finais

Java nos dá duas maneiras de declarar uma variável e atribuir um valor a ela:
  1. Você pode declarar uma variável e inicializá-la mais tarde.
  2. Você pode declarar uma variável e atribuir um valor imediatamente.
Aqui está um exemplo que demonstra esses usos de variáveis ​​finais:

public class FinalExample {

   // A static final variable that is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";

   // A final variable that is not initialized, but will only work if you
   // initialize it 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);

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

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

Uma variável final pode ser considerada uma constante?

Como não podemos atribuir novos valores às variáveis ​​finais, parece que essas são variáveis ​​constantes. Mas apenas à primeira vista: se o tipo de dados da variável for immutable, então, sim, é uma constante. Mas se o tipo de dado for mutable, ou seja, mutável, então será possível usar métodos e variáveis ​​para alterar o valor do objeto referenciado por uma finalvariável. Por isso, não pode ser chamado de constante. O exemplo a seguir mostra que algumas variáveis ​​finais são realmente constantes, enquanto outras não, pois podem ser alteradas.

public class FinalExample {

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

   // Mutable final variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("Constant?");
}

Variáveis ​​finais locais

Quando uma finalvariável é criada dentro de um método, ela é chamada de local finalvariável:

public class FinalExample {

   public static void main(String[] args) {
       // You can do this
       final int minAgeForDriveCar = 18;

       // Or you can do this, in a for-each loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Podemos usar a palavra-chave final em um loop for aprimorado, porque uma nova variável é criada após cada iteração do loop. Lembre-se de que isso não se aplica a um loop for normal, portanto, obteremos um erro de tempo de compilação.

// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

aula final

Uma classe declarada como finalnão pode ser estendida. Simplificando, nenhuma outra classe pode herdá-lo. Um excelente exemplo de finalclasse no JDK é String. O primeiro passo para criar uma classe imutável é marcá-la como final, evitando assim que ela seja estendida:

public final class FinalExample {
}

// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}

Métodos finais

Quando um método é marcado como final, ele é chamado de método final (faz sentido, certo?). Um método final não pode ser substituído em uma classe filha. A propósito, os métodos wait() e notify() da classe Object são finais, então não temos como substituí-los.

public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

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

Como e onde usar final em Java

  • Use a palavra-chave final para definir algumas constantes de nível de classe;
  • Crie variáveis ​​finais para objetos que você não deseja que sejam alterados. Por exemplo, propriedades específicas do objeto que podemos usar para fins de log.
  • Se você não deseja que uma classe 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, marque o método como final. Isso é muito importante para garantir que a implementação não mude.

23. O que são tipos mutáveis ​​e imutáveis?

Mutável

Objetos mutáveis ​​são objetos cujo estado e variáveis ​​podem ser alterados após a criação. Exemplos de classes mutáveis ​​incluem StringBuilder e 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());

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

Imutável

Objetos imutáveis ​​são objetos cujo estado e variáveis ​​não podem ser alterados após a criação do objeto. Uma ótima chave para um HashMap, você não acha? :) Por exemplo, String, Integer, Double e assim por diante. Exemplo:

// We'll 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;
   }

   // We remove the setter

   public static void main(String[] args) {

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

       // There is no way to change this field, so it is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}
Na próxima parte, consideramos perguntas e respostas sobre coleções. Meu perfil no GitHub Top 50 perguntas e respostas de entrevistas de emprego para Java Core. Parte 2
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION