A resposta é "porque um scanner possui estado".
Observando o código do java.util.Scanner , você verá vários campos particulares, como um buffer e suas informações associadas, um Matcher, um Pattern, uma fonte de entrada, informações sobre se a fonte está fechada ou não, o tipo da última coisa correspondida, informações sobre se a última coisa foi uma correspondência válida ou não, a raiz usada para números, o local (informações sobre se você está usando .ou ,como separador de milhares) e seu próprio cache LRU para padrões usados recentemente , as informações sobre a última exceção encontrada, algumas informações sobre a análise de números, algumas informações sobre a análise de booleanos, bastante mais informações sobre a análise de números inteiros ... e acho que é isso.
Como você pode ver, é um bloco de texto bastante grande lá. Esse é o estado do scanner. Para transformar o Scanner em uma classe estática, esse estado precisaria ser armazenado em outro lugar. A maneira C de fazê-lo realmente não tem tanto estado com isso. Você tem um fscanf. O FILE mantém algum estado sobre a posição em que está (mas isso precisa ser passado para cada chamada de fscanf). Se houve um erro, é necessário processá-lo (e então você começa a escrever um código parecido com este ) - e isso não informa informações como "Eu estava esperando um número inteiro, mas encontrei uma string".
Quando se olha o scanner teoricamente estático - todo o estado é mantido fora da classe, não é encapsulado dentro da classe. Outros bits de código podem mexer com essas variáveis. Quando outro código pode mexer com o estado da classe, fica muito difícil argumentar sobre o que a classe fará em qualquer situação.
Talvez você possa escrever algo como ScannerState { Locale loc; ... }e ter um código que resulte em:
ScannerState state = new ScannerState(a whole lot of arguments);
int foo = Scanner.nextInt(state);
Porém, isso é muito mais complicado do que ter o estado encapsulado em um objeto Scanner em primeiro lugar (e não precisar passar no estado).
Por fim, o Scanner implementa a interface, o Iterator<String>que significa que é possível usá-lo em códigos como:
Scanner in = new Scanner(someFile);
whie(in.hasNext()) { ... }
Sem conseguir obter uma instância da classe Scanner, esse tipo de estrutura se torna mais complicado dentro de uma linguagem orientada a objetos.