Só tem um problema nessa lógica, se alterada a quantidade quebras de linhas, a sobra do índice "par", ficaria no meio da String. Nesse caso, a primeira iteração da sobra "par" fica no índice 2. A variável quebraLinhas sendo 4, seria um problema.
O seu código ficou muito mais show, kkkkk.
Mas fiz as alterações para que fique sempre no final as sobras "pares".
Vê se está correto o código. Ele rodou perfeitamente, mas se está correto no sentido de é a melhor forma.
public static void main(String[] args) {
String str = "TANTOFAZOTEXTO";
int quebraLinhas = 4;
int qtdChar = str.length() / quebraLinhas;
//int resto = str.length() % quebraLinhas;
String[] partes = new String[quebraLinhas];
//int contador = 0;
String sobra = "";
//Esse for roda até percorrer toda a String
//a var "i" incrementa de acordo com a quantidade de caracteres em cada quebra de linha
for (int i = 0; i < str.length(); i+=qtdChar) {
if (i + qtdChar <= str.length()) { //Esse if valida se o índice inicial até o final é menor que a quantidade de caracteres que ainda tem
String parte = str.substring(i, i + qtdChar); //Esse método copia o "pedaço" da String que queremos
int indice = i / qtdChar; //Essa divisão vai sempre dar o indice na sequencia que precisamos: 0, 1 ,2 ....
if ( indice % 2 == 0) { //Verifica se é par
//Instanciamos o objeto parte novamente mas na classe StringBuilder ao invés de String, o método reverse reverteu o pedaço da String
//e como ele não retorna nada, usamos toString para passar o valor.
partes[indice] = new StringBuilder(parte).reverse().toString();
} else {
partes[indice] = parte;
}
} else {
sobra = str.substring(i); //Passando apenas um índice, esse método pega do índice informado até o fim
//sem precisar iterar para conseguir todos ou usar uma lógica para passar o
//argumento correto para uma substring com os índices iniciais e finais
}
}
//ao invés de concatenar a String, usar o StringBuilder para construir aos poucos
//StringBuilder é mais performático
StringBuilder sb = new StringBuilder();
StringBuilder sbSobra = new StringBuilder();
for (int i = 0, j = 0; i < partes.length; i++) {
if (i % 2 == 0 && j < sobra.length()) { //Se o índice for par e ainda houver sobra suficiente
if (j % 2 == 0) { //Começando com 0 que é par, insere antes, e no próximo que é impar, insere depois e sucessivamente
sb.append(sobra.charAt(j));
sb.append(partes[i]);
} else {
sb.append(partes[i]);
sbSobra.append(sobra.charAt(j));
}
j++;
} else { //índice ímpar, acrescenta a sobra depois
sb.append(partes[i]);
}
}
sb.append(sbSobra);
System.out.println(sb.toString());
}
Esses comentários "óbvios" que coloquei é para eu perder menos tempo tentando ler o código quando quiser revisar alguma coisa. Visto que eu salvo todos os pacotes que crio pra poder consultar depois.