为什么文件中的值没有存储在 class 属性中?

Why the values from the file are not being stored in the class attributes?

我正在尝试编写一个代码,该代码从一个文件中读取,该文件中列出了 apple 的产品。我还有两个 类 将文件中的数据存储在这些文件中的对象数组中。

但每当我尝试从这些对象的数组中获取值时,它始终为空。

我认为从文件中读取这些值时出现了问题,但我无法确定是哪一个。另外,目前没有调试器可供我使用。

这是我用来读取文件并将其值存储在 类 中的代码。

Scanner fScanner=null;
        FileWriter fw = null;


        try {
            fScanner = new Scanner(new File("apple.txt"));
        } 
        catch (FileNotFoundException e) {
            System.out.println(e.toString());
        }

        while(fScanner.hasNextLine()) {
            String line = fScanner.nextLine();
            Scanner lineScanner = new Scanner(line);

            if(lineScanner.next().equals("IPHONE")) 
                countIphone++;

            else if(lineScanner.next().equals("IPAD"))
                countIpad++;
        }

            Iphone[] iphone = new Iphone[countIphone];
            Ipad[] ipad = new Ipad[countIpad];

        while(fScanner.hasNextLine()) {
            String line = fScanner.nextLine();
            Scanner lineScanner = new Scanner(line);

            if(lineScanner.next().equals("IPHONE")) {
                for(int i=0;i<countIphone;i++) {
                    iphone[i].setModel("IPHONE"+lineScanner.useDelimiter(",").next());
                    iphone[i].setScreenSize(lineScanner.useDelimiter(",").next());
                    iphone[i].setProcessor(lineScanner.useDelimiter(",").next());
                    iphone[i].setSimType(lineScanner.useDelimiter(",").next());
                    iphone[i].setColor(lineScanner.useDelimiter(",").next());
                    iphone[i].setROM(lineScanner.useDelimiter(",").next());
                    iphone[i].setIs3DTouch(lineScanner.useDelimiter(",").next());
                    iphone[i].setPrice(lineScanner.useDelimiter(",").next());
                }
            }


            else if(lineScanner.next().equals("IPAD")) {
                for(int i=0;i<countIpad;i++) {
                    ipad[i].setModel("IPAD"+lineScanner.useDelimiter(",").next());
                    ipad[i].setScreenSize(lineScanner.useDelimiter(",").next());
                    ipad[i].setProcessor(lineScanner.useDelimiter(",").next());
                    ipad[i].setIsWifi(lineScanner.useDelimiter(",").next());
                    ipad[i].setColor(lineScanner.useDelimiter(",").next());
                    ipad[i].setMemory(lineScanner.useDelimiter(",").next());
                    ipad[i].setPrice(lineScanner.useDelimiter(",").next());
                }
            }

        }

文件如下所示:

IPHONE 7, 4.7, A10, GSM, JET BLACK, 32GB, TRUE, 700
IPAD AIR 2, 9.7, A8, TRUE, SILVER, 64GB, 400
IPHONE SE, 4, A9, CDMA, SILVER, 16GB, FALSE, 490
IPAD PRO, 9.7, A9, TRUE, SPACE GREY, 32GB, 650
IPHONE X, 7, A11, LTE, BLACK, 128GB, TRUE, 999
IPAD PRO X, 12, A12, TRUE, SPACE GREY, 256GB, 700

您的算法遍历文件两次:

  • 首先while(fScanner.hasNextLine())循环计算文件中的iphone和ipad
  • 第二个while(fScanner.hasNextLine())循环读取实际数据

问题是第一个循环结束后,所有的输入都被消耗掉了。

您可以通过关闭并重新打开扫描仪来解决此问题。但是,您的代码还有其他问题 - for 条件语句中的循环假定所有 iphone 和所有 ipad 都在文件中,但事实并非如此。

您可以通过结合扫描循环并为 iPhone 和 iPad 使用可调整大小的容器来解决此问题:

List<Iphone> iphones = new ArrayList<>();
List<Ipad> ipads = new ArrayList<>();

@dasblinkenlight 指出了 iphoneipad 为空的原因。但是还是有些地方不对:

  • 同时迭代 for(int i=0;i<countIphone;i++) { ... } iphone[i]null。您从未分配 iphone[i] = new Iphone();。因此,尝试调用 iphone[i].setModel(...) 之类的方法将抛出 NullPointerException。这同样适用于 ipad.
  • 的迭代
  • 不太明显的一点是调用 new Scanner(new File("apple.txt")) 您正在打开一个资源。它没有在代码中关闭。如果您仍想使用 Scanner,请使用 try-resource-statement

我想建议另一种设计并使用 nio-API and stream-API

首先我为已知设备定义了一个枚举:

  public enum Device {
    IPHONE, IPAD;

    public static Device byName(String name) {
      return Arrays.stream(values())
        .filter(d -> name.startsWith(d.toString()))
        .findAny()
        .orElseThrow(() -> new IllegalArgumentException(String.format("Unknown device name %s", name)));
    }
  }

方法Device.byNamereturns从apple.txt对应的Device.[=一行的第一个字40=]

我为每个设备定义了一个 Function 作为工厂。它从 apple.txt 和 returns 适当的实例中获取一条分割线(由 ", " 分割)。

  private static Function<String[], Iphone> toIphone() {
    return line -> {
      Iphone iphone = new Iphone();
      iphone.setModel(Device.IPHONE.toString() + line[0].split(" ")[1]);
      iphone.setScreenSize(line[1]);
      iphone.setProcessor(line[2]);
      iphone.setSimType(line[3]);
      iphone.setColor(line[4]);
      iphone.setROM(line[5]);
      iphone.setIs3DTouch(line[6]);
      iphone.setPrice(line[7]);
      return iphone;
    };
  }

  private static Function<String[], Ipad> toIpad() {
    return line -> {
      Ipad ipad = new Ipad();
      ipad.setModel(Device.IPAD.toString() + line[0].split(" ")[1]);
      ipad.setScreenSize(line[1]);
      ipad.setProcessor(line[2]);
      ipad.setIsWifi(line[3]);
      ipad.setColor(line[4]);
      ipad.setMemory(line[5]);
      ipad.setPrice(line[6]);
      return ipad;
    };
  }

为了决定调用哪个方法,我定义了一个方法,它采用代表来自 apple.txt 的设备的分割线。它调用 Device.byName 传入分割线的第一个元素。

  private static Device classify(String[] device) {
    return Device.byName(device[0]);
  }

现在我们可以使用上面的代码来进行这样的文件处理:

  public static void main(String[] args) throws IOException {
    Map<Device, List<String[]>> devices = Files
      .lines(new File("./apple.txt").toPath())
      .map(l -> l.split(", "))
      .collect(Collectors.groupingBy(Example::classify));

    Iphone[] iphones = devices.get(Device.IPHONE).stream()
      .map(toIphone())
      .toArray(Iphone[]::new);
    Ipad[] ipads = devices.get(Device.IPAD).stream()
      .map(toIpad())
      .toArray(Ipad[]::new);
  }

使用Files.lines我们不需要担心关闭读取File.
通过调用 Collectors.groupingBy(Example::classify),我们从 Stream 接收到一个 Map,它以 Device 的实例作为键。对于 Devices 它 returns 具有所有对应实例的 List