Tudo vem do hardware.
Um byte é a menor unidade de memória endereçável na maioria dos hardwares.
Todo tipo que você mencionou é criado a partir de vários bytes.
Um byte é de 8 bits. Com isso, você pode expressar 8 booleanos, mas não pode procurar apenas um de cada vez. Você endereça 1, está endereçando todos os 8.
E costumava ser tão simples, mas depois passamos de um barramento de 8 bits para um de 16, 32 e agora de 64 bits.
O que significa que, embora ainda possamos endereçar no nível de bytes, não podemos mais recuperar um único byte da memória sem obter os bytes vizinhos.
Diante desse hardware, os designers de idiomas escolheram para nos permitir escolher tipos que nos permitissem escolher tipos que se encaixassem no hardware.
Você pode afirmar que esse detalhe pode e deve ser abstraído, especialmente em um idioma que visa executar em qualquer hardware. Isso ocultaria problemas de desempenho, mas você pode estar certo. Simplesmente não aconteceu dessa maneira.
Java realmente tenta fazer isso. Os bytes são promovidos automaticamente para Ints. Um fato que o deixará maluco na primeira vez em que tentar fazer algum trabalho sério de mudança de bits.
Então, por que não funcionou bem?
O grande ponto de venda de Java na época em que você podia se sentar com um bom algoritmo C conhecido, digitá-lo em Java e, com pequenos ajustes, ele funcionaria. E C está muito próximo do hardware.
Manter esse tamanho ativo e abstrato fora dos tipos integrais simplesmente não funcionava juntos.
Então eles poderiam ter. Eles simplesmente não.
Talvez o programador não queira que alguém possa usar um número maior que um determinado tamanho e isso permita que ele o limite.
Este é um pensamento válido. Existem métodos para fazer isso. A função de grampo para um. Um idioma pode chegar ao ponto de estabelecer limites arbitrários em seus tipos. E quando esses limites são conhecidos em tempo de compilação, isso permitiria otimizações na maneira como esses números são armazenados.
Java simplesmente não é essa linguagem.