什么是NumberFormatException,我该如何解决它?

32
Error Message:
Exception in thread "main" java.lang.NumberFormatException: For input string: "Ace of Clubs"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at set07102.Cards.main(Cards.java:68)
C:\Users\qasim\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 0 seconds)

我的 While 循环:

while (response != 'q' && index < 52) {
    System.out.println(cards[index]);
    int first_value = Integer.parseInt(cards[index]);
    int value = 0;
    //Add a Scanner
    Scanner scanner = new Scanner(System.in);
    System.out.println("Will the next card be higher or lower?, press q if you want to quit");
    String guess = scanner.nextLine();
    if(cards[index].startsWith("Ace")) { value = 1; }
    if(cards[index].startsWith("2")) { value = 2; }
    if(cards[index].startsWith("3")) { value = 3; }
    //checking 4-10
    if(cards[index].startsWith("Queen")){ value = 11; }
    if(cards[index].startsWith("King")){ value = 12; }
    if(guess.startsWith("h")){
        if(value > first_value){ System.out.println("You answer was right, weldone!"); } 
        else { System.out.println("You answer was wrong, try again!"); }
    } else if(guess.startsWith("l")){
        if(value < first_value) { System.out.println("You answer as right, try again!"); }
        else { System.out.println("You answer was wrong, try again!"); }
    } else { System.out.println("Your was not valid, try again!"); }
    scanner.close();            
    index++;
}//end of while loop

8
int first_value = Integer.parseInt(cards[index]); - 你试图将字符串解析为整数,但该字符串是“草花A”。 - khelwood
1
你缺少一张牌...国王是13,皇后是12,杰克是11,只是说一下 ;) 你应该使用if else,因为你不能有一张以“king”和“3”开头的牌。为什么要使用52的索引限制?你没有使用颜色。最后一件事,如果你尝试输入“q”,在结束之前你将收到一个无效答案的消息。至于错误,一切都已经说过了。 - AxelH
9个回答

50
Error Message:
Exception in thread "main" java.lang.NumberFormatException: For input string: "Ace of Clubs"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at set07102.Cards.main(Cards.java:68)
C:\Users\qasim\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
意思是:
There was an error. We try to give you as much information as possible
It was an Exception in main thread. It's called NumberFormatException and has occurred for input "Ace of Clubs".
at line 65th of NumberFormatException.java which is a constructor,
which was invoked from Integer.parseInt() which is in file Integer.java in line 580,
which was invoked from Integer.parseInt() which is in file Integer.java in line 615,
which was invoked from method main in file Cards.java in line 68.

It has resulted in exit code 1

换句话说,您尝试将"Ace of Clubs"解析为Java无法使用Integer.parseInt方法的int。Java提供了美丽的堆栈跟踪,告诉您确切的问题所在。您需要的工具是调试器,使用断点将允许您检查所选时刻的应用程序状态

如果您要使用解析,则可能的解决方案是以下逻辑:

if (cards[index].startsWith("Ace")) 
    value = 1;
else if (cards[index].startsWith("King"))
    value = 12;
else if (cards[index].startsWith("Queen"))
    value = 11;
...
else {
    try {
        Integer.parseInt(string.substring(0, cards[index].indexOf(" "))); 
    } catch (NumberFormatException e){
        //something went wrong
    }
}

Java中的异常是什么?

异常是程序执行过程中发生的事件,打破了程序指令的正常流程。

-文档

Integer#parseInt中的构造函数和用法

static NumberFormatException forInputString(String s) {
    return new NumberFormatException("For input string: \"" + s + "\"");
}

public NumberFormatException (String s) {
    super (s);
}

它们对于理解如何阅读堆栈跟踪非常重要。看看 NumberFormatException 如何从 Integer#parseInt 抛出:

if (s == null) {
    throw new NumberFormatException("null");
}

如果输入的String s格式无法解析,则稍后会出现此问题:

throw NumberFormatException.forInputString(s); 

什么是NumberFormatException

抛出该异常表示应用程序尝试将字符串转换为数字类型之一,但该字符串不具有适当的格式。

-文档

NumberFormatException 扩展 IllegalArgumentException。它告诉我们它比IllegalArgumentException更专业化。实际上,它用于强调虽然参数类型正确(String),但String的内容不是数字(a,b,c,d,e,f在HEX中被视为数字并且在需要时是合法的)。

我该如何修复它?
好吧,不要修复它被抛出的事实。它被抛出是很好的。有一些事情您需要考虑:

  1. 我可以读取堆栈跟踪吗?
  2. 导致异常Stringnull吗?
  3. 它看起来像一个数字吗?
  4. 它是“我的字符串”还是用户的输入?
  5. 待续

广告 1。

消息的第一行是提示异常已经发生和导致问题的输入String。字符串始终跟随:并被引用("some text")。然后您会对从末尾阅读堆栈跟踪产生兴趣,因为前几行通常是NumberFormatException的构造函数、解析方法等。最后,在您制造错误的方法中会指出它。将指出在哪个文件和哪个方法中调用它。即使附加了一行。您将看到。如何阅读堆栈跟踪的示例如上所述。

广告 2。

当您看到,而不是"For input string:"和输入,有一个null(不是"null"),这意味着您试图将空引用传递给数字。如果您实际上希望将其视为0或任何其他数字,则可能会对我在StackOverflow上的另一篇文章感兴趣。它在这里可用。

有关解决意外null的描述在StackOverflow线程什么是NullPointerException,我该如何修复它?

广告 3。

如果跟在冒号后面并用引号引起来的字符串看起来像一个数字,那么可能存在一个字符无法被解码或有不可见的空格。显然,"“ 6”"和"“123 ”"都无法被解析。这是因为空格造成的。但有时候,字符串看起来像"“6”",实际上长度可能大于你看到的数字位数。

在这种情况下,我建议使用调试器或至少使用System.out.println打印您尝试解析的String的长度。如果显示的长度超过数字位数,请尝试将stringToParse.trim()传递给解析方法。如果这样还不行,请复制冒号后面的整个字符串,并使用在线解码器进行解码。它会给出所有字符的代码。

另外,最近我在StackOverflow上发现了一种情况,即您可能会看到输入看起来像一个数字,例如"1.86",它只包含这4个字符,但仍存在错误。请记住,只能使用#Integer#parseInt#解析整数。对于解析十进制数,应该使用Double#parseDouble

另一种情况是,当数字具有许多位时。它可能太大或太小,无法适应intlong。您可能需要尝试new BigDecimal(<str>)

最后,我们来到了一个共识的地方,即我们不能避免用户将“abc”作为数字字符串输入的情况。为什么?因为他可以。在幸运的情况下,这是因为他是测试人员或者只是一个极客。在不好的情况下,这是攻击者。

现在我该怎么办? 好吧,Java给我们try-catch,你可以做以下操作:
try {
    i = Integer.parseInt(myString);
} catch (NumberFormatException e) {
    e.printStackTrace();
    //somehow workout the issue with an improper input. It's up to your business logic.
}

"黑桃A"。此外,在您的错误详细描述中,我认为第二个Integer.parseInt的行号有一个打字错误,应该是580而不是615。 - Abhineet
1
当我有时间时,我会专注于点5的太大的数字。 - xenteros
2
一个很好的回答。我想我应该更经常地将这个问题标记为重复;-) - GhostCat
1
@GhostCat,这就是为什么我准备了这个答案。 - xenteros
1
@OleV.V. 当然可以!已更新。 - xenteros

11

什么是NumberFormatException

该异常表示应用程序尝试将一个字符串转换为数值类型之一,但该字符串的格式不正确。

根据您的堆栈跟踪,在您的情况下,此异常是由Integer.parseInt(String)引发的,这意味着提供的String不包含可解析的integer。还根据堆栈跟踪,这是因为您尝试将String"Ace of Clubs"解析为整数,这是行不通的,因为它不是整数的String表示形式。

如何修复?

最简单且常规的方法是捕获NumberFormatException异常。

int value = -1;
try {
    value = Integer.parseInt(myString);
} catch (NumberFormatException e) {
    // The format was incorrect
}

它可以工作,但捕获异常很慢,因为需要构建函数调用栈来创建代价高昂的Exception,所以如果可以避免就避免。此外,您需要正确地处理异常,这并不总是明显的。

或者你可以使用正则表达式先检查String是否与Integer匹配,但这很容易出错,因为你可能会使用错误的正则表达式


在您的情况下,应该使用更多面向对象的方法来代替处理String。例如,您可以使用一个classenum来表示卡牌,而不是使用简单的String,因为这更容易出错,如您已经注意到的那样。

因此,如果您决定为您的卡片使用专用类,您的代码可以是:

public class Card {

    private final Rank rank;
    private final Suit suit;

    public Card(final Rank rank, final Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public Rank getRank() {
        return this.rank;
    }

    public Suit getSuit() {
        return this.suit;
    }
}

对于卡牌的花色和等级,我们可以使用enum,因为现有等级和花色的数量是有限的。

public enum Rank {
    ACE(1), TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), HEIGHT(8),
    NINE(9), TEN(10), JACK(11), QUEEN(12), KING(13);

    private final int value;

    Rank(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }
}

public enum Suit {
    SPADE, HEART, DIAMOND, CLUB
}

那么cards将是一个Card数组,而不是一个String数组,并且可以按如下方式初始化:

那么cards将是一个Card数组,而不是一个String数组,并且可以按如下方式初始化:

Rank[] ranks = Rank.values();
Suit[] suits = Suit.values();
Card[] cards = new Card[ranks.length * suits.length];
for (int i = 0; i < ranks.length; i++) {
    for (int j = 0; j < suits.length; j++) {
        cards[i * suits.length + j] = new Card(ranks[i], suits[j]);
    }
}
如果您需要洗牌您的纸牌数组,可以按如下方式进行(请注意,如果您决定使用卡牌的List而不是数组,只需使用Collections.shuffle(list)即可)。

如果你需要将你的卡牌数组洗牌,可以按照以下步骤进行(请注意,如果你决定使用卡牌的List而非一个数组,你只需要使用Collections.shuffle(list)

List<Card> allCards = Arrays.asList(cards);
Collections.shuffle(allCards);
allCards.toArray(cards);

那么您将能够直接访问您的卡片价值,使用 cards[index].getRank().getValue() ,而不用冒着抛出异常的风险(除非您使用不当的索引,会抛出 IndexOutOfBoundsException 异常)。


我不会同意你的论点,即捕获NFE很丑陋。在大型系统中,当您可以假设用户会提供非数字输入时,另一方面,您希望保持日志的清洁,最好捕获它并记录信息或抛出自己的服务异常,而不是让整个堆栈跟踪以红色打印。 - xenteros
3
一般来说,捕获异常会很慢,因为它需要建立调用栈,以创建Exception,这是很费时的。所以如果可以避免使用异常最好不要使用,但有时你无法避免。在这里,您可以通过使用真正的面向对象方法来避免异常,这就是我提出的方法。 - Nicolas Filotto

6

看起来cards[]是一个字符串数组,你正在尝试将Ace of Clubs转换为整数

int first_value = Integer.parseInt(cards[index]);

3
java.lang.NumberFormatException 

发生在您尝试解析不是数字字符串的输入时。 在您的情况下,您正在尝试将一个非数字字符串解析为整数。 由于这是不可能的,因此会出现NumberFormatException异常。
int first_value = Integer.parseInt(cards[index]);//cards[index] value should be //number string "123" not "abc"

3

NumberFormatException是Java所用的一种方式,表示“我试图将一个字符串转换为整数,但无法完成”。

在您的异常跟踪中,您可以阅读:

Exception in thread "main" java.lang.NumberFormatException: For input string: "Ace of Clubs"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at set07102.Cards.main(Cards.java:68)

基本上,这意味着在您的代码的第68行调用Integer.parseInt方法时,将"Ace of Clubs"作为参数传递。此方法期望表示为字符串的整数值,例如“4”,因此该方法会抱怨并抛出NumberFormatException异常,因为"Ace of Clubs"根本不像是一个整数。


2
NumberFormatException 表示 Integer.parseInt() 无法将字符串转换为数字。
我建议以下两种方案之一:
1. 将卡片封装为名称(字符串)/值(整数)组合。使用值进行比较,使用名称向用户呈现信息。然后,Cards[] 变成了卡片列表,而不是字符串列表。
2. 自己解析字符串。这可能更容易,因为您已经在 if(cards[index].startsWith("Ace")) { value = 1; } 中完成了这些操作。您可以将其移入名为 CardToInt() 的函数中,并使用该函数代替 Integer.parseInt()

1

让我最困惑的第一件事(无意冒犯)是你将值限制在1-13之间,而实际上应该是0-52。另外,按照你的逻辑,这个值总是会更高。更好的方法是使用数字生成器。以下是我的代码,使用数字生成器(或Java随机数生成器):

public static void main(String[] args) {

String[] cards = { "Ace of Clubs", "1 of Clubs", "2 of Clubs",
        "3 of Clubs", "4 of Clubs", "5 of Clubs", "6 of Clubs",
        "7 of Clubs", "8 of Clubs", "9 of Clubs", "10 of Clubs",
        "Queen of Clubs", "King of Clubs", "Ace of Diamonds",
        "1 of Diamonds", "2 of Diamonds", "3 of Diamonds",
        "4 of Diamonds", "5 of Diamonds", "6 of Diamonds",
        "7 of Diamonds", "8 of Diamonds", "9 of Diamonds",
        "10 of Diamonds", "Queen of Diamonds", "King of Diamonds",
        "Ace of Hearts", "1 of Hearts", "2 of Hearts", "3 of Hearts",
        "4 of Hearts", "5 of Hearts", "6 of Hearts", "7 of Hearts",
        "8 of Hearts", "9 of Hearts", "10 of Hearts",
        "Queen of Hearts", "King of Hearts", "Ace of Spades",
        "1 of Spades", "2 of Spades", "3 of Spades", "4 of Spades",
        "5 of Spades", "6 of Spades", "7 of Spades", "8 of Spades",
        "9 of Spades", "10 of Spades", "Queen of Spades",
        "King of Spades" };

Scanner scanner = new Scanner(System.in);
Random rand = new Random();
String response = "";
int index = 0;
int value = 0;  
while (!response.equals("q") && index < 52) {

    // set next card value based on current set of cards in play
    if (cards[index].endsWith("Clubs")) {
        value = rand.nextInt(12);
    }
    if (cards[index].endsWith("Diamonds")) {
        value = rand.nextInt(12) + 13;
    }
    if (cards[index].endsWith("Hearts")) {
        value = rand.nextInt(12) + 26;
    }
    if (cards[index].endsWith("Spades")) {
        value = rand.nextInt(12) + 39;
    }

    // display card too user (NOTE: we use the random number not the index)
    System.out.println("Card is: " + cards[value]);

    // ask user what well the next card be
    System.out.println("Will the next card be higher or lower?, press q if you want to quit");
    response = scanner.nextLine();

    // display if user was right (NOTE: compared the random number to the current index)
    // ignore incorrect response and just continue
    if ((value > index && response.startsWith("h")) || (value < index && response.startsWith("l"))) {
        System.out.println("You answer was right, well done!");
    } else {
        System.out.println("You answer was wrong, try again!");
    }

    // continue loop
    index++;
}
}

关于NumberFormatException,我相信Nicolas Filotto已经解释得很好了。

1

异常出现在您的代码中,在那里将字符串转换为整数:

int first_value = Integer.parseInt(cards[index]);

当你传递一个字符串"Ace of Clubs",它无法转换为整数,因此会抛出数字格式异常。你可以使用以下方法:

try {
     ....
     // Your Code
     ....
    }
catch(NumberFormatException e)
{
    e.getMessage();  //You can use anyone like printStackTrace() ,getMessage() to handle the Exception
}

1
int first_value = Integer.parseInt(cards[index]); 

在编写上述语句时,您试图将“草花A”解析为数字。

您可以使用以下方法测试任何字符串是否可以解析为整数:

boolean tryParseInt(String value) {  
     try {  
         Integer.parseInt(value);  
         return true;  
      } catch (NumberFormatException e) {  
         return false;  
      }  
}

关于您的问题,什么是NumberFormatException:它被抛出以表明应用程序已尝试将字符串转换为数字类型之一,但该字符串没有适当的格式。(参考-http://docs.oracle.com/javase/7/docs/api/java/lang/NumberFormatException.html

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