今天在写一个小demo的时候,用到了Scanner,然后我灵机一动。。想着把它放在try-resource-catch里面,因为正好是要用jdbc,所以想利用一下这个resource,于是我就把它放进去了,结果,发生了接下来的一幕。。。
我们来看一下代码
这里就是问题的根源
package jdbc_day1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class TestLogin {
static {
try {
Class.forName("oracle.jdbc.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static String login() {
try (Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "hr", "hr");
PreparedStatement ps = conn.prepareStatement("select * from tbl_user where name = ? and pwd = ?");
Scanner s = new Scanner(System.in);
) {
boolean bn;
//Scanner s = new Scanner(System.in);
do {
bn = false;
System.out.print("请输入帐号");
String name = s.next();
System.out.print("请输入密码");
String pwd = s.next();
ps.setString(1, name);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
System.out.println("登录成功!");
return name;
} else {
System.out.println("登录失败!");
System.out.println("重新输入-y 退出-n");
char c = s.next().charAt(0);
bn = (c == 'y' || c == 'Y') ? true : false;
}
rs.close();
} while (bn);
//s.close();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
测试类
package jdbc_day1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;
public class TestUpdate {
static {
try {
Class.forName("oracle.jdbc.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "hr", "hr");
PreparedStatement ps = conn.prepareStatement("update tbl_user set pwd = ? where name = ?");) {
String name = TestLogin.login();
Scanner s = new Scanner(System.in);
if (name != null) {
System.out.print("请输入新密码:");
String newPwd = s.next();
ps.setString(1, newPwd);
ps.setString(2, name);
ps.executeUpdate();
System.out.println("修改成功!");
}
s.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
美滋滋的写完了,想着,我真是太机智了,又省了六行代码~~ 舒服
当我运行的时候

报错了。。。 我找一下错误, 在25行,也就是下面这里

然后我就懵了,开始怀疑,难道是我昨晚喝多了? s.next()不是这么写的吗。。。然后我换成了s.nextLine();
结果还是如此. 此处省略很多问号。。。 接着,我打开了源码,也就是报错的位置,看下图

作为三级一遍过的男人,这点英文太简单了
(如果我们在输入的最后,那么NoSuchElement; 如果仍然有输入,那么input失配)
假装我没有用有道词典。但是,这翻译过来也看不懂啥玩意啊…
接着,费劲周折,我把Scanner放到外面,自己手动关闭,结果成功了
public static String login() {
try (Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "hr", "hr");
PreparedStatement ps = conn.prepareStatement("select * from tbl_user where name = ? and pwd = ?");) {
boolean bn;
Scanner s = new Scanner(System.in);
do {
bn = false;
System.out.print("请输入帐号");
String name = s.next();
System.out.print("请输入密码");
String pwd = s.next();
ps.setString(1, name);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
System.out.println("登录成功!");
return name;
} else {
System.out.println("登录失败!");
System.out.println("重新输入-y 退出-n");
char c = s.next().charAt(0);
bn = (c == 'y' || c == 'Y') ? true : false;
}
rs.close();
} while (bn);
s.close();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}

然后咱们来分析一下原因,这到底是为什么呢?
首先,咱们看一下System.in底层是什么样的

从这里可以看出System.in底层是封装的一个静态输入流
如果我们将Scanner放入try-resource-catch里面的话,会自动将所有资源关闭,当然包括这个输入流
所有当我们在测试类中调用完login方法之后,这个输入流in已经在login中关闭了,由于是静态的,所以我们在测试类中的in也是关闭的,这个时候,我们用一个关闭的流去接收键盘输入的值,就会报java.util.NoSuchElementException这个错误,但是如果我们手动关闭的话,当登陆成功后,就直接带着返回值返回到调用处了,而s.close();这个语句并没有执行,所以这个流还是开启的.因此就可以正常使用咯~~
从这里可以得到一个小小的经验:在我们使用键盘输入的时候,尽量手动关闭资源,不然稍不注意,可能就会犯我这个错误咯~~
本文探讨了在Java中将Scanner置于try-resource-catch块内导致的问题,详细解析了System.in流被提前关闭的原因及解决方案。
211

被折叠的 条评论
为什么被折叠?



