Perguntas da entrevista do Java 8 (+ respostas)
1. Introdução
Neste artigo, exploraremos algumas das questões relacionadas ao JDK8 que podem aparecer durante uma entrevista.
O Java 8 é uma versão da plataforma repleta de novos recursos de linguagem e classes de biblioteca. A maioria desses novos recursos é voltada para a obtenção de código mais limpo e compacto, e alguns adicionam novas funcionalidades que nunca foram suportadas em Java.
Leitura adicional:
Perguntas sobre entrevistas de gerenciamento de memória em Java (+ respostas)
Um conjunto de perguntas populares da entrevista relacionadas ao Gerenciamento de Memória e, é claro, respostas.
Perguntas da entrevista sobre coleções Java
Um conjunto de perguntas práticas sobre entrevistas em Java relacionadas a coleções
2. Conhecimento geral de Java 8
Q1. Quais novos recursos foram adicionados ao Java 8?
O Java 8 é fornecido com vários novos recursos, mas os mais significativos são os seguintes:
-
Lambda Expressions - um novo recurso de linguagem que permite tratar ações como objetos
-
Method References - habilita a definição de Expressões Lambda referindo-se a métodos diretamente usando seus nomes
-
Optional - classe de wrapper especial usada para expressar opcionalidade
-
Functional Interface - uma interface com no máximo um método abstrato, a implementação pode ser fornecida usando uma expressão Lambda
-
Default methods - nos dá a capacidade de adicionar implementações completas em interfaces além de métodos abstratos
-
Nashorn, JavaScript Engine - mecanismo baseado em Java para executar e avaliar código JavaScript
-
Stream API - uma classe iteradora especial que permite o processamento de coleções de objetos de maneira funcional
-
Date API - uma API de data inspirada no JodaTime aprimorada e imutável
Junto com esses novos recursos, muitos aprimoramentos de recursos são feitos sob o capô, tanto no nível do compilador quanto da JVM.
3. Referências de método
Q1. O que é uma referência de método?
Uma referência de método é uma construção Java 8 que pode ser usada para referenciar um método sem invocá-lo. É usado para tratar métodos como expressões lambda. Eles funcionam apenas como açúcar sintático para reduzir a verbosidade de algumas lambdas. Dessa forma, o seguinte código:
(o) -> o.toString();
pode se tornar:
Object::toString();
Uma referência de método pode ser identificada por dois pontos duplos, separando um nome de classe ou objeto e o nome do método. Tem variações diferentes, como referência de construtor:
String::new;
Referência do método estático:
String::valueOf;
Referência do método de instância vinculada:
str::toString;
Referência do método da instância não vinculada:
String::toString;
Q2. Qual é o significado de String :: Valueof Expression?
É uma referência de método estática para o métodovalueOf da classeString.
4. Opcional
Q1. O que éOptional? Como pode ser usado?
Optional é uma nova classe em Java 8 que encapsula um valor opcional, ou seja, um valor que está lá ou não. É um invólucro em torno de um objeto e você pode pensar nele como um contêiner zero ou um elemento.
Optional tem um valorOptional.empty() especial em vez denull empacotado. Portanto, ele pode ser usado em vez de um valor anulável para se livrar deNullPointerException em muitos casos.
Você pode ler um artigo dedicado sobreOptionalhere.
O objetivo principal deOptional, conforme projetado por seus criadores, era ser um tipo de retorno de métodos que anteriormente retornariamnull. Tais métodos exigiriam que você escrevesse um código padrão para verificar o valor de retorno e, às vezes, poderia esquecer de fazer uma verificação defensiva. No Java 8, um tipo de retornoOptional exige explicitamente que você trate de valores agrupados nulos ou não nulos de maneira diferente.
Por exemplo, o métodoStream.min() calcula o valor mínimo em um fluxo de valores. Mas e se o fluxo estiver vazio? Se não fosse porOptional, o método retornarianull ou lançaria uma exceção.
Mas ele retorna um valorOptional que pode serOptional.empty() (o segundo caso). Isso nos permite lidar facilmente com esse caso:
int min1 = Arrays.stream(new int[]{1, 2, 3, 4, 5})
.min()
.orElse(0);
assertEquals(1, min1);
int min2 = Arrays.stream(new int[]{})
.min()
.orElse(0);
assertEquals(0, min2);
É importante notar queOptional não é uma classe de uso geral comoOption em Scala. Não é recomendado para ser usado como um valor de campo em classes de entidade, o que é claramente indicado por não implementar a interfaceSerializable.
5. Interfaces Funcionais
Q1. Descreva algumas das interfaces funcionais na biblioteca padrão.
Existem várias interfaces funcionais no pacotejava.util.function, as mais comuns incluem, mas não se limitam a:
-
Function - leva um argumento e retorna um resultado
-
Consumer - leva um argumento e não retorna nenhum resultado (representa um efeito colateral)
-
Supplier - não leva argumento e retorna um resultado
-
Predicate - leva um argumento e retorna um booleano
-
BiFunction - leva dois argumentos e retorna um resultado
-
BinaryOperator - é semelhante a aBiFunction, pegando dois argumentos e retornando um resultado. Os dois argumentos e o resultado são todos do mesmo tipo
-
UnaryOperator - é semelhante aFunction, pegando um único argumento e retornando um resultado do mesmo tipo
Para obter mais informações sobre interfaces funcionais, consulte o artigo“Functional Interfaces in Java 8”.
Q2. O que é uma interface funcional? Quais são as regras para definir uma interface funcional?
Uma interface funcional é uma interface com nem mais, nem menos, mas um único método abstrato (métodosdefault não contam).
Onde uma instância dessa interface é necessária, uma Expressão Lambda pode ser usada. Colocado de forma mais formal:Functional interfaces fornece tipos de destino para expressões lambda e referências de método.
Os argumentos e o tipo de retorno dessa expressão correspondem diretamente aos do método abstrato único.
Por exemplo, a interfaceRunnable é uma interface funcional, então, em vez de:
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("Hello World!");
}
});
você poderia simplesmente fazer:
Thread thread = new Thread(() -> System.out.println("Hello World!"));
As interfaces funcionais são geralmente anotadas com a anotação@FunctionalInterface - que é informativa e não afeta a semântica.
6. Método Padrão
Q1. O que é um método padrão e quando o usamos?
Um método padrão é um método com uma implementação - que pode ser encontrada em uma interface.
Podemos usar um método padrão para adicionar uma nova funcionalidade a uma interface, mantendo a compatibilidade com as classes que já estão implementando a interface:
public interface Vehicle {
public void move();
default void hoot() {
System.out.println("peep!");
}
}
Normalmente, quando um novo método abstrato é adicionado a uma interface, todas as classes de implementação são interrompidas até que implementem o novo método abstrato. No Java 8, esse problema foi resolvido pelo uso do método padrão.
Por exemplo, a interfaceCollection não tem declaração de métodoforEach. Assim, adicionar esse método simplesmente quebraria a API de coleções inteiras.
Java 8 apresenta o método padrão para que a interfaceCollection possa ter uma implementação padrão do métodoforEach sem exigir que as classes que implementam essa interface implementem o mesmo.
Q2. O código a seguir será compilado?
@FunctionalInterface
public interface Function2 {
public V apply(T t, U u);
default void count() {
// increment counter
}
}
Yes. O código será compilado porque segue a especificação da interface funcional de definir apenas um único método abstrato. O segundo método,count, é um método padrão que não aumenta a contagem de métodos abstratos.
7. Expressões Lambda
Q1. O que é uma expressão Lambda e para que ela é usada
Em termos muito simples, uma expressão lambda é uma função que pode ser referenciada e transmitida como um objeto.
As expressões Lambda introduzem o processamento de estilo funcional em Java e facilitam a gravação de código compacto e fácil de ler.
Por esse motivo, as expressões lambda são um substituto natural para classes anônimas como argumentos de método. Um de seus principais usos é definir implementações em linha de interfaces funcionais.
Q2. Explique a sintaxe e as características de uma expressão Lambda
Uma expressão lambda consiste em duas partes: a parte do parâmetro e a parte das expressões separadas por uma seta para a frente, como abaixo:
params -> expressions
Qualquer expressão lambda possui as seguintes características:
-
Optional type declaration - ao declarar os parâmetros no lado esquerdo do lambda, não precisamos declarar seus tipos, pois o compilador pode inferi-los de seus valores. Entãoint param → … eparam →… são todos válidos
-
Optional parentheses - quando apenas um único parâmetro é declarado, não precisamos colocá-lo entre parênteses. Isso significa queparam → …e(param) → … são todos válidos. Mas quando mais de um parâmetro é declarado, parênteses são necessários
-
Optional curly braces - quando a parte das expressões tem apenas uma única instrução, não há necessidade de chaves. Isso significa queparam – > statementeparam – > \{statement;} são todos válidos. Mas chaves são necessárias quando há mais de uma declaração
-
Optional return statement - quando a expressão retorna um valor e está entre chaves, não precisamos de uma instrução de retorno. Isso significa que(a, b) – > \{return a+b;} e(a, b) – > \{a+b;} são válidos
8. Nashorn Javascript
Q1. O que é Nashorn em Java8?
Nashorn é o novo mecanismo de processamento Javascript para a plataforma Java que acompanha o Java 8. Até o JDK 7, a plataforma Java usava o Mozilla Rhino para o mesmo objetivo. como um mecanismo de processamento Javascript.
O Nashorn fornece melhor conformidade com a especificação JavaScript normalizada da ECMA e melhor desempenho em tempo de execução do que seu antecessor.
Q2. O que é JJS?
No Java 8,jjs é o novo executável ou ferramenta de linha de comando usada para executar código Javascript no console.
9. Streams
Q1. O que é um fluxo? Como isso difere de uma coleção?
Em termos simples, um fluxo é um iterador cuja função é aceitar um conjunto de ações a serem aplicadas em cada um dos elementos que ele contém.
The stream representa uma sequência de objetos de uma fonte, como uma coleção, que oferece suporte a operações de agregação. Eles foram projetados para tornar o processamento da coleção simples e conciso. Ao contrário das coleções, a lógica da iteração é implementada dentro do fluxo, então podemos usar métodos comomapeflatMap para realizar um processamento declarativo.
Outra diferença é que a APIStream é fluente e permite pipelining:
int sum = Arrays.stream(new int[]{1, 2, 3})
.filter(i -> i >= 2)
.map(i -> i * 3)
.sum();
E ainda outra distinção importante das coleções é que os fluxos são inerentemente carregados e processados lentamente.
Q2. Qual é a diferença entre operações intermediárias e terminais?
As operações de fluxo são combinadas em pipelines para processar fluxos. Todas as operações são intermediárias ou terminais.
Operações intermediárias são aquelas operações que retornamStream, permitindo operações adicionais em um fluxo.
Essas operações são sempre preguiçosas, ou seja, eles não processam o fluxo no local da chamada, uma operação intermediária só pode processar dados quando há uma operação no terminal. Algumas das operações intermediárias sãofilter,mapeflatMap.
As operações do terminal encerram o pipeline e iniciam o processamento do fluxo. O fluxo é passado por todas as operações intermediárias durante a chamada de operação do terminal. As operações do terminal incluemforEach,reduce, Collectesum.
Para esclarecer esse ponto, vejamos um exemplo com efeitos colaterais:
public static void main(String[] args) {
System.out.println("Stream without terminal operation");
Arrays.stream(new int[] { 1, 2, 3 }).map(i -> {
System.out.println("doubling " + i);
return i * 2;
});
System.out.println("Stream with terminal operation");
Arrays.stream(new int[] { 1, 2, 3 }).map(i -> {
System.out.println("doubling " + i);
return i * 2;
}).sum();
}
A saída será a seguinte:
Stream without terminal operation
Stream with terminal operation
doubling 1
doubling 2
doubling 3
Como você pode ver, as operações intermediárias são acionadas apenas quando existe uma operação terminal.
Q3. Qual é a diferença entreMap eflatMap operação de fluxo?
Há uma diferença na assinatura entremap eflatMap. De maneira geral, uma operaçãomap envolve seu valor de retorno dentro de seu tipo ordinal, enquantoflatMap não.
Por exemplo, emOptional, uma operaçãomap retornaria o tipoOptional<String>, enquantoflatMap retornaria o tipoString.
Portanto, após o mapeamento, é necessário desembrulhar (leia “nivelar”) o objeto para recuperar o valor, enquanto que, após o mapeamento plano, não há necessidade de o objeto já estar nivelado. O mesmo conceito é aplicado ao mapeamento e mapeamento plano emStream.
AmbosmapeflatMap são operações de fluxo intermediário que recebem uma função e aplicam esta função a todos os elementos de um fluxo.
A diferença é que paramap, essa função retorna um valor, mas paraflatMap, essa função retorna um fluxo. A operaçãoflatMap “nivela” os fluxos em um.
Aqui está um exemplo em que pegamos um mapa de nomes de usuários e listas de telefones e o "nivelamos" em uma lista de telefones de todos os usuários usandoflatMap:
Map> people = new HashMap<>();
people.put("John", Arrays.asList("555-1123", "555-3389"));
people.put("Mary", Arrays.asList("555-2243", "555-5264"));
people.put("Steve", Arrays.asList("555-6654", "555-3242"));
List phones = people.values().stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
Q4. O que é Stream Pipelining no Java 8?
O pipelining de fluxo é o conceito de encadear operações juntas. Isso é feito dividindo as operações que podem acontecer em um fluxo em duas categorias: operações intermediárias e operações de terminal.
Cada operação intermediária retorna uma instância do próprio Stream quando é executada; um número arbitrário de operações intermediárias pode, portanto, ser configurado para processar dados, formando um pipeline de processamento.
Deve haver uma operação de terminal que retorne um valor final e encerre o pipeline.
10. API Java 8 Date and Time
Q1. Conte-nos sobre a nova API de data e hora em Java 8
Um problema de longa data para desenvolvedores de Java tem sido o suporte inadequado às manipulações de data e hora exigidas pelos desenvolvedores comuns.
As classes existentes, comojava.util.DateeSimpleDateFormatter, não são thread-safe, levando a possíveis problemas de simultaneidade para os usuários.
O design inadequado da API também é uma realidade na antiga API de dados Java. Aqui está apenas um exemplo rápido - anos emjava.util.Date começam em 1900, os meses começam em 1 e os dias começam em 0, o que não é muito intuitivo.
Esses problemas e vários outros levaram à popularidade de bibliotecas de data e hora de terceiros, como o Joda-Time.
Para resolver esses problemas e fornecer melhor suporte no JDK, uma nova API de data e hora, que está livre desses problemas, foi projetada para Java SE 8 sob o pacotejava.time.
11. Conclusão
Neste artigo, exploramos algumas questões muito importantes para perguntas de entrevistas técnicas com um viés no Java 8. Esta não é de forma alguma uma lista exaustiva, mas contém apenas perguntas que achamos mais prováveis de aparecer em cada novo recurso do Java 8.
Mesmo se você estiver apenas começando, a ignorância do Java 8 não é uma boa maneira de ir em uma entrevista, especialmente quando o Java aparece fortemente em seu currículo. Portanto, é importante que você dedique algum tempo para entender as respostas a essas perguntas e possivelmente fazer mais pesquisas.
Boa sorte na sua entrevista.