Arrays
Por que devemos usar arrays?
Imagine um cenário onde você precise armazenar diversos dados de um só tipo. Por exemplo, diversos itens de compra em um supermercado. A primeira solução que pode vir à mente seria declarar diversas variáveis, uma para cada item. Mas e se existisse uma estrutura mais eficiente para agrupar esses itens, sem precisar declarar dezenas de variáveis isoladas? É exatamente para isso que servem os arrays: uma estrutura de dados linear que nos permite armazenar múltiplos elementos de um mesmo tipo em posições contíguas de memória, recuperando-os rapidamente através de um índice.
Declarando um array
Uma forma de declarar um array em java seria:
int[] arr = new int[7];
também existem algumas formas alternativas como:
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7};
int[] arr = {1, 2, 3, 4, 5, 6, 7};
Nesse caso, o compilador infere automaticamente o tamanho do array a partir da quantidade de elementos fornecidos. E é claro, é possível criar arrays de diversos tipos, sejam eles os tipo primitivos: char, boolean, byte, double, float ou tipos de referência como objetos personalizáveis. Depois de criado, um array possui tipo e tamanho imutáveis. É possível alterar o valor armazenado em cada posição, desde que o novo valor seja do mesmo tipo, mas não adicionar ou remover posições do array.
Acessando elementos de um array
Cada posição de um array é identificada por um índice, utilizamos os colchetes logo após o nome da variável, passando o número do índice desejado. Em Java, a contagem dos índices sempre começa a partir do 0.
int[] arr = {1, 2, 3, 4, 5, 6, 7};
int num = arr[0];
System.out.println(num); // retorna o valor 1 que se encontra no índice 0
Se tentarmos acessar um índice que não existe, o Java lançará uma exceção em tempo de execução chamado ArrayIndexOutOfBoundsException.
Por exemplo, se quisermos acessar o último elemento de um array, o pensamento intuitivo seria passar o tamanho total dele (neste caso, 7). Porém, isso provocará o erro mencionado, pois o índice 7 não existe em um array de 7 elementos.
Como a indexação é baseada em zero, o último índice válido será sempre o tamanho total menos 1. A forma mais segura de fazer isso dinamicamente é usando a propriedade .length:
int[] arr = {1, 2, 3, 4, 5, 6, 7};
int num = arr[arr.length - 1];
System.out.println(num); // retorna o valor 7 que se encontra no índice 6.
Como o Array é Armazenado na Memória?
O objeto array é alocado na Heap, e seus elementos são armazenados de forma contígua dentro desse bloco de memória, A variável declarada armazena apenas uma referência para esse objeto e, quando declarada dentro de um método, normalmente encontra-se na pilha de execução. Isso significa que todos os seus elementos são colocados em uma sequência linear de blocos vizinhos, sem espaços vazios entre eles.

Essa organização contígua é o principal diferencial de performance do array. Ao buscarmos um elemento através do seu índice, a JVM não precisa realizar uma varredura na memória inteira, retornando o elemento em tempo constante, apenas utilizando de uma fórmula matemática simples:
Onde o endereço base é a posição exata onde o array começa, o índice a posição que você deseja acessar e o tamanho do dado é a quantidade de bytes do dado que está sendo manipulado, no caso da imagem, um int que possui 4 bytes.
A propriedade .length
A propriedade length retorna o tamanho do array em questão, ela possui custo constante O(1), visto que trata-se de uma leitura direta dos metadados armazenados no cabeçalho do objeto (Object Header), sem necessidade de percorrer o array . Podemos utilizar a propriedade dessa forma:
int[] arr = {1, 2, 3, 4, 5, 6, 7};
System.out.println(arr.length); // Irá imprimir tamanho 7
Conclusão
Os arrays são a base fundamental do armazenamento sequencial no Java. Sua alocação contígua na memória heap garante uma performance imbatível para leitura de dados, operando em tempo constante O(1). No entanto, essa mesma característica traz sua maior limitação: a rigidez. O tamanho fixo nos obriga a saber de antemão quantos elementos vamos gerenciar. É justamente para resolver esse problema de inflexibilidade que o Java nos fornece estruturas mais avançadas, como o ArrayList.