项目场景:
编写氪金游戏,充值抽卡系统的代码,将Scanner类中的创建,和关闭Scanner对象,同时写进了for循环内,导致主线程异常。
问题描述
半成品的充值抽卡代码:
import java.util.Scanner;
public class ChouKa {
private static int yuanShi=0;
public static void main(String[] args) {
for(int j=0;j<10;j++) {
Scanner sc=new Scanner(System.in);
//导致BUG的代码
System.out.println("您是否要充值?(true or false)");
boolean chongZhi=sc.nextBoolean();
if(chongZhi==true){
System.out.println("请输入您的充值金额(元)");
int money=sc.nextInt();
int yuanShiC=money*10;
System.out.println("邮箱提醒:到账原石"+yuanShiC);
yuanShi+=yuanShiC;
}
System.out.println("原石:"+yuanShi);
sc.close();
//导致BUG的代码
}
}
}
报错如下:
编写过程期间,循环充值抽卡代码。第一次输入充值金额,正常运行代码;到第二次运行时,用户不能输入数据,无法运行代码,报错显示,主线程异常。
原因分析:
在循环中创建和关闭Scanner对象,可能会导致主线程异常
第一,Scanner是一个阻塞式IO操作,Scanner对象在读取用户输入时,会导致主线程阻塞,程序停止继续执行,流正在等待来自用户的输入,例如从键盘读取输入。在这种情况下,流将阻塞
第二,用户输入完毕内容,关闭Scanner对象,它也关闭了底层输入源,例如文件或流。如果在循环的下一次迭代中创建了一个新的Scanner对象,但是输入源已经关闭,那么在对新的Scanner对象调用next()时可能会导致抛出异常
往深了讲,原因是源码中的nextBoolean()方法只读取布尔值,而不使用留在输入缓冲区中的换行字符(\n)。当程序执行到循环的下一个迭代并创建一个新的Scanner对象时,nextInt()方法将读取前一个迭代中留在输入缓冲区中的换行符,而不是等待来自用户的新输入。这将导致程序跳过用户输入的下一个提示,直接进入下一个迭代,从而用户无法输入,代码无法继续运行,主线程发生异常。
弊端:在循环中反复创建和关闭Scanner对象,相比在循环过程之外进行此操作,计算机操作更复杂,运行内存和时间占用的更多,同时系统反应的会更慢
因此,在循环中应该避免创建和关闭Scanner对象。最佳实践是在循环外创建Scanner对象,然后在循环中重复使用该对象,以避免阻塞主线程
解决方案:
Scanner sc=new Scanner(System.in);和sc.close(); 写在for循环语句之外
此外,for循环中的创建对象,在for循环外调用,是无法解析的
修改后代码如下:
public class ChouKa {
private static int yuanShi=0;
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
//Scanner对象只需创建一次,多次创建,会导致相同名称,但实际是不同对象的问题
for(int j=0;j<10;j++) {
System.out.println("您是否要充值?(true or false)");
boolean chongZhi=sc.nextBoolean();
if(chongZhi==true){
System.out.println("请输入您的充值金额(元)");
int money=sc.nextInt();
int yuanShiC=money*10;
System.out.println("邮箱提醒:到账原石"+yuanShiC);
yuanShi+=yuanShiC;
}
System.out.println("原石:"+yuanShi);
}
sc.close();
//使用完用户输入的数据后,调用此方法,释放内存
}
}
问题解决: