常见错误代码注意点

这文章写了很长时间了,如果大家还有什么好玩的,神奇的,容易错误应用的java代码,可以推荐给我,整理出来。谢谢大家

import java.math.BigDecimal;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

/**
 * 常见错误代码注意点
 * @author Yuanqy
 * @time 2015年5月10日 9:23:00
 */
public class ExceptionTest {

    /**
     * 用例1:入箱与拆箱 <br>
     * Integer、Short、Byte、Character、Long这几个包装类的valueOf方法的实现是类似的:会有缓存cache.high,cache.low;<br>
     * Double、Float的valueOf方法的实现是类似的:要么直接赋值,要么通过String转。 <br>
     * Boolean的valueOf方法的实现是个三目运算,形如` return (b ? TRUE : FALSE);
     */
    @Test
    public void test1() {
        System.out.println("==test1=======================================");
//        //JDK,源代码:
//        public static Integer valueOf(int i) {
//            assert IntegerCache.high >= 127;
//            if (i >= IntegerCache.low && i <= IntegerCache.high)
//                return IntegerCache.cache[i + (-IntegerCache.low)];
//            return new Integer(i);
//        }
        Integer i1 = 128;
        Integer i2 = 128;
        Integer i3 = 127;
        Integer i4 = 127;
        System.out.println("i1 == i2:" + (i1 == i2));// false
        System.out.println("i3 == i4:" + (i3 == i4));// true
    }

    /**
     * 用例2:入箱与拆箱 <br>
     * 在java代码中,很多场景会用到装箱和拆箱操作,稍不留神,就会掉坑里。<br>
     * 保证不出异常,就在运算过程中用同种类型。装箱就全部装箱。不装箱就全部不装箱。
     */
    @Test
    public void test2() {
        class TestBean {
            private Integer test;

            public Integer getTest() {
                return test;
            }

            public void setTest(Integer test) {
                this.test = test;
            }
        }
        try {
            System.out.println("==test2=======================================");
            TestBean bean = new TestBean();
            bean.setTest(bean != null ? bean.getTest() : new Integer(1)); // 这行正常
            bean.setTest(bean != null ? bean.getTest() : 1);// 这行异常【很经典的异常】,原因是三元表达式的执行过程和 int/Integer 的入箱与拆箱 过程照成的。
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * 用例3:注意数据溢出
     */
    @Test
    public void test3() {
        System.out.println("==test3=======================================");
        int s = 1;
        for (int i = 10; i <= 49; i++) {
            s = s * i;
            System.out.println(s + "==" + Integer.toBinaryString(s));
        }
        System.out.println("结果是:" + s);// 结果是:0
    }

    /**
     * 用例4:不要相信 get/Select,要保证操作原子性
     * 并且:基本数据类型的 ++ ,-- 运算符 也非线程安全,是非原子性的。
     * 计数操作,使用AtomicXX类操作
     * 还有:Thread、Runnable、Callable、Future、FutureTask,这些多线程操作都有线程安全问题,需要特殊处理。
     * 【网上误人子弟的真JB多,特别是IO,多线程的文章】。
     * @throws InterruptedException
     */
    @Test
    public void test4() throws InterruptedException {
        System.out.println("==test4=======================================");
        int sum = 100;
        final CountDownLatch latch = new CountDownLatch(sum);
        Thread t = new Thread() {
            private AtomicInteger count = new AtomicInteger(0);// 计数不能使用int做++count
            private BigDecimal money = BigDecimal.ZERO;

            public BigDecimal getMoney() {
                return money;
            }

            public void setMoney(BigDecimal money) {
                this.money = money;
            }

            @Override
            public void run() {
                BigDecimal curM = BigDecimal.ZERO;
                // synchronized (curM) {//加同步块或读写锁
                try {
                    curM = getMoney();
                    curM = curM.add(BigDecimal.ONE);
                    setMoney(curM);
                    System.out.println(count.addAndGet(1) + ":getMoney=" + getMoney().toPlainString());
                    latch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // }
            }
        };
        // t只实例一次
        for (int i = 0; i < sum; i++) {
            new Thread(t).start();
        }
        latch.await();
        System.out.println("结果:执行了100次加操作,结果却不是100。");
    }

    /**
     * 用例5:涉及到小数操作,不允许出现float double浮点数类型
     * 特别是金融场景
     */
    @Test
    public void test5() {
        System.out.println("==test5=======================================");
        System.out.println(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1);
        System.out.println(0.1 * 10);
    }

    /**
     * 浮点数操作,最好使用BigDecimal 或 String
     */
    @Test
    public void test6() {
        System.out.println("==test6=======================================");
        Double d=123456789.98D;
        
        System.out.println(new BigDecimal(d).toString());// 错
        System.out.println(new Double(d).toString());// 错
        System.out.println(new BigDecimal(new Double(d).toString()).toString());// 对
        System.out.println(new BigDecimal(d).toPlainString());// 错
    }
}

执行结果控制台:

==test1=======================================
i1 == i2:false
i3 == i4:true
==test2=======================================
java.lang.NullPointerException
	at ExceptionTest.test2(ExceptionTest.java:60)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
==test3=======================================
10==1010
110==1101110
1320==10100101000
17160==100001100001000
240240==111010101001110000
3603600==1101101111110010010000
57657600==11011011111100100100000000
980179200==111010011011000101100100000000
463356416==11011100111100100001000000000
213837312==1100101111101110011000000000
-18221056==11111110111010011111100000000000
-382642176==11101001001100010101100000000000
171806720==1010001111011001000000000000
-343412736==11101011100001111111000000000000
348028928==10100101111101000000000000000
110788608==110100110101000000000000000
-1414463488==10101011101100010000000000000000
464191488==11011101010110000000000000000
112459776==110101101000000000000000000
-1033633792==11000010011001000000000000000000
-944242688==11000111101110000000000000000000
793247744==101111010010000000000000000000
-385875968==11101001000000000000000000000000
150994944==1001000000000000000000000000
838860800==110010000000000000000000000000
-704643072==11010110000000000000000000000000
402653184==11000000000000000000000000000
2013265920==1111000000000000000000000000000
-805306368==11010000000000000000000000000000
-1342177280==10110000000000000000000000000000
-2147483648==10000000000000000000000000000000
-2147483648==10000000000000000000000000000000
0==0
0==0
0==0
0==0
0==0
0==0
0==0
0==0
结果是:0
==test4=======================================
1:getMoney=1
2:getMoney=2
3:getMoney=3
4:getMoney=4
5:getMoney=5
6:getMoney=6
7:getMoney=7
8:getMoney=8
9:getMoney=9
10:getMoney=10
11:getMoney=11
12:getMoney=12
13:getMoney=13
14:getMoney=14
15:getMoney=15
16:getMoney=16
17:getMoney=17
18:getMoney=18
19:getMoney=19
20:getMoney=20
21:getMoney=21
22:getMoney=22
23:getMoney=22
24:getMoney=23
25:getMoney=24
26:getMoney=25
27:getMoney=26
28:getMoney=27
29:getMoney=28
30:getMoney=29
31:getMoney=30
32:getMoney=31
33:getMoney=32
34:getMoney=32
35:getMoney=33
36:getMoney=33
37:getMoney=34
38:getMoney=35
39:getMoney=36
40:getMoney=37
41:getMoney=38
42:getMoney=39
44:getMoney=39
43:getMoney=38
47:getMoney=42
49:getMoney=44
46:getMoney=41
51:getMoney=46
53:getMoney=48
45:getMoney=40
54:getMoney=49
57:getMoney=52
55:getMoney=50
60:getMoney=55
61:getMoney=56
62:getMoney=57
52:getMoney=47
50:getMoney=45
64:getMoney=59
48:getMoney=43
67:getMoney=61
68:getMoney=62
69:getMoney=63
71:getMoney=65
66:getMoney=60
72:getMoney=66
74:getMoney=68
65:getMoney=59
75:getMoney=69
76:getMoney=70
77:getMoney=71
63:getMoney=58
79:getMoney=73
80:getMoney=74
59:getMoney=54
81:getMoney=75
82:getMoney=76
58:getMoney=53
84:getMoney=78
85:getMoney=79
56:getMoney=51
83:getMoney=77
78:getMoney=72
89:getMoney=83
73:getMoney=67
91:getMoney=84
70:getMoney=64
99:getMoney=85
98:getMoney=90
96:getMoney=89
97:getMoney=89
95:getMoney=87
94:getMoney=86
100:getMoney=85
93:getMoney=85
92:getMoney=85
90:getMoney=83
88:getMoney=82
87:getMoney=81
86:getMoney=80
结果:执行了100次加操作,结果却不是100。
==test5=======================================
0.9999999999999999
1.0
==test6=======================================
123456789.98000000417232513427734375
1.2345678998E8
123456789.98
123456789.98000000417232513427734375

猜你喜欢

转载自my.oschina.net/jweb/blog/1814018