Alinhar asteriscos em Java para desenhar uma caixa usando instruções "for"

Galera, estou tentando exibir uma caixa, ou melhor um retângulo em pé somente com asteriscos em Java e utilizando o laço “for”, porém o último asterisco da direita da primeira e última linha não estão alinhados com os asteriscos da coluna, alguém pode me ajudar? segue abaixo o meu código e a imagem da saída do programa.

public class Program {
    
    public static void main(String[] args) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (i == 0 || i == 8) {
                    System.out.print("*");
                } else {
                    if (j == 0 || j == 8) {
                        System.out.print("*");
                    }
                    System.out.print(" ");
                }
                //System.out.print("*");
            }
            System.out.println();
        }
    }
}

SAIDA_ASTERISCOS

Apenas trocando de 8 para 7 nessa condição já resolve:

 if (j == 0 || j == 8) {
  System.out.print("*");
}

Uma dica é usar variáveis com nomes objetivos para deixar o código mais fácil de entender:

public class Program {

    public static void main(String[] args) {
        // experimente alterar esses valores para mudar o tamanho do quadrado
        int width = 10;
        int height = 10;
        String valueToPrint = "*";

        for (int i = 0; i <= height; i++) {
            for (int j = 0; j <= width; j++) {
                if (i == 0 || i == height) {
                    System.out.print(valueToPrint);
                } else {
                    if (j == 0 || j == (width-1)) {
                        System.out.print(valueToPrint);
                    }
                    System.out.print(" ");
                }
            }
            System.out.println();
        }
    }
}

Repare que vc sempre imprime o espaço, ou seja, depois que imprimir o asterisco, vc coloca um espaço a mais.

Mas o correto é imprimir ou o asterisco, ou o espaço (apenas um deles), então deveria ter um else aí:

for (int i = 0; i < 9; i++) {
    for (int j = 0; j < 9; j++) {
        if (i == 0 || i == 8) {
            System.out.print("*");
        } else {
            if (j == 0 || j == 8) {
                System.out.print("*");
            } else { // <----- ***** AQUI *****
                System.out.print(" ");
            }
        }
    }
    System.out.println();
}

Agora sim, se for a primeira ou última coluna, imprime o asterisco. Caso contrário, imprime o espaço.


Só por curiosidade, a partir do Java 8, daria para usar join juntamente com nCopies para repetir a mesma string várias vezes:

int altura = 9, largura = 9;
String borda = "*", meio = " ";
for (int i = 0; i < altura; i++) {
    if (i == 0 || i == altura - 1) {
        System.out.println(String.join("", Collections.nCopies(largura, borda)));
    } else {
        System.out.println(borda + String.join("", Collections.nCopies(largura - 2, meio)) + borda);
    }
}

E a partir do Java 11 podemos usar o método repeat para obter o mesmo resultado. Ou seja, o código ficaria assim:

int altura = 9, largura = 9;
String borda = "*", meio = " ";
for (int i = 0; i < altura; i++) {
    if (i == 0 || i == altura - 1) {
        System.out.println(borda.repeat(largura));
    } else {
        System.out.println(borda + meio.repeat(largura - 2) + borda);
    }
}

Uma outra abordagem

Em vez de pensar em cada elemento individualmente, pense em termos de linhas: repare que em todas as linhas o primeiro e último caracteres são sempre o mesmo (o que chamei de borda). O que muda é apenas o “recheio” (na primeira e última linha é o mesmo caractere da borda, nas demais é o outro caractere, que chamei de meio).

Ou seja, cada linha é composta de um caractere da borda no início e outro no fim. E no meio vc preenche com um caractere que pode ser a borda (quando a linha é a primeira ou a última), ou o meio (nas outras linhas). Ficaria assim:

int altura = 9, largura = 9;
String borda = "*", meio = " ";
for (int i = 0; i < altura; i++) {
    // se é a primeira ou última linha, usa o caractere da borda
    // senão usa o outro caractere ("meio")
    String recheio = i == 0 || i == altura - 1 ? borda : meio;
    // uma borda no início e outra no fim, e no meio o "recheio"
    System.out.println(borda + recheio.repeat(largura - 2) + borda);
}

Mas claro, se a ideia for fazer os loops “na mão”, sem usar métodos prontos, daria para seguir a mesma ideia acima:

int altura = 9, largura = 9;
String borda = "*", meio = " ";
for (int i = 0; i < altura; i++) {
    String recheio = i == 0 || i == altura - 1 ? borda : meio;
    System.out.print(borda);
    for (int j = 0; j < largura - 2; j++) {
        System.out.print(recheio);
    }
    System.out.println(borda);
}

De qualquer forma amigo, muitíssimo obrigado, faltou essa parte da minha lógica mesmo. Voltei pro Java, estou muito enferrujado ainda, estou fazendo exercícios elementares para depois aprofundar mais em Collections, Polimorfismo, Herança etc… e depois ver as atualizações novas do Java. Muito obrigado!!!