0

0

Java中Scanner的规范使用:理解对象初始化与构造器的应用

碧海醫心

碧海醫心

发布时间:2025-07-10 21:22:13

|

722人浏览过

|

来源于php中文网

原创

java中scanner的规范使用:理解对象初始化与构造器的应用

本文旨在解决Java中Scanner对象在类成员变量或实例初始化块中不当使用导致的重复输入问题。通过深入分析Java对象初始化机制,阐述了将输入逻辑放置于构造器中的最佳实践,并强调了Scanner资源的有效管理与职责分离原则,以构建更健壮、可维护的代码。

问题剖析:为什么会出现重复输入?

在Java中,当我们将Scanner对象的创建和输入操作(如myObj.nextInt())直接放置在类的成员变量声明处或实例初始化块(即 {} 包裹的代码块)中时,每次创建该类的新实例时,这些代码都会被执行。这与许多初学者预期的行为(只执行一次)大相径庭。

考虑以下示例代码片段:

public class MyClass {
    // 成员变量声明时进行初始化,并执行I/O
    Scanner myObj = new Scanner(System.in);
    {System.out.print("Enter a value: ");} // 实例初始化块
    int value = myObj.nextInt(); // 每次创建对象都会执行此行

    public static void main(String[] args) {
        MyClass a = new MyClass(); // 第一次执行I/O
        MyClass b = new MyClass(); // 第二次执行I/O
        MyClass c = new MyClass(); // 第三次执行I/O
        // ... 后续操作
    }
}

当main方法中执行new MyClass()时,Java虚拟机(JVM)会执行以下步骤来初始化一个新对象:

  1. 为对象分配内存。
  2. 执行所有成员变量的初始化(包括Scanner myObj = new Scanner(System.in);)。
  3. 执行所有实例初始化块中的代码(包括{System.out.print("Enter a value: ");})。
  4. 执行构造器中的代码(如果存在且被调用)。

因此,如果main方法中创建了三个MyClass实例(如new MyClass();三次),那么上述的Scanner创建、提示信息打印和nextInt()操作将分别执行三次,导致用户需要输入三次值,这显然不是我们期望的单次输入行为。这种做法不仅会导致重复输入,还可能造成Scanner资源泄露(因为创建了多个Scanner但没有关闭)。

立即学习Java免费学习笔记(深入)”;

解决方案核心:构造器的力量

解决上述问题的关键在于理解并正确使用Java的构造器(Constructor)。构造器是一种特殊的方法,用于在创建对象时初始化对象的实例变量。它是设置对象初始状态的理想场所。

将输入逻辑放置在构造器中,可以确保:

  1. 受控的初始化: 只有当明确创建对象并调用其构造器时,输入逻辑才会被执行。
  2. 参数传递: 构造器可以接受参数,例如将一个已存在的Scanner实例传递给它,从而避免在每个对象内部都创建新的Scanner。
  3. 职责分离: 将对象的初始化逻辑(包括获取必要的初始数据)封装在构造器中,使得类的结构更清晰。

对于Scanner对象,最佳实践是:

万兴爱画
万兴爱画

万兴爱画AI绘画生成工具

下载
  • 在main方法中或程序的入口点只创建一个Scanner实例,因为它通常与System.in(标准输入流)关联,而System.in是全局唯一的。
  • 将这个Scanner实例作为参数传递给需要它进行输入操作的构造器或方法。
  • 在程序结束时,务必关闭Scanner资源,以释放系统资源。

优化后的代码实现

下面是一个根据上述原则优化后的Java代码示例,它演示了如何正确使用Scanner和构造器来避免重复输入,并进行单位转换计算:

import java.util.Scanner;

public class Test {
    // 1. 声明成员变量,但不在此处进行I/O操作或复杂初始化
    int caratValue;
    double gramsPerCarat;
    double caratsInGrams;
    double gramsInMilligrams;
    double milligramsInPounds;

    // 2. 定义构造器,用于初始化对象并执行输入、计算和(可选)输出
    // 构造器接受一个Scanner对象作为参数,避免重复创建Scanner
    public Test(Scanner scanner) {
        // 提示用户输入,并通过传入的Scanner获取值
        System.out.print("Enter Carat Value: ");
        this.caratValue = scanner.nextInt(); // 将输入的值赋给成员变量

        // 初始化常量和执行计算
        this.gramsPerCarat = 0.20;
        this.caratsInGrams = this.caratValue * this.gramsPerCarat;
        this.gramsInMilligrams = this.caratsInGrams * 1000;
        this.milligramsInPounds = this.gramsInMilligrams * 0.00220462 / 1000; // 注意:0.00220462 lbs/gram, 1000 mg/gram

        // 在构造器中直接输出结果,或提供getter方法在main方法中输出
        System.out.println("Carats in Grams: " + this.caratsInGrams);
        System.out.println("Grams in Milligrams: " + this.gramsInMilligrams);
        System.out.println("Milligrams in Pounds: " + this.milligramsInPounds);
    }

    // 如果需要从外部获取计算结果,可以提供getter方法
    public double getCaratsInGrams() {
        return caratsInGrams;
    }

    public double getGramsInMilligrams() {
        return gramsInMilligrams;
    }

    public double getMilligramsInPounds() {
        return milligramsInPounds;
    }

    // 3. 主方法:程序的入口点
    public static void main(String[] args) {
        // 在main方法中只创建一次Scanner对象
        Scanner mainScanner = new Scanner(System.in);

        // 创建Test类的实例,并将唯一的Scanner对象传递给其构造器
        // 此时,构造器中的输入和计算逻辑只会被执行一次
        Test conversionData = new Test(mainScanner);

        // 如果构造器中没有直接输出,可以在这里通过getter方法获取并输出
        // System.out.println("Carats in Grams (from getter): " + conversionData.getCaratsInGrams());
        // System.out.println("Grams in Milligrams (from getter): " + conversionData.getGramsInMilligrams());
        // System.out.println("Milligrams in Pounds (from getter): " + conversionData.getMilligramsInPounds());

        // 使用完毕后,关闭Scanner以释放系统资源
        mainScanner.close();
    }
}

关键实践与注意事项

  1. Scanner的生命周期管理:

    • 对于标准输入流System.in,通常在整个应用程序生命周期中只创建一个Scanner实例。
    • 在使用完毕后,务必调用scanner.close()方法关闭Scanner。这会关闭底层的输入流,防止资源泄露。在main方法中创建并关闭Scanner是一个常见的良好实践。
  2. 职责分离:

    • 将数据输入(I/O)与业务逻辑(计算)和数据输出(打印)进行适当分离。在上述示例中,构造器负责获取初始数据和进行计算,并直接输出了结果。如果业务逻辑更复杂,可以考虑将计算封装在单独的方法中,并通过getter方法提供结果。
    • 避免在类字段初始化或实例初始化块中执行有副作用的操作(如I/O操作、数据库连接、网络请求等),这些操作应该放在构造器或特定的方法中,以便更好地控制其执行时机。
  3. static关键字的考量:

    • 原问题更新后的代码使用了static Scanner myObj,这确实能确保Scanner只被创建一次,并且nextInt()也只执行一次。然而,这使得myObj成为一个类级别的共享资源,如果将来有多个线程或多个对象需要独立地从System.in读取,可能会引入并发问题或逻辑混乱。
    • 更推荐的做法是,将Scanner作为局部变量在main方法中创建,并通过参数传递给需要它的对象,这样可以更好地控制其作用域和生命周期。
  4. 代码可读性和维护性:

    • 使用构造器初始化对象,使代码意图更清晰:一个新对象在创建时需要哪些初始数据。
    • 避免“神奇的”重复行为,使程序行为更符合直觉。

通过遵循这些原则,您可以编写出更健壮、更易于理解和维护的Java应用程序。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
python中print函数的用法
python中print函数的用法

python中print函数的语法是“print(value1, value2, ..., sep=' ', end=' ', file=sys.stdout, flush=False)”。本专题为大家提供print相关的文章、下载、课程内容,供大家免费下载体验。

186

2023.09.27

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

525

2023.08.10

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

360

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2082

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

349

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

256

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

326

2023.10.09

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

412

2023.10.16

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

14

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号