Java构造函数读取指定字符串时终止执行的优雅实现方案
优雅解决Java构造函数中读取输入初始化final变量并处理quit终止的问题
问题背景
在Game类构造函数中,需要通过System.in读取用户输入来初始化多个final成员变量;当用户输入"quit"时需终止程序,但禁止使用System.exit()或Runtime.exit()。现有两种方案存在明显缺陷:
- 方案1:设置
gameFinished标志,在每一步输入后检查该标志,代码冗余且不够优雅 - 方案2:在输入方法中抛出异常并在构造函数捕获,但会导致
final变量无法保证初始化,违反Java语法规则
最优解决方案:静态工厂方法+自定义受检异常
核心思路是将输入验证逻辑从构造函数中剥离,放到静态工厂方法中处理;遇到用户输入"quit"时抛出自定义受检异常,由调用层捕获并终止程序。构造函数仅负责接收已验证的参数,确保final变量全部初始化。
步骤1:定义自定义受检异常
创建QuitException用于标识用户触发的quit操作:
public class QuitException extends Exception { public QuitException() { super("用户请求终止游戏"); } }
步骤2:重构Game类
将构造函数改为私有,通过静态工厂方法createGame()处理输入逻辑,遇到quit直接抛异常:
import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class Game { private final Scanner input; private final int playerCount; private final List<Player> players; private final int startGold; private final int winGold; private final int shuffleSeed; private int moveNumber; // 私有构造函数:仅接收已验证的参数,确保final变量全部初始化 private Game(int playerCount, List<Player> players, int startGold, int winGold, int shuffleSeed) { this.input = new Scanner(System.in); this.playerCount = playerCount; this.players = players; this.startGold = startGold; this.winGold = winGold; this.shuffleSeed = shuffleSeed; this.moveNumber = 0; printLogo(); } // 静态工厂方法:处理所有输入验证逻辑 public static Game createGame() throws QuitException { Scanner input = new Scanner(System.in); printLogoStatic(); int playerCount = getIntegerWithCondition(input, 1, "Question 1?"); List<Player> players = getPlayerNames(input, playerCount); int startGold = getIntegerWithCondition(input, 0, "Question 2?"); int winGold = getIntegerWithCondition(input, 1, "Question 3?"); int shuffleSeed = getIntegerWithCondition(input, 0, "Question 4?"); return new Game(playerCount, players, startGold, winGold, shuffleSeed); } private static int getIntegerWithCondition(Scanner input, int condition, String question) throws QuitException { System.out.println(question); boolean inputValid = false; int playerValue = 0; while (!inputValid) { String playerInput = input.nextLine(); if (playerInput.equals("quit")) { throw new QuitException(); } if (playerInput.matches("-*[0-9]+")) { try { playerValue = Integer.parseInt(playerInput); if (playerValue > condition) { inputValid = true; } else { System.out.printf("Error: 输入必须大于%d!\n", condition); } } catch (NumberFormatException e) { System.out.println("Error: 数字格式无效!"); } } else { System.out.println("Error: 输入不是有效数字!"); } } return playerValue; } private static List<Player> getPlayerNames(Scanner input, int playerCount) throws QuitException { List<Player> players = new ArrayList<>(); for (int i = 1; i <= playerCount; i++) { System.out.printf("请输入玩家%d的名字:\n", i); boolean inputValid = false; while (!inputValid) { String userInput = input.nextLine(); if (userInput.equals("quit")) { throw new QuitException(); } if (userInput.matches("[A-Za-z]+")) { players.add(new Player(userInput)); inputValid = true; } else { System.out.println("Error: 输入只能包含a-z的英文字母!"); } } } return players; } private void printLogo() { System.out.println("=== 游戏Logo ==="); } private static void printLogoStatic() { System.out.println("=== 游戏Logo ==="); } // Player内部类示例 public static class Player { private final String name; public Player(String name) { this.name = name; } } }
步骤3:在调用层处理异常
在main方法中捕获QuitException,直接返回即可终止程序:
public class Main { public static void main(String[] args) { try { Game game = Game.createGame(); System.out.println("游戏初始化成功!"); // 后续游戏逻辑 } catch (QuitException e) { System.out.println("用户终止游戏。"); // 无需调用System.exit,方法结束后程序自然终止 return; } } }
方案优势
- 合规性:构造函数仅接收已验证参数,所有
final变量都能被正确初始化,符合Java语法规范 - 简洁性:无需在每一步输入后检查标志位,遇到quit直接抛异常,流程清晰无冗余代码
- 职责分离:构造函数专注于对象状态初始化,输入验证逻辑由静态工厂方法处理,符合单一职责原则
- 优雅终止:调用层捕获异常后直接返回,程序自然结束,完全规避
System.exit()的使用
内容的提问来源于stack exchange,提问作者Nexon
相关产品推荐
相关产品推荐

