使用 DecimalFormat 进行格式化会引发异常 - “无法将给定对象格式化为数字”

Posted

技术标签:

【中文标题】使用 DecimalFormat 进行格式化会引发异常 - “无法将给定对象格式化为数字”【英文标题】:Formatting using DecimalFormat throws exception - "Cannot format given Object as a Number" 【发布时间】:2017-07-24 06:44:41 【问题描述】:

这可能看起来像是一个重复的问题,但我尝试了以下所有链接,但无法得到正确答案。

Cannot format given Object as a Number ComboBox

Illegal Argument Exception

但我不明白出了什么问题。这是我的代码

DecimalFormat twoDForm = new DecimalFormat("#.##");
double externalmark = 1.86;
double internalmark = 4.0;
System.out.println(String.valueOf((externalmark*3+internalmark*1)/4));
String val = String.valueOf((externalmark*3+internalmark*1)/4);
String wgpa1=twoDForm.format(val); // gives exception
String wgpa2=twoDForm.format((externalmark*3+internalmark*1)/4)); // works fine
System.out.println(wgpa1);

format 方法采用 Object 类型参数,这就是为什么我传递了一个 String 对象,它给出了异常

线程“main”java.lang.IllegalArgumentException 中的异常:不能 将给定的对象格式化为数字。

但是当我给双值作为参数时,程序运行良好。但是,如果该方法是使用 Object 类型参数定义的,为什么我在传递 String 时遇到异常,而在传递 double 时没有遇到异常?

【问题讨论】:

【参考方案1】:

DecimalFormatformat() 方法被重载。

在工作情况下,您正在调用:

 public final String format(double number)

在失败的情况下,您正在调用:

 public final String format (Object obj) 

第一种方法采用一个非常具体的参数。它需要double

第二种情况不是这样,它接受的类型非常广泛:Object,因此在运行时对传递的类型进行检查。

通过提供不是double 而是String 的参数,调用的方法是第二个。

在底层,此方法依赖于 format(Object number, StringBuffer toAppendTo, FieldPosition pos) 方法,该方法期望 number 参数是 Number 类的实例(ShortLong、...Double) :

@Override
public final StringBuffer format(Object number,
                                 StringBuffer toAppendTo,
                                 FieldPosition pos) 
    if (number instanceof Long || 
        number instanceof Integer ||                   
        number instanceof Short || 
        number instanceof Byte ||                   
        number instanceof AtomicInteger ||
        number instanceof AtomicLong ||
        (number instanceof BigInteger && ((BigInteger)number).bitLength () < 64)) 

        return format(((Number)number).longValue(), toAppendTo, pos);
     else if (number instanceof BigDecimal) 
        return format((BigDecimal)number, toAppendTo, pos);
     else if (number instanceof BigInteger) 
        return format((BigInteger)number, toAppendTo, pos);
     else if (number instanceof Number) 
        return format(((Number)number).doubleValue(), toAppendTo, pos);
     else 
        throw new IllegalArgumentException("Cannot format given Object as a Number");
    

但情况并非如此,因为您将 String 实例传递给它。

要解决此问题,请在成功案例中传递double 原语,或者将String 转换为Number 的实例,例如DoubleDouble.valueOf(yourString)。 我建议第一种方式(传递double),因为它在您的代码中已经使用double 原语更自然。 第二个需要额外的从StringDouble 的转换操作。

【讨论】:

你好 YCF !我开始变得更好了!太好了。你好吗? :) 最后一次非常高兴:)【参考方案2】:

如果有任何数学计算,那么使用 java.math.BigDecimal 类的方法是更好的选择,以确保结果的准确性和效率,即使数字太大。 使用 java.math.BigDecimal 代码:

double externalmark1 = 1.86;
double internalmark2 = 4.0;
System.out.println(String.valueOf((externalmark1*3+internalmark2*1)/4));
System.out.println("------------------------");

BigDecimal decimalValue1 = new BigDecimal((externalmark1*3+internalmark2*1)/4).setScale(2, RoundingMode.HALF_UP);       
System.out.println("aggregatemark [direct decimalValue]: "+decimalValue1.toString());
System.out.println("------------------------");

double aggregatemark = (externalmark1*3+internalmark2*1)/4;
System.out.println("aggregatemark [double]: "+aggregatemark);       
BigDecimal decimalValue2 = new BigDecimal(aggregatemark).setScale(2, RoundingMode.HALF_UP);     
System.out.println("aggregatemark [decimalValue]: "+decimalValue2.toString());
System.out.println("------------------------");

String aggregatemarkStr = String.valueOf((externalmark1*3+internalmark2*1)/4);
System.out.println("aggregatemark [string] : "+aggregatemarkStr);       
BigDecimal decimalValue3 = new BigDecimal(aggregatemarkStr).setScale(2, RoundingMode.HALF_UP);      
System.out.println("aggregatemark [decimalValue]: "+decimalValue3.toString());

【讨论】:

【参考方案3】:

答案在javadoc。它清楚地说,“数字可以是 Number 的任何子类”,并说它抛出 IllegalArgumentException“如果 number 为 null 或不是 Number 的实例。”

(那么他们为什么不直接将参数设为 Number 类型?因为该类是抽象 Format 类的子类,不限于数字格式。显然,期望是这样一般的Format 类有一个带有Object 参数的方法,Format 的子类预计会将参数限制为它们可以处理的对象类型,它们必须在运行时执行。)

【讨论】:

以上是关于使用 DecimalFormat 进行格式化会引发异常 - “无法将给定对象格式化为数字”的主要内容,如果未能解决你的问题,请参考以下文章

java 使用DecimalFormat进行数字的格式化实例详解

Java数据格式化使用DecimalFormat 对Float和double进行格式化

使用 DecimalFormat 格式化 Double 以在没有科学计数法的情况下打印

DecimalFormat类

引入DecimalFormat类进行数字格式化操作

关于DecimalFormat的用法