為了脫離理論,訓練營的下一步是練習(他們稱之為計畫挑戰)。我認為包含一個實用部分來鞏固到目前為止所涵蓋的內容會很有趣。
要求的是創建一個模擬銀行單一帳戶的項目,包含分行號碼、帳號、客戶姓名和餘額。這些資料必須來自終端,最後必須顯示一則訊息,說明帳戶已成功建立並顯示輸入的資料。原始描述可以在這裡看到。
嗯,看起來很簡單。但在我收到的任何任務中,我真正喜歡做的一件事是將其分解為非常短的步驟,以便我有一個清晰的路徑可以遵循。我這樣做也是因為當我在腦海中繪製流程時,我可以理解它是否符合所要求的內容,如果我認為它不起作用,我可以快速改變行動方針。
因此,要遵循的步驟如下:
看起來並不複雜。從第 1 步開始,我們有以下內容:
TerminalAccount.java
儘管到目前為止,課程中還沒有深入討論訪問修飾符,但我選擇將所有字段保留為私有,想像在銀行系統中,分行、帳戶、持有人和餘額數據必須得到很好的保護,只有類別本身必須得到很好的保護可以存取它們。
此外,createAccount 方法不應該傳回任何內容,而只是在控制台中顯示一則訊息,這就是它傳回 void 的原因。
Java 中一些令我惱火的事情就是從這個方法開始的。用於此目的的 Scanner 類別沒有捕獲使用者輸入的任何類型資訊的通用方法,而是為每種原始類型提供特定方法。換句話說,對於字串有 Scanner.nextLine(),對於數字有 Scanner.nextInt(),對於布林值有 Scanner.nextBoolean...
相較之下,在 C# 中,我們有一個標準輸入法,它總是回傳一個 String,然後它的類型改變:
還需要寫一點嗎?他有。但至少我不需要記住所有方法的語法,只需記住一個並轉換為我接下來想要的類型。更直觀。
但讓我們繼續吧,要解決這個問題還有很長的路要走。那麼下一步就是要求用戶輸入客戶資料進行註冊。
TerminalAccount.java
嗯,顯然一切都很好。讓我們在 main 方法中實例化這個類,看看會發生什麼。
Main.java
牛?為什麼帳號輸入被忽略並且演算法已經詢問了客戶的姓名?
閱讀 Scanner 類別的文檔,它解釋說它使用某種類型的字元作為分隔符號將輸入分解為標記,以知道在哪裡停止。對於 .next() 和 .hasNext() 方法(及其變體,例如 .nextInt() 和 .hasNextInt()),分隔符號是全域空格 (s+),例如空格、製表符和換行符。
該方法的作用是取得分隔符號前面的標記,將其轉換為指定的類型並傳回該值。其餘部分保留在輸入緩衝區中。
好的,到目前為止還好。問題在於,用於捕獲字串的 .nextLine() 方法會消耗換行符 (n),而不是丟棄它。然後,當在另一個丟棄的後面使用時,它會讀取留下的內容並立即結束其操作,繼續執行下一行程式碼。
Tá, e como consertamos essa lambança? Simples: adicionando um novo Scanner.nextLine() logo depois do .nextInt() para "limpar" o que sobrou. É bonito? Não, mas resolve.
Dá pra mudar o delimitador para aceitar a quebra de linha (\n ) em vez de um whitespace comum (\s+), mas isso poderia quebrar a forma com que as informações são quebradas em tokens, então é melhor deixar pra lá.
TerminalAccount.java
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); System.out.print("Por favor, insira o número da agência: "); this.branch = sc.nextInt(); sc.nextLine(); //... } }
Mais feio que bater na mãe.
Beleza, funcionou. Poderíamos dizer que o exercício está completo, mas vamos por um segundo imaginar que o usuário,sem querer, digitou uma letra na hora de colocar o número da agência:
Eita lasqueira.
Isso acontece porque, como já sabemos, a tipagem do Java é estática e o Scanner está esperando um número mas quando colocamos uma letra junto com um número, essa cadeia se torna uma String. O input que estava esperando um int recebeu uma String e ficou confuso tal qual uma criança que recebe meias de presente de natal.
Para remediar essa situação, podemos criar um loop simples, que informe para o usuário que a informação que ele inseriu está incorreta de acordo com as especificações do sistema e pedir que ele insira os dados novamente (de maneira correta, dessa vez). Como não sabemos quantas tentativas o usuário vai levar para inserir os dados corretamente, um while parece adequado.
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); boolean isBranchNumberInputCorrect = false; do { try { System.out.print("Por favor, insira o número da agência: "); this.branch = sc.nextInt(); sc.nextLine(); isBranchNumberInputCorrect = true; } catch (InputMismatchException e) { System.out.println("Por favor, insira apenas números inteiros para o número da agência."); sc.nextLine(); } } while (!isBranchNumberInputCorrect); //... } }
Aqui criamos uma variável de controle chamada IsBranchNumberInputCorrect (porque, novamente, sou muito criativo quando se trata de nomes), inicializada em false. Em seguida, começamos o bloco do, uma vez que queremos que o código faça uma ação antes de verificar se o dado inserido é valido ou não e jogamos nosso input lá pra dentro.
Caso dê tudo certo, o dado inserido será armazenado no campo branch, qualquer caractere sobrando será consumido pelo Scanner.nextLine() e a nossa variável de controle será atualizada para true. Aí a condição do while vai checar se isBranchNumberInputCorrect é false. Se for, reinicia o loop no caso de sucesso, o laço é encerrado.
Agora, caso o usuário insira algo não esperado (como uma String), o método Scanner.nextInt() vai emitir um evento de erro InputMismatchException, que será capturado pelo nosso bloco catch. Uma vez lá dentro, o código vai exibir uma mensagem de erro alertando que o tipo de dado está errado e consumido qualquer caractere que tenha ficado pra trás.
A gente pode fazer a mesma coisa com o input de saldo, para garantir que o valor inserido sempre será numérico e não permitir que a aplicação quebre caso seja inserido algo como 12,56f:
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); //... boolean isBalanceInputCorrect = false; do { try { System.out.print("Por favor, insira o saldo inicial: "); this.balance = sc.nextDouble(); sc.nextLine(); isBalanceInputCorrect = true; } catch (InputMismatchException e) { System.out.println("Por favor, insira apenas valores decimais."); sc.nextLine(); } } while (!isBalanceInputCorrect); //... } }
Poderíamos parar por aqui e dar esse exercício como encerrado, mas ainda tem umbugque requer um pouco de atenção. O que aconteceria se, em vez de delimitarmos nosso saldo com uma vírgula (,) usássemos um ponto (por exemplo, 10.56)?
Isso acontece devido ao locale, a adaptação do input à cultura do local. Aqui no Brasil, o decimal é delimitado pela vírgula, então o método não entende que essa separação com ponto é válida.
A documentação da classe Scanner nos mostra que é possível alterar a cultura para uma que atenda ou um ou outro padrão, mas não os dois ao mesmo tempo. Também é possível alterar especificamente o delimitador, para um símbolo ou para outro, mas não os dois ao mesmo tempo.
Um dos métodos para solucionar esse problema não é muito elegante, mas resolve: em vez de capturar o dado diretamente como double, vamos usar o método Scanner.nextLine() para pegar o input como uma String, trocar os pontos por vírgula e tentar trocar o tipo para double.
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); //... boolean isBalanceInputCorrect = false; do { try { System.out.print("Por favor, insira o saldo inicial: "); String balanceString = sc.nextLine().replace(",", "."); this.balance = Double.parseDouble(balanceString); isBalanceInputCorrect = true; } catch (NumberFormatException e) { System.out.println("Por favor, insira apenas valores decimais."); } } while (!isBalanceInputCorrect); //... } }
Além da alteração do método de captura do dado, tivemos mais algumas modificações: retiramos as chamadas para o método Scanner.nextLine() que serviam apenas para consumir os caracteres remanescentes, porque nosso input já faz isso pra gente. Além disso, o tipo do erro mudou: agora não se trata de um erro de incompatibilidade de tipo (InputMismatchException), mas sim um de erro no formato do número (NumberFormatException).
Com essas alterações feitas, bora ver se tudo deu certo:
Deu tudo certo! Com isso, conseguimos dizer que o exercício está concluído (finalmente)!
O repositório desse exercício está disponível aqui caso tenha interesse de ver.
E é isso. Até o próximo módulo!
以上是練習 - 使用終端模擬銀行帳戶的詳細內容。更多資訊請關注PHP中文網其他相關文章!