Perguntas da entrevista sobre o sistema Java Type

Perguntas da entrevista sobre o sistema Java Type

1. Introdução

O Java Type System é um tópico frequentemente abordado em entrevistas técnicas para desenvolvedores de Java. Este artigo analisa algumas questões importantes que são feitas com mais frequência e que podem ser difíceis de corrigir.

2. Questões

Q1. Descreva o local da classe de objeto na hierarquia de tipos. Quais tipos herdam do objeto e quais não? As matrizes herdam do objeto? Uma expressão Lambda pode ser atribuída a uma variável de objeto?

Ojava.lang.Object está no topo da hierarquia de classes em Java. Todas as classes herdam dele, seja explicitamente, implicitamente (quando a palavra-chaveextends é omitida da definição da classe) ou transitivamente por meio da cadeia de herança.

No entanto, existem oito tipos primitivos que não herdam deObject, a saberboolean,byte,short,char,int,float,long edouble.

De acordo com a Java Language Specification, matrizes também são objetos. Eles podem ser atribuídos a uma referênciaObject, e todos os métodosObject podem ser chamados neles.

Expressões lambda não podem ser atribuídas diretamente a uma variávelObject porqueObject não é uma interface funcional. Mas você pode atribuir um lambda a uma variável de interface funcional e, em seguida, atribuí-lo a uma variávelObject (ou simplesmente atribuí-lo a uma variável de objeto lançando-o para uma interface funcional ao mesmo tempo).

 

Q2. Explique a diferença entre os tipos primitivos e de referência.

Os tipos de referência herdam da classejava.lang.Object superior e são herdáveis ​​(exceto as classesfinal). Tipos primitivos não herdam e não podem ser subclassificados.

Os valores do argumento digitado de forma primitiva sempre são passados ​​pela pilha, o que significa que são passados ​​por valor, não por referência. Isso tem a seguinte implicação: as alterações feitas em um valor de argumento primitivo dentro do método não se propagam para o valor real do argumento.

Tipos primitivos geralmente são armazenados usando os tipos de valor de hardware subjacentes.

Por exemplo, para armazenar um valorint, uma célula de memória de 32 bits pode ser usada. Os tipos de referência introduzem a sobrecarga do cabeçalho do objeto, presente em todas as instâncias de um tipo de referência.

O tamanho do cabeçalho de um objeto pode ser bastante significativo em relação a um tamanho de valor numérico simples. É por isso que os tipos primitivos foram introduzidos em primeiro lugar - para economizar espaço na sobrecarga do objeto. A desvantagem é que nem tudo em Java é tecnicamente um objeto - valores primitivos não herdam da classeObject.

 

Q3. Descreva os diferentes tipos primitivos e a quantidade de memória que eles ocupam.

Java possui 8 tipos primitivos:

  • boolean - valor lógico detrue /false. O tamanho do booleano não é definido pela especificação da JVM e pode variar em diferentes implementações.

  • byte - valor com sinal de 8 bits,

  • short - valor com sinal de 16 bits,

  • char - valor de 16 bits sem sinal,

  • int - valor de 32 bits com sinal,

  • long - valor de 64 bits com sinal,

  • float - valor de ponto flutuante de precisão única de 32 bits correspondente ao padrão IEEE 754,

  • double - valor de ponto flutuante de precisão dupla de 64 bits correspondente ao padrão IEEE 754.

 

Q4. Qual é a diferença entre uma classe abstrata e uma interface? Quais são os casos de uso de um e do outro?

Uma classe abstrata é umclass com o modificadorabstract em sua definição. Não pode ser instanciado, mas pode ser uma subclasse. A interface é um tipo descrito com a palavra-chaveinterface. Também não pode ser instanciado, mas pode ser implementado.

A principal diferença entre uma classe abstrata e uma interface é que uma classe pode implementar várias interfaces, mas estender apenas uma classe abstrata.

Uma classeabstract geralmente é usada como um tipo base em alguma hierarquia de classes e significa a intenção principal de todas as classes que herdam dela.

Uma classeabstract também pode implementar alguns métodos básicos necessários em todas as subclasses. Por exemplo, a maioria das coleções de mapas no JDK herdam da classeAbstractMap que implementa muitos métodos usados ​​pelas subclasses (como o métodoequals).

Uma interface especifica algum contrato com o qual a classe concorda. Uma interface implementada pode significar não apenas a principal intenção da classe, mas também alguns contratos adicionais.

Por exemplo, se uma classe implementa a interfaceComparable, isso significa que as instâncias dessa classe podem ser comparadas, seja qual for o objetivo principal dessa classe.

 

Q5. Quais são as restrições sobre os membros (campos e métodos) de um tipo de interface?

Uma interface pode declarar campos, mas eles são declarados implicitamente comopublic,staticefinal, mesmo se você não especificar esses modificadores. Consequentemente, você não pode definir explicitamente um campo de interface comoprivate. Em essência, uma interface pode ter apenas campos constantes, não campos de instância.

Todos os métodos de uma interface também são implicitamentepublic. Eles também podem ser (implicitamente)abstract oudefault.

 

Q6. Qual é a diferença entre uma classe interna e uma classe aninhada estática?

Simplificando - uma classe aninhada é basicamente uma classe definida dentro de outra classe.

Classes aninhadas se enquadram em duas categorias com propriedades muito diferentes. Uma classe interna é uma classe que não pode ser instanciada sem instanciar a classe envolvente primeiro, ou seja, qualquer instância de uma classe interna está implicitamente vinculada a alguma instância da classe envolvente.

Aqui está um exemplo de uma classe interna - você pode ver que ela pode acessar a referência à instância da classe externa na forma de construçãoOuterClass1.this:

public class OuterClass1 {

    public class InnerClass {

        public OuterClass1 getOuterInstance() {
            return OuterClass1.this;
        }

    }

}

Para instanciar essa classe interna, você precisa ter uma instância de uma classe externa:

OuterClass1 outerClass1 = new OuterClass1();
OuterClass1.InnerClass innerClass = outerClass1.new InnerClass();

A classe aninhada estática é bem diferente. Sintaticamente, é apenas uma classe aninhada com o modificadorstatic em sua definição.

Na prática, isso significa que essa classe pode ser instanciada como qualquer outra classe, sem vinculá-la a qualquer instância da classe envolvente:

public class OuterClass2 {

    public static class StaticNestedClass {
    }

}

Para instanciar tal classe, você não precisa de uma instância de classe externa:

OuterClass2.StaticNestedClass staticNestedClass = new OuterClass2.StaticNestedClass();

 

Q7. O Java tem herança múltipla?

Java não suporta a herança múltipla para classes, o que significa que uma classe só pode herdar de uma única superclasse.

Mas você pode implementar várias interfaces com uma única classe, e alguns dos métodos dessas interfaces podem ser definidos comodefaulte ter uma implementação. Isso permite que você tenha uma maneira mais segura de misturar diferentes funcionalidades em uma única classe.

 

Q8. Quais são as classes de wrapper? O que é Autoboxing?

Para cada um dos oito tipos primitivos em Java, há uma classe de wrapper que pode ser usada para quebrar um valor primitivo e usá-lo como um objeto. Essas classes são, correspondentemente,Boolean,Byte,Short,Character,Integer,Float,Long eDouble. Esses wrappers podem ser úteis, por exemplo, quando você precisa colocar um valor primitivo em uma coleção genérica, que aceita apenas objetos de referência.

List list = new ArrayList<>();
list.add(new Integer(5));

Para evitar problemas de conversão manual de primitivas, uma conversão automática conhecida como autoboxing / auto unboxing é fornecida pelo compilador Java.

List list = new ArrayList<>();
list.add(5);
int value = list.get(0);

 

Q9. Descreva a diferença entre iguais () e ==

O operador == permite comparar dois objetos para "igualdade" (ou seja, que ambas as variáveis ​​se referem ao mesmo objeto na memória). É importante lembrar que a palavra-chavenew sempre cria um novo objeto que não passará a igualdade== com nenhum outro objeto, mesmo que pareçam ter o mesmo valor:

String string1 = new String("Hello");
String string2 = new String("Hello");

assertFalse(string1 == string2);

Além disso, o operador == permite comparar valores primitivos:

int i1 = 5;
int i2 = 5;

assertTrue(i1 == i2);

O métodoequals() é definido na classejava.lang.Object e, portanto, está disponível para qualquer tipo de referência. Por padrão, ele simplesmente verifica se o objeto é o mesmo através do operador ==. Mas geralmente é substituído nas subclasses para fornecer a semântica específica de comparação para uma classe.

Por exemplo, para a classeString, este método verifica se as strings contêm os mesmos caracteres:

String string1 = new String("Hello");
String string2 = new String("Hello");

assertTrue(string1.equals(string2));

 

Q10. Suponha que você tenha uma variável que faça referência a uma instância de um tipo de classe. Como você verifica se um objeto é uma instância desta classe?

Você não pode usar a palavra-chaveinstanceof neste caso porque ela só funciona se você fornecer o nome real da classe como um literal.

Felizmente, a classeClass tem um métodoisInstance que permite verificar se um objeto é uma instância desta classe:

Class integerClass = new Integer(5).getClass();
assertTrue(integerClass.isInstance(new Integer(4)));

 

Q11. O que é uma classe anônima? Descreva seu caso de uso.

Classe anônima é uma classe one-shot definida no mesmo local em que sua instância é necessária. Esta classe é definida e instanciada no mesmo local, portanto, não precisa de um nome.

Antes do Java 8, você costumava usar uma classe anônima para definir a implementação de uma única interface de método, comoRunnable. No Java 8, lambdas são usadas em vez de interfaces de método abstrato único. Porém, as classes anônimas ainda têm casos de uso, por exemplo, quando você precisa de uma instância de uma interface com vários métodos ou de uma classe com alguns recursos adicionais.

Veja como você pode criar e preencher um mapa:

Map ages = new HashMap(){{
    put("David", 30);
    put("John", 25);
    put("Mary", 29);
    put("Sophie", 22);
}};