Java Clock类构造方法赋值时hour可传入超12的非法值问题
问题根因
你写的范围校验逻辑只存在于各字段的setXxx方法中,全参构造方法完全没有做任何合法性判断,是直接把外部传入的参数赋值给私有字段的,传入hour=15这类非法值时会直接跳过校验,生成带非法状态的对象。
你观察到的minute字段暂未出现越界问题纯粹是测试用例覆盖不全的巧合:一方面你没在测试构造方法时传入超出分钟范围的值,另一方面你写的setMinute校验本身就有逻辑错误——12小时制时钟分钟合法范围是0-59,你写的判断条件允许值为60的非法输入存入。
你之前自行修改代码后触发更多异常,通常是两个原因导致的:一是重复在构造方法里写校验逻辑时漏了边界判断,二是没有处理入参为null的场景,比如setPeriod里直接写period.equals("PM"),如果传入null值会直接触发空指针。
修复方案
- 全参构造方法不要直接给字段赋值,统一调用已经实现好的
setXxx方法完成赋值,从根源上避免校验逻辑重复写、两边规则不一致的问题。 - 修正
setMinute的判断上界,把<=60改成<=59;修正所有字符串入参的equals判断写法,把常量放在equals前面,避免入参为null时触发空指针,比如把period.equals("PM")改成"PM".equals(period)。 - 构造方法中需要处理
setXxx返回false的非法入参场景,要么直接抛出参数非法的异常明确提示调用方,要么回退到该字段的默认值,绝对不能让对象带着非法状态完成初始化。
修复后参考代码
public class Clock { private int hour; private int minute; private String period; private String timeZone; public Clock() { this.hour = 12; this.minute = 0; this.period = "AM"; this.timeZone = "Eastern"; } public Clock(int hour, int minute, String period, String timeZone) { // 先初始化默认值,避免某一步校验失败出现字段值混乱 this(); // 复用setter的校验逻辑赋值,非法入参直接抛出明确异常 if (!this.setHour(hour)) { throw new IllegalArgumentException("小时取值范围必须为1-12"); } if (!this.setMinute(minute)) { throw new IllegalArgumentException("分钟取值范围必须为0-59"); } if (!this.setPeriod(period)) { throw new IllegalArgumentException("时段仅支持传入AM/PM"); } if (!this.setTimeZone(timeZone)) { throw new IllegalArgumentException("时区仅支持Eastern/Central/Mountain/Pacific"); } } public boolean setHour(int hour) { if (hour >= 1 && hour <= 12) { this.hour = hour; return true; } else { return false; } } public int getHour() { return this.hour; } public boolean setMinute(int minute) { // 修正上界错误:分钟最大为59 if (minute >= 0 && minute <= 59) { this.minute = minute; return true; } else { return false; } } public int getMinute() { return this.minute; } public boolean setPeriod(String period) { // 修正空指针风险:常量在前调用equals if ("PM".equals(period) || "AM".equals(period)) { this.period = period; return true; } else { return false; } } public String getPeriod() { return this.period; } public boolean setTimeZone(String timeZone) { // 修正空指针风险:常量在前调用equalsIgnoreCase if ("Eastern".equalsIgnoreCase(timeZone) || "Central".equalsIgnoreCase(timeZone) || "Mountain".equalsIgnoreCase(timeZone) || "Pacific".equalsIgnoreCase(timeZone)) { this.timeZone = timeZone; return true; } else { return false; } } public String getTimeZone() { return this.timeZone; } }
内容的提问来源于stack exchange,提问作者user19124593
相关产品推荐
相关产品推荐

