上周五我们学习了Java异常处理的一些基本知识,周末就做了关于用try..catch修改我们以前做的一个模拟ATM取款机的练习。
/**
* 显示登陆界面
*/
public void showLogin(){
Scanner sc = new Scanner(System.in);
System.out.print("请输入帐号:");
int accountId = 0;
try{
accountId = sc.nextInt();
}catch (InputMismatchException e){
System.out.println("请按正确要求输入!");
showLogin();
}
System.out.print("请输入密码:");
int pwd = 0;
try{
pwd = sc.nextInt();
}catch (InputMismatchException e){
System.out.println("请按正确要求输入!");
showLogin();
}
/**
* 判断密码是否正确
*/
if(account.vertify(accountId, pwd)){
/**
* 进入菜单界面
*/
showControl();
}
else{
/**
* 递归调用自身
*/
System.out.println("密码错误,请重新输入!");
showLogin();
}
}
其中就遇到了一个问题,我们做的atmView类中的一个方法是要求用户输入账号密码的,其中pwd和accountId 我们定义为int类型,为了防止用户乱输入导致程序异常,我们在输入那儿加了try块。可是问题就来了,我们这个方法中如果判断用户账号密码不对就会递归调用自身,可是当他运行时,除了第一次会要求用户输入外,他不会在调用输入方法,导致程序出现死循环,直至堆溢出。
开始时我考虑是不是因为加了try…catch的原因导致的,毕竟以前没修改时都运行正常。所以考虑是因为程序抛出异常后,又递归调用所以抛出的异常未清空导致二次不进入try进行尝试运行就直接进入异常处理的原因。所以我就按这方面的思路上网查资料,可是一无所得。
后来还是在J2EE群组交流群的一位成员提醒我,叫我把调试结果发给他。我在调试的时候才发现,自己开始的思路是完全错误的。因为调试的时候程序是进入了try块的。只是在要求用户输入的accountId = sc.nextInt();这儿直接跳过了,看来问题是出在Scanner方法这儿。
我们先来看看关于Scanner的信息:
java.util.Scanner[delimiters=/p{javaWhitespace}+][position=3][match valid=true][need input=false][source closed=false][skipped=false][group separator=/,][decimal separator=/.][positive prefix=][negative prefix=/Q-/E][positive suffix=][negative suffix=][NaN string=/Q�/E][infinity string=/Q∞/E]
我们知道当我们不按要求输入时,scanner会抛出一个InputMismatchException异常,其实我们比对scanner的信息不难发现,其中有一项match valid=true 就是这项标识是否输入匹配的。调试中我们发现这项一旦输入不匹配就会变为false,而第二次输入时就不会再要求输入了。
因为我们在程序中输入十分常见,我们一般把private Scanner sc = new Scanner(System.in);定义为私有的方便所有方法调用。这样一旦我们输入异常后,sc中的match valid=true就会变为false了,第二次递归调用就不会再要求用户输入。
所以解决的办法是在方法里定义一个Scanner sc = new Scanner(System.in);让方法每次被调用时都在初始化sc就能解决该问题了。
通过今天遇到的这个问题,向我们再次展示了Java编程中调试的重要性!
Ps: 这不是最佳解决办法,而且问题的描述有问题,请看下篇博客!