如何检查扫描仪是否已关闭

6
我正在尝试编写两个方法,但遇到了一些问题。这两个方法都涉及使用Scanner读取文件,其中每一行都是逗号分隔的。这些方法是我FileIO类的一部分。除了该类之外,我还有另外两个类,Food和FoodArrayList。Food对象包含来自食品捐赠的多个信息(捐赠物品的人,捐赠原因等)。FoodArrayList类与ArrayList的功能基本相同,只不过我仅为存储Food对象而创建它。
我正在努力解决的两个方法如下:
1. 读取文件并简单地计算文件中行数。它返回行数。此方法用于确定FoodArrayList的大小。这是因为文件的每一行描述了一个Food对象。
2. 读取文件并将信息存储到FoodArrayList对象中。请记住,每个逗号分隔的行描述一个Food对象。因此,此方法通过文件,创建Food对象,并将它们插入到FoodArrayList中,直到到达文件末尾。
我所遇到的问题是在两个方法中都关闭了我的Scanner。我一直在思考如何编写一个if语句来检测Scanner是否已关闭。在这两个方法中,我在方法的开头都有以下内容:
    try{
        if(scan.hasNext() == false || scan == null)
            scan = new Scanner(new File(this.getFileName()));
    }
    catch(FileNotFoundException fnfe){
        System.err.println("The " + fileName + " could not be found.");
    }
    catch(IOException ioe){
        ioe.printStackTrace();
    }

这种方法似乎不起作用。如果我首先使用方法#1,然后稍后尝试使用方法#2(记住,在每个方法的末尾关闭Scanner),则if语句将被跳过,scan将永远不会被重新实例化,并将抛出IllegalStateException。
一个非常重要的观点是:我希望两种方法都可重复使用。如果方法#1一直运行到文件结束,然后我稍后使用方法#2,我希望Scanner对象再次从文件开头读取。而唯一的方法似乎是重新实例化Scanner对象。
当我第一次学习Java中的I/O时,我记得被告知始终确保在使用完它们后关闭Scanner对象。那几乎已经深入我的大脑了。无论如何,我该怎么办呢?我想到的一个想法是在我的两个方法的末尾将scan设置为null。那是否必要呢?我在StackOverflow的其他帖子上读到,这会让我不关闭Scanner对象,只需让垃圾收集器处理即可。
除此之外,我还想知道Scanner关闭后会发生什么以及为什么它无法重新打开。我知道这与流有关,但在Java环境下我不确定流是什么。我尝试查找Stream类, 但并没有从中获得太多的理解。
以下是两种方法的代码:
public int numberOfLines(){
    try{
        if(scan.hasNext() == false || scan == null)
            scan = new Scanner(new File(this.getFileName()));
    }
    catch(FileNotFoundException fnfe){
        System.err.println("The " + fileName + " could not be found.");
    }
    catch(IOException ioe){
        ioe.printStackTrace();
    }

    int lineCount = 0;
    while(scan.hasNextLine()){
        scan.nextLine();
        lineCount++;
    }
    scan.close();
    return lineCount;
}

方法 #2

public void readFile(FoodArrayList fal){
    try{
        if(scan.hasNext() == false || scan == null)
            scan = new Scanner(new File(this.getFileName()));
    }
    catch(FileNotFoundException fnfe){
        System.err.println("The " + fileName + " could not be found.");
    }
    catch(IOException ioe){
        ioe.printStackTrace();
    }

    while(scan.hasNextLine()){
        String stringRead = scan.nextLine();
        StringTokenizer tokens = new StringTokenizer(stringRead,",");
        Food temp = new Food();
        temp.setCorpName(tokens.nextToken());
        temp.getContact().setLast(tokens.nextToken());
        temp.getContact().setFirst(tokens.nextToken());
        temp.setDate(tokens.nextToken());
        temp.setProductCode(tokens.nextToken());
        temp.setDescription(tokens.nextToken());
        temp.setReason(tokens.nextToken());
        String numberString = tokens.nextToken();
        int number = Integer.parseInt(numberString);
        temp.setNumber(number);
        temp.setCoP(tokens.nextToken());
        fal.insert((Food)temp);
    }
    scan.close();
}

我希望将这两种方法分开。这是因为在我的客户端类中,我正在执行以下操作:

    FoodArrayList list;
    FileIO fio = new FileIO();
    int listSize = fio.numberOfLines();     
    list = new FoodArrayList(listSize);     
    fio.readFile(list);

我的FileIO类的默认构造函数已经考虑了我要读取的特定文件。当然,重载的构造函数允许使用不同的文件名作为输入。如有需要,请提供帮助。

1
如果(scan.hasNext() == false || scan == null),你应该交换条件,因为如果scan对象可能为空,则应首先检查它。此外,如果您正在将Food对象存储在数组中,则只需要第一个方法。但我猜您已经在使用某些集合,不需要行数。 - YoungHobbit
FoodArrayList列表是什么?当您调用其构造函数并将listsize作为参数传递时,您想要做什么? - Ankit Deshpande
FoodArrayList是一个类,其操作方式与ArrayList对象相同,但它仅设计用于存储Food对象。我知道我本可以使用ArrayList的实例,但我有我的理由创建这个特定的类。当我将listSize作为参数传递时,我基本上是创建了一个大小为listSize的Food对象数组。 - coolDude
我本可以手动计算文件中的行数,并在我的代码中输入list = new FoodArrayList(24)。然而,根据我对编程专业的理解,我们通常希望避免硬编码并使我们的代码具有灵活性。这就是为什么我创建了numberOfLines()方法的原因。 - coolDude
1个回答

2
我想让这两种方法都可重复使用。如果方法#1一直运行到文件结尾,而我后来使用方法#2,我希望Scanner对象能够再次从文件开头读取。似乎唯一的方法是重新实例化Scanner对象。
看起来你已经有了答案。无论一个Scanner对象可能处于什么状态,你都想重新实例化它,所以就...这样做吧。
在方法开始时,声明并实例化你的Scanner引用。你还可以使用try-with-resources,这样你的Scanner在结束运行时总是被关闭。
public void countLines() {
    try(Scanner scan = new Scanner(new File("file.txt"))) {
        // code
    } catch (IOException e) {
        e.printStackTrace();
    }
}

尽管实际上你所做的工作可以用相同的方法完成,但如果你坚持分别执行这些操作,则初始化两个不同的 Scanner 是最佳选择。


我明白你的意思。但是这两种方法都是FileIO类的一部分。在我的构造函数中,我已经实例化了Scanner对象。 - coolDude
也许我误解了你的意思......你是说不要甚至使用if语句,而重新实例化Scanner吗?这样一来,无论我如何为FileIO类定义我的构造函数,因为每次使用这些方法时都希望从文件开头开始,所以这并不重要。而重新实例化Scanner对象是从文件读取开始的唯一方式。 - coolDude
我希望这项工作可以用两种不同的方法完成。这是因为在我的客户类中,我有以下代码:FoodArrayList list; FileIO fio = new FileIO(); int listSize = fio.numberOfLines(); list = new FoodArrayList(listSize); fio.readFile(list);在评论中很难看出我的意图。如果您愿意,我可以将此代码添加到主要问题中。 - coolDude
你无法在评论中粘贴代码。即使你可以,它看起来也很糟糕。 - Makoto
刚刚将客户端代码添加到我的问题中。希望这能澄清我想要实现的内容(并且更易读)。 - coolDude

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接