Untuk keluar sedikit daripada teori, langkah seterusnya dalam bootcamp ialah latihan (yang mereka panggil cabaran projek). Saya fikir ia akan menarik untuk memasukkan bahagian praktikal untuk menyatukan kandungan yang diliputi setakat ini.
Apa yang diminta ialah mencipta projek yang menyerupai satu akaun dalam bank, yang mengandungi nombor cawangan, nombor akaun, nama pelanggan dan imbangan. Data ini mesti datang dari terminal dan pada penghujungnya, mesej mesti dipaparkan yang menyatakan bahawa akaun telah berjaya dibuat dan menunjukkan data yang dimasukkan. Penerangan asal boleh dilihat di sini.
Nampaknya agak mudah. Tetapi satu perkara yang saya sangat suka lakukan dalam mana-mana tugas yang saya terima ialah memecahkannya kepada langkah-langkah yang sangat singkat, supaya saya mempunyai jalan yang jelas untuk diikuti. Saya melakukan ini juga kerana semasa saya melukis aliran dalam kepala saya, saya dapat memahami sama ada ia masuk akal dengan apa yang ditanya dan saya boleh menukar tindakan dengan cepat jika saya fikir ia tidak berkesan.
Oleh itu, langkah-langkah yang perlu diikuti adalah seperti berikut:
Nampaknya bukan sesuatu yang kompleks. Bermula dengan langkah 1, kami mempunyai yang berikut:
TerminalAccount.java
public class TerminalAccount { // eu sei, sou muito criativo private int branch; private String account; private String clientName; private double balance; public void createAccount(){ // aqui a mágica acontece } }
Walaupun pengubah akses belum diliputi secara mendalam setakat ini dalam kursus ini, saya memilih untuk membiarkan semua medan tertutup, membayangkan bahawa dalam sistem perbankan, cawangan, akaun, pemegang dan data baki mesti dilindungi dengan baik dan hanya kelas sendiri sepatutnya mempunyai akses kepada mereka.
Selain itu, kaedah createAccount tidak seharusnya mengembalikan apa-apa, cuma paparkan mesej dalam konsol, jadi pemulangannya tidak sah.
Kaedah ini adalah di mana beberapa perkara yang menjengkelkan saya tentang Java bermula. Daripada mempunyai kaedah generik yang menangkap sebarang jenis maklumat yang dimasukkan pengguna, kelas Pengimbas, yang digunakan untuk ini, mempunyai kaedah khusus untuk setiap jenis primitif. Dalam erti kata lain, terdapat Scanner.nextLine() untuk Strings, Scanner.nextInt() untuk nombor, Scanner.nextBoolean untuk boolean...
Sebagai perbandingan, dalam C# kami mempunyai kaedah input standard yang sentiasa mengembalikan String, yang kemudian jenisnya ditukar:
String input = Console.ReadLine(); int.TryParse(input, out number) Console.WriteLine(number) //completamente válido
Adakah saya perlu menulis lebih sedikit? Dia ada. Tetapi sekurang-kurangnya saya tidak perlu mengingati sintaks semua kaedah, hanya satu dan tukar kepada jenis yang saya mahu seterusnya. Lebih intuitif.
Tetapi mari kita teruskan, masih jauh lagi untuk menyelesaikan masalah ini. Langkah seterusnya ialah meminta pengguna memasukkan data pelanggan untuk pendaftaran.
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(); System.out.print("Por favor, insira o número da conta: "); this.account = sc.nextLine(); System.out.print("Por favor, insira o nome do cliente: "); this.clientName = sc.nextLine(); System.out.print("Por favor, insira o saldo inicial: "); this.balance = sc.nextDouble(); System.out.println("Olá " + this.clientName + ", obrigado por criar uma conta em nosso banco. sua agência é " + this.branch + ", conta " + this.account + " e seu saldo " + this.balance + " já está disponível para saque."); } }
Nah, nampaknya semuanya baik-baik saja. Mari kita nyatakan kelas ini dalam kaedah utama kita dan lihat apa yang berlaku.
Main.java
public class Main { public static void main(String[] args) { TerminalAccount account = new TerminalAccount(); account.createAccount(); } }
Lembu? Mengapakah input nombor akaun diabaikan dan algoritma sudah meminta nama pelanggan?
Membaca dokumentasi untuk kelas Pengimbas, dijelaskan bahawa ia memecahkan input kepada token menggunakan beberapa jenis aksara sebagai pembatas, untuk mengetahui di mana hendak berhenti. Dalam kes kaedah .next() dan .hasNext() (dan variannya, seperti .nextInt() dan .hasNextInt()), pembatas ialah ruang putih global (s+) seperti ruang, tab dan pemisah baris .
Apa yang dilakukan oleh kaedah ialah mengambil token yang didahului oleh pembatas, menukarnya kepada jenis yang ditentukan dan mengembalikan nilai tersebut. Selebihnya kekal dalam penimbal input.
Okay, setakat ni ok. Masalahnya ialah kaedah .nextLine(), yang digunakan untuk menangkap Strings menggunakan aksara pemisah baris (n), dan bukannya membuangnya. Kemudian, apabila digunakan mengikuti yang lain yang membuang, ia membaca apa yang tertinggal dan serta-merta menamatkan operasinya, beralih ke baris kod seterusnya.
Kedengarannya mengelirukan, dan memang begitu. Saya membuat rajah untuk membantu anda memahami kegilaan aliran ini:
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 um bug que 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!
Atas ialah kandungan terperinci Latihan - Mensimulasikan Akaun Bank Menggunakan Terminal. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!