
本文详解如何在java中正确使用scanner获取用户输入,并通过setter方法或构造函数为对象赋值,重点解决“在main外获取输入并传入对象”的常见误区,提供可运行的完整示例与关键注意事项。
本文详解如何在java中正确使用scanner获取用户输入,并通过setter方法或构造函数为对象赋值,重点解决“在main外获取输入并传入对象”的常见误区,提供可运行的完整示例与关键注意事项。
在面向对象编程实践中,常需将用户交互逻辑(如键盘输入)与业务对象解耦——即不在main()中直接处理输入,而是封装到独立方法中,再将结果注入对象。但初学者易陷入两个典型误区:一是误以为setXxx()方法可直接接收输入表达式(如ls.setName(input.nextLine())语法虽合法,但若未妥善管理Scanner状态则易出错);二是混淆参数传递与引用修改机制,导致getData(String, int, ...)这类按值传递的方法无法真正更新调用方变量。
✅ 正确做法:返回对象实例,而非尝试修改原始参数
Java中基本类型和引用类型均按值传递。因此,以下写法无效:
// ❌ 错误:参数tenant是副本,修改它不会影响main中的变量
public static void getData(String tenant, int apt, ...) {
tenant = input.nextLine(); // 仅修改局部副本
}正确策略是:让getData()方法创建并返回一个已初始化的Lease对象,由main()接收并使用。
✅ 推荐实现:构造函数初始化 + 静态工厂方法
优化后的Lease类应提供带参构造器,确保对象创建即处于有效状态:
立即学习“Java免费学习笔记(深入)”;
class Lease {
private String name;
private int aptNumber;
private double rent;
private int term;
private static final int FEE = 10;
// ✅ 提供全参构造器(推荐)
public Lease(String name, int aptNumber, double rent, int term) {
this.name = name;
this.aptNumber = aptNumber;
this.rent = rent;
this.term = term;
}
// ✅ 保留默认构造器(兼容性)
public Lease() {
this("XXX", 0, 1000.0, 12);
}
// getter/setter 方法(保持不变,略)
public void setName(String tenant) { name = tenant; }
public void setAptNumber(int apt) { aptNumber = apt; }
public void setRent(double monthRent) { rent = monthRent; }
public void setTerm(int t) { term = t; }
public String getName() { return name; }
public int getAptNumber() { return aptNumber; }
public double getRent() { return rent; }
public int getTerm() { return term; }
public void addPetFee() {
rent += FEE;
explainPetPolicy();
}
public static void explainPetPolicy() {
System.out.println("A pet fee of $" + FEE + " is added to the monthly rent.");
}
}对应测试类TestLease需重构为清晰的三层职责:
- 输入采集:getData() 创建Scanner,读取用户输入,直接构造并返回Lease对象;
- 对象展示:showValues(Lease) 负责格式化输出;
- 主流程编排:main() 仅负责调用、传递与协调。
import java.util.Scanner;
public class TestLease {
public static void main(String[] args) {
// ✅ 步骤1:调用getData获取已初始化的对象
Lease lease = getData();
// ✅ 步骤2:展示结果(可重复调用,体现对象复用性)
showValues(lease);
// ✅ 可选:演示setter动态修改
lease.addPetFee();
System.out.println("\nAfter pet fee:");
showValues(lease);
}
public static void showValues(Lease ls) {
System.out.println("\n\nYour lease results:" +
"\nName : " + ls.getName() +
"\nApartment : " + ls.getAptNumber() +
"\nRent : $" + String.format("%.2f", ls.getRent()) +
"\nTerm : " + ls.getTerm() + " months");
}
public static Lease getData() {
Scanner input = new Scanner(System.in);
System.out.print("Enter lessee name >> ");
String name = input.nextLine().trim(); // ✅ 建议trim()防空格
System.out.print("Enter apartment number >> ");
int aptNumber = input.nextInt();
System.out.print("Enter rent >> ");
double rent = input.nextDouble();
System.out.print("Enter lease term in months >> ");
int term = input.nextInt();
// ✅ 关键:立即构造对象并返回,避免状态分散
Lease lease = new Lease(name, aptNumber, rent, term);
// ✅ 清理Scanner缓冲区(防止nextLine()残留换行符)
input.nextLine(); // 消费掉nextInt()后遗留的换行符
return lease;
}
}⚠️ 关键注意事项
- Scanner资源管理:本例中Scanner在方法内创建/使用/隐式丢弃,适用于简单场景;生产环境建议在try-with-resources中声明,或作为类成员统一管理。
- 输入验证缺失:实际项目中需添加hasNextXxx()检查及异常捕获(如InputMismatchException),避免程序崩溃。
- nextInt()/nextDouble()后换行符问题:它们不消费行尾\n,后续nextLine()会直接返回空字符串。务必在数字输入后调用一次input.nextLine()清理缓冲区(如上例所示)。
- 对象不可变性考量:若业务要求租约创建后不可修改,应移除所有setter,仅保留构造器注入——这是更安全的设计模式。
✅ 总结
将用户输入逻辑提取到独立方法中,核心在于返回完整对象而非试图修改参数。结合带参构造器与清晰的职责分离,代码既符合面向对象原则,又具备良好的可读性与可维护性。记住:setData(...)不是必须的;createAndReturnData()才是更自然、更健壮的Java实践。










