Por que "B" trava e "#" voa? Desvendando a clássica pergunta do Java
\n\nSe você já escreveu loops simples em Java para exibir caracteres no console, talvez tenha notado uma estranheza: a exibição da letra minúscula 'B' pode ser visivelmente mais lenta do que a exibição da cerquilha '#'. Esse efeito um dia explodiu no Stack Overflow — a pergunta recebeu milhares de visualizações e votos. Hoje vamos entender o que está por trás desse fenômeno e como ele se relaciona com os mecanismos internos do Java, da entrada-saída no console e da bufferização.
Configuração do experimento
\nImagine que executamos dois loops que imprimem 100.000 caracteres no console: um loop exibe '#', o outro exibe 'B'. À primeira vista, a operação é idêntica. No entanto, em muitos sistemas (especialmente Windows), o primeiro loop termina em cerca de 100 ms, enquanto o segundo leva vários segundos. Por quê?
Vamos reproduzir isso em Java.
\n\nCódigo para teste
\npublic class SlowPrintTest {\n public static void main(String[] args) {\n long start = System.nanoTime();\n for (int i = 0; i < 100_000; i++) {\n System.out.print("#");\n }\n long end = System.nanoTime();\n System.out.println("\\nTempo para '#': " + (end - start) / 1_000_000 + " ms");\n\n start = System.nanoTime();\n for (int i = 0; i < 100_000; i++) {\n System.out.print("B");\n }\n end = System.nanoTime();\n System.out.println("\\nTempo para 'B': " + (end - start) / 1_000_000 + " ms");\n }\n}\n\nExecute este código no terminal, não na IDE (por exemplo, via java SlowPrintTest). O resultado vai te surpreender. No Windows com CMD, a diferença pode ser de 10 a 50 vezes.
Por que isso acontece? A raiz do problema está na renderização de fontes
\nO problema não está no System.out.print() em si nem na JVM. A principal causa está em como o sistema operacional e o terminal processam a exibição dos caracteres. System.out em Java é um wrapper sobre o fluxo de saída padrão (stdout). Os dados são transmitidos ao console, mas a velocidade de exibição na tela depende de:
- \n
- Largura do caractere — muitos consoles antigos (como o CMD no Windows) usam fontes monoespaçadas, mas o caractere
'B'pode exigir uma renderização mais complexa do que'#'. \n - Atualização da tela — o console pode realizar uma redesenho completo da linha ao exibir cada caractere, em vez de apenas adicioná-lo. \n
- Bufferização do terminal — alguns terminais bufferizam a saída em blocos, mas se o caractere exigir o cálculo da largura do glifo, isso retarda o processo. \n
O efeito é mais pronunciado no Windows Command Prompt (CMD) com fontes raster. Quando você imprime 'B', o console tenta exibir o caractere em modo gráfico, enquanto para '#' usa uma abordagem simplificada. Em terminais modernos (Windows Terminal, iTerm2, Alacritty), a diferença pode ser menos perceptível ou inexistente.
O papel da bufferização em Java
\nUm aspecto importante da otimização de saída é o uso de fluxos bufferizados. System.out por padrão não é bufferizado (ou melhor, é bufferizado no nível da JVM, mas com um tamanho pequeno). Cada chamada a print() pode acionar uma chamada de sistema write(), o que é muito custoso. Se adicionarmos bufferização via BufferedWriter, o desempenho melhora, mas a diferença entre os caracteres permanece — ela está no lado do terminal.
// Exemplo com bufferização\nBufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));\nfor (int i = 0; i < 100_000; i++) {\n bw.write("#");\n}\nbw.flush();\n\nMesmo com bufferização, se o terminal renderiza 'B' lentamente, o tempo total aumentará. Um