{
  "assessmentTests": {
    "java_test": {
      "name": "Teste Java",
      "desc": "30 questões de cenários sobre sintaxe/tipos, métodos/sobrecarga/escopo, coleções/genéricos/fluxo de controle, e exceções/concorrência/idiomas — descubra se seu Java corresponde ao que uma vaga de emprego significa por Java Forte.",
      "recommendation": "Seu perfil de habilidades em Java",
      "results": {
        "beginner": {
          "name": "Beginner",
          "desc": "You can write working classes and methods, but the questions you missed cluster around identity and defaults rather than syntax — String == vs .equals, why new String(\"x\") never reuses the string pool, what a numeric field defaults to versus a wrapper field. None of this is about being bad at Java; these are the specific rules that trip up people who learned the language by trial and error rather than from how references and primitives actually work under the hood. They matter at work because each one is where code compiles, runs, and quietly does the wrong thing.",
          "recommendation": "Start with three things, in this order: why == on two String objects compares references, not characters (and .equals is what you almost always want), how int overflow wraps silently instead of throwing, and why an uninitialized Integer field defaults to null while an int field defaults to 0. Oracle's official Java tutorials and Baeldung both cover all three with runnable examples."
        },
        "intermediate": {
          "name": "Intermediate",
          "desc": "You handle everyday application code comfortably — classes, collections, straightforward control flow — and would not be slowed down by routine feature work. The gap between here and Advanced is mostly in what happens when Java's rules interact with concurrency and generics: a static method resolved by declared type instead of the object you expected, a HashMap iteration order you silently relied on, a ConcurrentModificationException from removing an item mid-loop. Those are the kind of bugs that pass a quick read-through and only show up under a specific runtime condition.",
          "recommendation": "Focus on how the compiler's static view of your code differs from what runs: overload resolution and static-method hiding by declared type rather than runtime type, why HashMap gives no iteration-order guarantee, and why modifying a list directly while iterating throws ConcurrentModificationException. Then generics erasure, since that trips up people who already know collections individually."
        },
        "advanced": {
          "name": "Advanced",
          "desc": "This is the level most job postings mean by \"strong Java.\" You read a class with static blocks, instance blocks and multiple constructors and can predict the exact order they run in, you know why a finally block can silently discard a try block's return value, and you reach for try-with-resources instead of a manual finally-close because you know the ordering guarantee it gives you. What separates this band from the top is the concurrent and interface-level side of the work: what synchronized actually locks, what volatile does and does not guarantee, and how default-method conflicts get resolved.",
          "recommendation": "Push into the parts that protect code other threads and other interfaces also touch: the difference between what a synchronized instance method locks versus a synchronized static method, why volatile gives visibility but not atomicity for counter++, and how Java forces you to resolve a default-method diamond by hand. Baeldung's and Java Concurrency in Practice's material on the Java Memory Model is the natural next stop for both."
        },
        "expert": {
          "name": "Expert",
          "desc": "You scored at the top of every section — core syntax and types, OOP mechanics and scope, collections and generics, and exceptions, concurrency and idioms. Practically, that means you can be handed a stranger's class and explain why a compile error, a runtime exception, or a silently wrong value happens, not just what the syntax says it should do, which is the harder and more valuable skill. At this level the language itself is rarely the limiting factor; the limit is usually concurrency design or the shape of the data underneath it.",
          "recommendation": "The returns are now in design and diagnosis: reading a thread dump before assuming a hang is a deadlock, choosing between synchronized, java.util.concurrent locks and atomics as a tradeoff rather than a default, and stream-pipeline design that stays lazy on purpose rather than by accident. If you are being screened for a role, describe a concurrency bug like a missed happens-before edge or a ConcurrentModificationException you found in production rather than naming Java features — it demonstrates the reasoning, not just the vocabulary."
        }
      },
      "questions": [
        {
          "question": "Você cria dois objetos String: String a = new String(\"cat\"); String b = new String(\"cat\"); O que a == b avalia para?",
          "options": [
            {
              "icon": "",
              "label": "true — String literals com os mesmos caracteres são sempre o mesmo objeto"
            },
            {
              "icon": "",
              "label": "true — new String(...) reutiliza o string pool, então texto idêntico sempre compartilha um objeto"
            },
            {
              "icon": "",
              "label": "Um erro de compilação — == não pode ser usado para comparar objetos String"
            },
            {
              "icon": "",
              "label": "false — new String(...) sempre aloca um novo objeto no heap, então == compara duas referências diferentes mesmo que .equals(b) retornaria true"
            }
          ]
        },
        {
          "question": "Integer x = 100; Integer y = 100; System.out.println(x == y); depois Integer x2 = 200; Integer y2 = 200; System.out.println(x2 == y2); O que as duas linhas imprimem?",
          "options": [
            {
              "icon": "",
              "label": "true depois true — objetos Integer autoboxed são sempre cacheados independente do valor"
            },
            {
              "icon": "",
              "label": "false depois false — == em objetos Integer é sempre uma comparação de referência, então valores iguais nunca correspondem"
            },
            {
              "icon": "",
              "label": "true depois false, mas apenas porque 200 transborda um byte — o limite do cache não está relacionado a -128..127"
            },
            {
              "icon": "",
              "label": "true depois false — valores Integer autoboxed de -128 a 127 são cacheados e compartilhados, então 100 reutiliza um objeto, mas 200 cai fora do cache e autoboxes em dois objetos separados"
            }
          ]
        },
        {
          "question": "int max = Integer.MAX_VALUE; System.out.println(max + 1); O que acontece?",
          "options": [
            {
              "icon": "",
              "label": "Lança uma ArithmeticException por transbordamento de inteiro"
            },
            {
              "icon": "",
              "label": "Imprime Integer.MAX_VALUE novamente, porque Java fixa no máximo do tipo"
            },
            {
              "icon": "",
              "label": "Imprime Integer.MIN_VALUE — aritmética int silenciosamente envolve ao transbordar em vez de lançar"
            },
            {
              "icon": "",
              "label": "É um erro de compilação — o compilador detecta o transbordamento antecipadamente"
            }
          ]
        },
        {
          "question": "System.out.println(0.1 + 0.2 == 0.3); O que isso imprime, e por quê?",
          "options": [
            {
              "icon": "",
              "label": "false — 0.1, 0.2 e 0.3 não podem ser representados exatamente em ponto flutuante binário, então a soma carrega erro de arredondamento que a torna não bit-a-bit igual a 0.3"
            },
            {
              "icon": "",
              "label": "true — Java arredonda aritmética de double ao valor decimal mais próximo representável antes de comparar"
            },
            {
              "icon": "",
              "label": "true — adição de dois doubles é sempre exata para valores com dois dígitos decimais"
            },
            {
              "icon": "",
              "label": "Lança uma exceção, porque == não é definido para double em Java"
            }
          ]
        },
        {
          "question": "Uma classe declara dois campos de instância sem inicializador: int count; e Integer total; Antes do construtor rodar, quais são seus valores padrão?",
          "options": [
            {
              "icon": "",
              "label": "Ambos padrão a 0, porque Integer autoboxes para int na inicialização de campo"
            },
            {
              "icon": "",
              "label": "count é 0 e total é 0, boxed automaticamente na primeira vez que é lido"
            },
            {
              "icon": "",
              "label": "count é 0 e total é null — campos numéricos primitivos padrão a zero, mas um campo de referência wrapper não inicializado padrão a null como qualquer outra referência de objeto"
            },
            {
              "icon": "",
              "label": "Ambos padrão a null até serem explicitamente atribuídos, já que Java não tem padrões numéricos implícitos"
            }
          ]
        },
        {
          "question": "Um loop roda 1000 vezes, cada vez fazendo result += \"x\"; em um resultado String. O que realmente acontece sob o capô cada iteração?",
          "options": [
            {
              "icon": "",
              "label": "O array de caracteres interno do objeto String existente é estendido no lugar"
            },
            {
              "icon": "",
              "label": "Java automaticamente agrupa as concatenações e só aloca um objeto String final"
            },
            {
              "icon": "",
              "label": "Compila para uma única chamada StringBuilder.append() compartilhada em todas as 1000 iterações, sem objeto extra alocado por iteração"
            },
            {
              "icon": "",
              "label": "Um novo objeto String é criado e result é reatribuído para apontar a ele — o objeto String anterior é descartado, porque String é imutável e += não pode modificá-lo no lugar"
            }
          ]
        },
        {
          "question": "final List<String> names = new ArrayList<>(); Qual desses é verdadeiro?",
          "options": [
            {
              "icon": "",
              "label": "names.add(\"Ana\") compila e funciona bem — final apenas previne reatribuir a própria referência names, não mutar o objeto que ele aponta"
            },
            {
              "icon": "",
              "label": "names.add(\"Ana\") é um erro de compilação, porque final torna a própria lista não modificável"
            },
            {
              "icon": "",
              "label": "A lista é thread-safe para escritas concorrentes porque foi declarada final"
            },
            {
              "icon": "",
              "label": "final em uma variável local não tem efeito a menos que o tipo também seja declarado imutável"
            }
          ]
        },
        {
          "question": "int[] a = {1, 2, 3}; int[] b = {1, 2, 3}; System.out.println(a == b); System.out.println(Arrays.equals(a, b)); O que as duas linhas imprimem?",
          "options": [
            {
              "icon": "",
              "label": "false depois true — == compara referências de array (dois objetos de array diferentes), enquanto Arrays.equals compara os elementos"
            },
            {
              "icon": "",
              "label": "true depois true — arrays com conteúdo idêntico são o mesmo objeto em Java"
            },
            {
              "icon": "",
              "label": "false depois false — Arrays.equals só funciona para arrays de objetos, não arrays de int primitivos"
            },
            {
              "icon": "",
              "label": "true depois false — == em arrays compara conteúdo, e Arrays.equals é redundante"
            }
          ]
        },
        {
          "question": "void show(Object o) { print(\"Object\"); } void show(String s) { print(\"String\"); } Object ref = \"hello\"; show(ref); Qual sobrecarga roda, e por quê?",
          "options": [
            {
              "icon": "",
              "label": "show(String) roda, porque Java inspeciona a classe real em tempo de execução do objeto antes de escolher a sobrecarga"
            },
            {
              "icon": "",
              "label": "show(Object) roda — resolução de sobrecarga é decidida em tempo de compilação usando o tipo declarado da variável, não o tipo real em tempo de execução do objeto que ela refere"
            },
            {
              "icon": "",
              "label": "É um erro de compilação — Object não pode ser passado onde uma sobrecarga mais específica existe"
            },
            {
              "icon": "",
              "label": "Ambos os métodos rodam, uma vez cada, porque Java resolve sobrecargas tentando cada correspondência"
            }
          ]
        },
        {
          "question": "Class Base tem static void greet() { print(\"Base\"); }. Class Derived estende Base e também declara static void greet() { print(\"Derived\"); }. Você escreve: Base ref = new Derived(); ref.greet(); O que imprime?",
          "options": [
            {
              "icon": "",
              "label": "Derived — métodos estáticos sobrescrevem como métodos de instância, seguindo o tipo real em tempo de execução do objeto"
            },
            {
              "icon": "",
              "label": "É um erro de compilação — métodos estáticos não podem ser chamados através de uma referência de instância"
            },
            {
              "icon": "",
              "label": "Base — métodos estáticos não fazem dispatch dinâmico; a chamada é decidida usando o tipo declarado da variável, não o tipo em tempo de execução, então ref.greet() chama Base.greet()"
            },
            {
              "icon": "",
              "label": "Um erro em tempo de execução — Derived.greet() é inacessível por ser privado"
            }
          ]
        },
        {
          "question": "Dentro de um método, você escreve int count = 0; depois tenta referenciar count em um método aninhado (como uma classe local ou lambda). O que acontece?",
          "options": [
            {
              "icon": "",
              "label": "Funciona — count é acessível porque a closure captura a variável"
            },
            {
              "icon": "",
              "label": "Um erro de compilação — count não é effectivamente final (você o atribuiu), então não pode ser capturado por uma closure"
            },
            {
              "icon": "",
              "label": "Um erro de compilação — variáveis locais nunca podem ser acessíveis de dentro de uma classe aninhada"
            },
            {
              "icon": "",
              "label": "Funciona apenas se count tiver sido declarado final explicitamente"
            }
          ]
        },
        {
          "question": "void rename(StringBuilder sb) { sb = new StringBuilder(\"novo\"); } é chamado com um StringBuilder. Depois que o método retorna, a variável chamadora foi modificada?",
          "options": [
            {
              "icon": "",
              "label": "Sim — sb é uma referência mutable e o método a modifica"
            },
            {
              "icon": "",
              "label": "Não — a reatribuição sb = new StringBuilder(...) só muda a cópia local do parâmetro, não o objeto original da variável chamadora"
            },
            {
              "icon": "",
              "label": "Sim, porque StringBuilder.append() foi chamado implicitamente"
            },
            {
              "icon": "",
              "label": "Depende se o objeto original foi declarado final"
            }
          ]
        },
        {
          "question": "Uma classe tem um bloco inicializador estático, um bloco inicializador de instância e um construtor. Em que ordem eles executam quando um novo objeto é criado?",
          "options": [
            {
              "icon": "",
              "label": "construtor, depois bloco estático, depois bloco de instância"
            },
            {
              "icon": "",
              "label": "Bloco estático uma única vez quando a classe é carregada, depois bloco de instância, depois construtor cada vez que um novo objeto é criado"
            },
            {
              "icon": "",
              "label": "Bloco de instância, depois construtor — blocos estáticos nunca executam"
            },
            {
              "icon": "",
              "label": "Construtor, depois bloco de instância, depois bloco estático — a ordem reversa garante limpeza adequada"
            }
          ]
        },
        {
          "question": "Uma classe tem void log(String s) e void log(String... args). Você chama log(\"msg\"). Qual é chamado?",
          "options": [
            {
              "icon": "",
              "label": "log(String... args) — varargs é preferido quando disponível"
            },
            {
              "icon": "",
              "label": "log(String s) — resolução de sobrecarga prefere uma correspondência exata a uma conversão para varargs"
            },
            {
              "icon": "",
              "label": "Um erro de compilação — ambos coincidem igualmente"
            },
            {
              "icon": "",
              "label": "Ambos são chamados em sequência"
            }
          ]
        },
        {
          "question": "A primeira linha de um construtor chama this(0); para delegar a outro construtor. O que é verdadeiro?",
          "options": [
            {
              "icon": "",
              "label": "Isso é uma chamada de função regular e pode aparecer em qualquer lugar no construtor"
            },
            {
              "icon": "",
              "label": "this(...) ou super(...) deve ser a primeira instrução (ou está presente em ambos), caso contrário é um erro de compilação"
            },
            {
              "icon": "",
              "label": "this() é opcional — se omitido, super() é chamado automaticamente"
            },
            {
              "icon": "",
              "label": "Você não pode delegar entre construtores — isso apenas funciona com super()"
            }
          ]
        },
        {
          "question": "Você precisa de uma lista que terá elementos inseridos na frente extremamente frequentemente, e leituras de acesso aleatório são raras. Qual é o melhor ajuste estrutural, e por quê?",
          "options": [
            {
              "icon": "",
              "label": "ArrayList — seu array de suporte contíguo torna toda operação, incluindo inserção na frente, mais rápida que uma estrutura encadeada"
            },
            {
              "icon": "",
              "label": "Elas têm desempenho idêntico, porque ambas implementam a interface List com as mesmas garantias de tempo"
            },
            {
              "icon": "",
              "label": "LinkedList — inserir na frente é O(1) porque apenas religa um nó, enquanto ArrayList precisa deslocar cada elemento existente uma posição acima, tornando a inserção na frente O(n)"
            },
            {
              "icon": "",
              "label": "LinkedList, porque suporta acesso aleatório em O(1), o que ArrayList não suporta"
            }
          ]
        },
        {
          "question": "Você insere cinco entradas em um HashMap simples em uma ordem específica. Qual é a ordem de iteração?",
          "options": [
            {
              "icon": "",
              "label": "A mesma ordem em que foram inseridas — HashMap mantém ordem de inserção"
            },
            {
              "icon": "",
              "label": "Ordem não especificada — HashMap não garante nenhuma ordem de iteração"
            },
            {
              "icon": "",
              "label": "Ordem de hash — determinístico baseado em hashCode(), mas não relacionado à ordem de inserção"
            },
            {
              "icon": "",
              "label": "Ordem aleatória cada vez que você itera"
            }
          ]
        },
        {
          "question": "Quantas chaves null um java.util.HashMap simples pode conter de uma vez, e quantos valores null?",
          "options": [
            {
              "icon": "",
              "label": "Nenhuma chave null, mas múltiplos valores null — null não é um hash válido"
            },
            {
              "icon": "",
              "label": "Uma chave null (como qualquer outra chave distinta) e infinitos valores null"
            },
            {
              "icon": "",
              "label": "Nenhum — HashMap rejeita completamente null"
            },
            {
              "icon": "",
              "label": "Múltiplas chaves null porque null é sempre válido"
            }
          ]
        },
        {
          "question": "Você itera uma List com um loop for-each e chama list.remove(item) diretamente. O que acontece?",
          "options": [
            {
              "icon": "",
              "label": "Funciona silenciosamente — o iterator se adapta ao tamanho em mudança"
            },
            {
              "icon": "",
              "label": "ConcurrentModificationException — modificar uma Collection enquanto itera sobre ela por um iterator lança uma exceção"
            },
            {
              "icon": "",
              "label": "O loop pula o próximo item — a remoção desalinha os índices"
            },
            {
              "icon": "",
              "label": "Depende do tipo de List — ArrayList falha mas LinkedList funciona"
            }
          ]
        },
        {
          "question": "Em tempo de execução, dada uma List<String> list, o que você realmente pode determinar sobre o tipo de elementos?",
          "options": [
            {
              "icon": "",
              "label": "Você sabe que contém apenas Strings porque genéricos são preservados em tempo de execução"
            },
            {
              "icon": "",
              "label": "Genéricos são apagados em tempo de execução — você só sabe que é uma List, não que contém Strings. Você pode recuperar um objeto e ele pode ser qualquer tipo"
            },
            {
              "icon": "",
              "label": "Você sabe que contém Strings porque o compilador insere casts de tempo de execução"
            },
            {
              "icon": "",
              "label": "Tipo errado — List<String> é sempre específico em tempo de execução"
            }
          ]
        },
        {
          "question": "switch (day) { case 1: case 2: print(\"Weekday\"); break; case 6: print(\"Saturday\"); case 7: print(\"Sunday\"); break; default: print(\"?\"); } Se day é 6, o que imprime?",
          "options": [
            {
              "icon": "",
              "label": "Apenas Saturday — cada bloco case em um switch sempre para depois do seu próprio print"
            },
            {
              "icon": "",
              "label": "Apenas Sunday — a correspondência começa no case 6, mas só o último label correspondente antes do break executa"
            },
            {
              "icon": "",
              "label": "Nada imprime, porque day 6 não tem um case correspondente com seu próprio break diretamente abaixo"
            },
            {
              "icon": "",
              "label": "Saturday e depois Sunday — case 6 não tem break, então a execução cai para o código do case seguinte antes de atingir o break seguinte"
            }
          ]
        },
        {
          "question": "Uma classe Product precisa de uma única ordem de classificação \"natural\" óbvia por preço. O que a classe deve implementar?",
          "options": [
            {
              "icon": "",
              "label": "A anotação @SortBy(\"price\")"
            },
            {
              "icon": "",
              "label": "A interface Comparable e implementar compareTo(), que será usado por padrão por Collections.sort() e Arrays.sort()"
            },
            {
              "icon": "",
              "label": "A interface Comparator"
            },
            {
              "icon": "",
              "label": "Nada — Java classifica automaticamente por qualquer campo público"
            }
          ]
        },
        {
          "question": "Um método chama new FileReader(path), que declara throws IOException. O que é verdadeiro sobre tratar a exceção?",
          "options": [
            {
              "icon": "",
              "label": "É opcional — IOException é uma exceção RuntimeException"
            },
            {
              "icon": "",
              "label": "É obrigatório — você deve catch IOException ou declarar throws no seu método"
            },
            {
              "icon": "",
              "label": "IOException não pode ser lançada por construtores"
            },
            {
              "icon": "",
              "label": "Depende se o caminho do arquivo existe"
            }
          ]
        },
        {
          "question": "try (Resource a = new Resource(\"A\"); Resource b = new Resource(\"B\")) { } O que garante um try-with-resources?",
          "options": [
            {
              "icon": "",
              "label": "Nada — try-with-resources é apenas sintaxe"
            },
            {
              "icon": "",
              "label": "a e b são fechados automaticamente no fim do bloco (na ordem reversa de criação), chamando close() em cada um, mesmo que uma exceção seja lançada"
            },
            {
              "icon": "",
              "label": "a é fechado antes de b porque foi declarado primeiro"
            },
            {
              "icon": "",
              "label": "Nenhum deles é fechado — você deve fechá-los manualmente"
            }
          ]
        },
        {
          "question": "int getValue() { try { return 1; } finally { return 2; } } O que essa função retorna?",
          "options": [
            {
              "icon": "",
              "label": "1 — o valor de retorno do bloco try já foi confirmado antes de finally ser executado, então finally não pode alterá-lo"
            },
            {
              "icon": "",
              "label": "Lança uma exceção em tempo de execução, porque um método não pode retornar de dois lugares"
            },
            {
              "icon": "",
              "label": "2 — um return dentro de finally sobrescreve e substitui qualquer return já em andamento vindo do bloco try, descartando o valor 1 por completo"
            },
            {
              "icon": "",
              "label": "É um erro de compilação — finally não pode conter uma instrução return"
            }
          ]
        },
        {
          "question": "try { ... } catch (IOException e) { ... } catch (FileNotFoundException e) { ... } Que ordem é correta?",
          "options": [
            {
              "icon": "",
              "label": "IOException depois FileNotFoundException — a ordem não importa"
            },
            {
              "icon": "",
              "label": "FileNotFoundException depois IOException — subclasses devem ser capturadas antes de sua superclasse, ou o bloco de superclasse nunca é atingido"
            },
            {
              "icon": "",
              "label": "Qualquer ordem — Java reordena automaticamente"
            },
            {
              "icon": "",
              "label": "Ambas em um único bloco catch — catch (IOException | FileNotFoundException e)"
            }
          ]
        },
        {
          "question": "Uma classe tem um método sincronizado de instância process() e um método sincronizado estático process(). Eles podem coexistir?",
          "options": [
            {
              "icon": "",
              "label": "Não — synchronized conflita com sobrecarga"
            },
            {
              "icon": "",
              "label": "Sim — eles sincronizam em objetos diferentes (um sincroniza em 'this', o outro sincroniza em um lock de classe), portanto não conflitam"
            },
            {
              "icon": "",
              "label": "Apenas se o nome for diferente"
            },
            {
              "icon": "",
              "label": "Não — o segundo declarado gera um erro de compilação"
            }
          ]
        },
        {
          "question": "Um campo é declarado volatile int counter = 0;, e múltiplos threads rodam loop { counter++; }. Qual é verdadeiro?",
          "options": [
            {
              "icon": "",
              "label": "volatile previne race conditions — counter sempre terá o valor correto"
            },
            {
              "icon": "",
              "label": "volatile garante que uma leitura sempre obtém o valor mais recente escrito por outro thread, mas ++ ainda é não-atômico, então race conditions podem ocorrer"
            },
            {
              "icon": "",
              "label": "volatile é sinônimo de synchronized"
            },
            {
              "icon": "",
              "label": "volatile não tem efeito com leitura-modificação-escrita como ++"
            }
          ]
        },
        {
          "question": "Interface A e interface B cada uma declaram um default method describe(). Uma classe implementa ambas. O que acontece?",
          "options": [
            {
              "icon": "",
              "label": "Um erro de compilação — ambíguo qual implementação usar"
            },
            {
              "icon": "",
              "label": "A classe herda automaticamente um — Java escolhe a primeira alfabeticamente"
            },
            {
              "icon": "",
              "label": "A classe deve sobrescrever describe(), caso contrário é um erro de compilação"
            },
            {
              "icon": "",
              "label": "Ambas as implementações rodam em sequência"
            }
          ]
        },
        {
          "question": "list.stream().filter(x -> x > 0).map(x -> x * 2); é escrito mas o resultado nunca é atribuído a uma variável. O que acontece?",
          "options": [
            {
              "icon": "",
              "label": "Um erro — você deve atribuir um Stream a algo"
            },
            {
              "icon": "",
              "label": "Nada — Streams são lazy. Nenhuma operação intermediária (filter, map) executa até uma operação terminal (como forEach, collect) ser chamada"
            },
            {
              "icon": "",
              "label": "A lista é transformada no lugar"
            },
            {
              "icon": "",
              "label": "O filter e o map são executados imediatamente"
            }
          ]
        }
      ],
      "optionOrderVersion": "0154f9576054ad6f"
    }
  }
}