5

更新:感谢有关保留代码 SSCCE 的建议。就像我说的,这是我第一次在这里发帖。下次我一定会花时间确保代码被充分精简,然后再在这里发布。


所以我正在为我的计算机科学课编写一个程序,并且遇到了一个我无法弄清楚的奇怪问题。我试图通过使用 try/catch 语句来阻止无效的数据类型输入,从而使我的程序变得健壮,以免它破坏并结束整个程序,但它似乎不起作用。当输入了无效的数据类型时,程序结束时不会在窗口中显示任何 Java 的标准错误消息。我不知道为什么会这样,但我认为我错误地使用了 try/catch 语句。这是我的程序的两个类:

/* The core class for the finance calculations to be done in the FinanceApp class. */

public class FinanceCore{
  // Instance variables
  int numYears;
  double principal;
  double interestRate;
  double balance;

  public FinanceCore(){
    numYears = 0;
    principal = 0;
    interestRate = 0;
    balance = 0;
  }

  // Mutator methods that return boolean values depending on whether the input was valid or not
  public boolean setYears(int y){
    if(y >= 0){
      numYears = y;
      return true;
    }
    else return false;
  }

  public boolean setPrincipal(double p){
    if(p >= 0){
      principal = p;
      balance = principal;
      return true;
    }
    else return false;
  }

  public boolean setInterestRate(double ir){
    if(ir >= 0 && ir <= 1){
      interestRate = ir;
      return true;
    }
    else return false;
  }

  // Two accessors
  public int getYears(){
    return numYears;
  }

  public double getPrincipal(){
    return principal;
  }

  // This method calculates and returns the balance at the end of each year
  public double plusYear(){
    balance = balance*(1+interestRate);
    return balance;
  }
}

/* This program recieves three pieces of data (interest rate, principal amount, number of years) and generates an output
* table that shows how much money will be in a fund with the given parameters at the end of every year. */

import java.util.Scanner;

public class FinanceApp{

  public static void main(String[]args){
    // First, we will declare our global variables, and set them to default values
    Scanner reader = new Scanner(System.in);
    FinanceCore account = new FinanceCore();
    int menuItem = 0;

    // Now, we'll greet the user (because we're friendly like that)
    System.out.println("Welcome! Please select a menu option below.");

    while(true){
      /* Now, our first user interface: a menu system that displays four options to the user arranged in
       * columns for aesthetic effect. This is accomplished using the printf method.
       */

      System.out.printf("%n%-20s%-20s%n%-20s%-20s%n%-20s%n",
        "Set Principal[1]","Set Interest Rate[2]","Set Timespan[3]","Calculate[4]","Quit[5]");

      System.out.print(": ");

      // Now we get the user input until it is valid, and catch and errors in input type
      try {
        menuItem = reader.nextInt(); 
      }
      catch(Exception e){
        reader.nextLine(); // Clear the input stream to avoid an infinite loop
        System.out.println("Please a valid number 1-5.");
      }

      // The code for setting the principal amount
      if(menuItem == 1){
      while(true){
        System.out.print("Please enter the principal investment amount: ");

        try{
          if(account.setPrincipal(reader.nextDouble()));
          break;
        }
        catch(Exception e){
          reader.nextLine(); // Clear the input stream to avoid an infinite loop
          System.out.println("Please enter a valid dollar amount.");
        }
      }
    }
    // The code for setting the interest rate
    else if(menuItem == 2){
      while(true){
        System.out.print("Please enter the quarterly interest rate: ");
        try{
          if(account.setInterestRate(reader.nextDouble()));
          break;
         }
         catch(Exception e){
           reader.nextLine(); // Clear the input stream to avoid an infinite loop
           System.out.println("Please enter a valid decimal number between 0 and 1 (inclusive).");
         }
       }
     }

     // The code for setting the number of years
     else if(menuItem == 3){
       while(true){
         System.out.print("Please enter the number of years the account will exist: ");
         try{
           if(account.setYears(reader.nextInt()));
           break;
         }
         catch(Exception e){
           reader.nextLine(); // Clear the input stream to avoid an infinite loop
           System.out.println("Please enter a valid integer value.");
         }
       }
     }

     // This part actually executes the calculation
     else if(menuItem == 4){
       System.out.printf("%-10s%-10s%n%-10d%-10.2f%n","YEAR","BALANCE",0,account.getPrincipal());
       int count = 1;
       for(int c = account.getYears(); c > 0; c--){
         System.out.printf("%-10d%-10.2f%n",count,account.plusYear());
         count++;
       }
     }

     // If the user enters any other number, the program quits
     else
       break;
     }
   }
 }

请注意,该程序存在一个我似乎无法修复的持久性问题。出于某种原因,每次用户在菜单选择提示符处输入无效的数据类型时,程序就会结束(尽管没有抛出任何错误)。

4

4 回答 4

3

当您使用 try-catch 块时,您实际上是在告诉编译器将负责显示错误消息——不会显示任何内置消息。相反,您在 catch 语句中包含的任何错误提示都会显示出来。

当我运行您的程序时,我看到了您的错误消息,但没有内置 Java 错误消息;这是应该的。确实,错误正在被抛出——但您正在捕获它们,因此 Java 不会在控制台上显示默认消息。

关于您在节目结束时的评论:

看看如果用户在菜单提示符处输入了错误的数据类型会发生什么;menuItem仍然为零。因此,所有 if 语句的计算结果都为 false。因此,else 语句运行,终止程序。

于 2012-12-01T03:49:36.703 回答
3

其他答案解释了为什么你没有得到你期望的输出。我还想指出您正在犯的几个重要的编程错误:

1)不要那样抓Exception

当您捕获时,您会捕获代码可能抛出的Exception每一个可能的子类型。Exception在您的情况下,您显然期望anInputMismatchException被抛出,但这些nextXxx方法也可以抛出其他异常,例如NoSuchElementExceptionor IllegalStateException。然后还有其他NullPointerException可能表明存在错误的可能性。

您应该明确地捕获并处理您所期望的异常,并将其他异常留给更通用的意外异常处理。

2)打印出Exception错误信息

它将为您提供实际错误的详细信息;例如

  try {
    menuItem = reader.nextInt(); 
  }
  catch(InputMismatchException e){
    reader.nextLine(); // Clear the input stream to avoid an infinite loop
    System.out.println(e.getMessage());
    System.out.println("Please a valid number 1-5.");
  }

事实上,异常是意料之外的,打印或记录异常的堆栈跟踪是一个好主意,以便您(或必须处理用户错误报告的人)可以弄清楚实际发生了什么。

3)当一个setter的验证失败时,抛出一个异常:

你的二传手做这种事情:

public boolean setInterestRate(double ir){
    if(ir >= 0 && ir <= 1){
        interestRate = ir;
        return true;
    }
    else return false;
}

问题是三方面的:

  • Setter 通常不会返回任何东西。这不是正常的成语。
  • 如果方法的调用者没有检查你的 setter 是否返回 false,它不会注意到 set 操作没有发生。
  • 没有任何东西说明设置操作失败的原因。

像这样写:

public boolean setInterestRate(double ir){
    if(ir >= 0 && ir <= 1){
        interestRate = ir;
    }
    else {
        throw new IllegalArgumentException(
            "Interest rate not between 0 and 1 (" + ir + ")");
    }
}
于 2012-12-01T05:10:52.607 回答
2

如果输入了无效的菜单项,您将得到 menuItem = 0,它会落在您的 if 阶梯底部并达到最后的中断。

于 2012-12-01T03:46:37.610 回答
2

在你的 catch 块的最后一个语句之后有一个 continue ,如下所示;

catch(Exception e){
reader.nextLine(); // Clear the input stream to avoid an infinite loop
System.out.println("Please a valid number 1-5.");
continue;
}

因为您允许程序在捕获异常后继续运行,该异常会停止您的程序,因为它是前进的。

希望这可以帮助。

于 2012-12-01T03:47:00.697 回答