从kafka源码阅读中看出了问题之除零不异常

最近在看kafka源码,

看到一个让我感到很陌生的地方,也引起了我深深地思考。

竟然还有可以除0合法的地方:

在org.apache.kafka.common.metrics.stats.Max类中,看到了构造函数:

    public Max() {
        super(Double.NEGATIVE_INFINITY);
    }

我忍不住F3了一下Double.NEGATIVE_INFINITY,然后看到了令我震惊的地方:

在java基础类库,java.lang.Double类中,竟然还有这么一个定义:

/**
     * A constant holding the positive infinity of type
     * {@code double}. It is equal to the value returned by
     * {@code Double.longBitsToDouble(0x7ff0000000000000L)}.
     */
    public static final double POSITIVE_INFINITY = 1.0 / 0.0;

    /**
     * A constant holding the negative infinity of type
     * {@code double}. It is equal to the value returned by
     * {@code Double.longBitsToDouble(0xfff0000000000000L)}.
     */
    public static final double NEGATIVE_INFINITY = -1.0 / 0.0;

给我的震撼,以至于让我要把注释也带上,以充分看到更多信息。

这不会引起java.lang.ArithmeticException吗?

我赶紧写了下面这么一个程序:

package com.zte.cook.cainiao;

/**
 * 
 * @author heng.guo
 * @date 2018-08-01
 *
 */
public class Test {
    private static double a = 1.0 / 0.0;
    
    public double b = 1.0 / 0.0;
    
    public static void main(String[] args) {
        System.out.println("a : " + a);
        System.out.println("b : " + new Test().b);
        double c = 1.0 / 0.0;
        System.out.println("c : " + c);
        double zero = 0.0;
        double d = 1.0 / zero;
        System.out.println("d : " + d);
        int iZero = 0;
        double e = 1.0 / iZero;
        System.out.println("e : " + e);
        int f = 1 / iZero;
        System.out.println("f" + f);
    }
}

再把运行结果贴上:

int f = 1 / iZero引起了java.lang.ArithmeticException。 其余都能正常输出,为什么int就不可以呢?

在文章开头,我们看见了java.lang.Double类中定义了这么一个除0的类静态常量。

那么java.lang.Integer有没有这么一个定义呢?

我翻遍了这么一个类,没有这样的定义。我又写了这么一段代码,打算从反编译角度入手看看:

/**
 * 
 * @author heng.guo
 * @date 2018-08-01
 *
 */
public class Test {
    public static void main(String[] args) {
        double d = 1.0 / 0;
        int i = 1 / 0;
    }
}

 在JDK1.8.0_151的环境下,我使用javac编译了Test.java文件,得到Test.class字节码文件,然后利用javap反编译,从字节码角度分析一下这份代码:

注意,我用蓝线圈起来的部分,我们可以看到原来double d = 1.0 / 0.0被悄悄替换成了double Infinityd,即无穷大。

而int则仍然使用到了1和0(注意iconst_1和iconst_0)。

那么是不是javac看见double类型的变量,赋值时被除数是0,都会悄悄替换掉呢?

看下面代码:

/**
 * 
 * @author heng.guo
 * @date 2018-08-01
 *
 */
public class Test {
    public static void main(String[] args) {
        double d = 1.0 / 0;
        double t = 2.2 / 0;
        double f = 3.3 / 0.0;
    }
}

反编译结果:

 果然,全部都被javac悄悄替换掉了。

那么,double值除0会不会引起异常呢?我们看看下面这段代码:

/**
 * 
 * @author heng.guo
 * @date 2018-08-01
 *
 */
public class Test {
    public static void main(String[] args) {
        double d = Double.parseDouble(args[0]);
        divide(d);
    }

    public static void divide(double d) {
        double e = 1.0 / d;
    }
}

 我把被除数,放到运行期才赋值,避开编译器javac, 看看jvm是怎么处理除0的。

即使放到运行期,仍然没有报错。没有引起java.lang.ArithmeticException。这样来看,double除0简直是开挂了。

我从字节码文件的反编译结果,没有看出什么。这引起了我的思考。

我现在能想到的是,javac和jvm在double除0的时候,返回的Infinity或者-Infinity。也就是正无穷大和负无穷大。

而对int则会引起错误。

同时在java.lang.Float类中,也定义了这么一些常量:

    /**
     * A constant holding the positive infinity of type
     * {@code float}. It is equal to the value returned by
     * {@code Float.intBitsToFloat(0x7f800000)}.
     */
    public static final float POSITIVE_INFINITY = 1.0f / 0.0f;

    /**
     * A constant holding the negative infinity of type
     * {@code float}. It is equal to the value returned by
     * {@code Float.intBitsToFloat(0xff800000)}.
     */
    public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;

 并且float在除0时也不会引起异常。另外,我还看了java.lang.Long类,类中也没有无穷大常量。归根到底,问题出在 / 这个符号上了。

【总结】:double和float除0返回无穷大。int和long除0抛出异常。即浮点型除0返回无穷大,整形除0返回异常。

【思考】: java中除操作究竟发生了什么?对于整型和浮点型为什么会有区别?
 

猜你喜欢

转载自blog.csdn.net/guohengcook/article/details/81348257
今日推荐