关闭Scanner引发无限循环:Java Socket端口输入异常咨询
嘿,这个坑我当初刚学Java Socket的时候也踩过!咱们来把这个问题掰扯明白:
问题根源:你关闭的不是Reader,是全局的System.in!
你用来读取控制台输入的BufferedReader,底层是绑定到标准输入流System.in的(也就是你写的new InputStreamReader(System.in)这一层)。当你调用reader.close()时,这个方法会递归关闭它包裹的所有底层流——也就是说,你不仅关了BufferedReader,连System.in也被一起关闭了!
而System.in是一个全局唯一、只能打开一次的流:一旦被关闭,后续任何尝试从它读取数据的操作都会直接抛出IOException,而且这个流根本没法重新打开。这就是为什么第一次端口占用后你关闭Reader,第二次再尝试读取时就会触发异常,进而导致无限循环(因为异常没被正确处理,程序一直重复尝试读取但永远失败)。
解决方案:不要随意关闭关联System.in的Reader
正确的做法是:只初始化一次关联System.in的Reader,在整个程序生命周期内复用它,直到程序结束再考虑关闭(甚至可以不用手动关闭,JVM退出时会自动清理标准流资源)。
举个简单的正确示例:
public class ServerPortTest { public static void main(String[] args) { // 只初始化一次Reader,绑定System.in try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { while (true) { System.out.print("请输入服务器端口:"); String portInput = reader.readLine(); // 处理用户输入EOF的情况(比如Ctrl+D) if (portInput == null) { System.out.println("输入结束,程序退出"); break; } try { int port = Integer.parseInt(portInput); ServerSocket serverSocket = new ServerSocket(port); System.out.println("服务器在端口" + port + "启动成功!"); // 这里可以添加服务器逻辑 serverSocket.close(); break; } catch (NumberFormatException e) { System.out.println("输入无效,请输入数字格式的端口号!"); } catch (IOException e) { System.out.println("端口" + portInput + "已被占用,请重新输入!"); } } } catch (IOException e) { System.out.println("读取输入时发生异常:" + e.getMessage()); } } }
这里用了try-with-resources语法,会在程序结束时自动关闭Reader,既保证了资源清理,又不会在循环中提前关闭System.in。
补充说明:为什么会触发无限循环?
当你第一次关闭Reader导致System.in被关闭后,第二次调用reader.readLine()会直接抛出IOException。如果你的代码没有捕获这个异常(或者捕获后没有跳出循环、处理输入失败的情况),程序就会一直进入循环体,反复尝试读取失败的输入,最终陷入无限循环。
内容的提问来源于stack exchange,提问作者Pierre Anken

