编写高质量代码:改善Java程序的151个建议 | 第二章 基本类型

编写高质量代码:改善Java程序的151个建议

第二章 基本类型

建议21:用偶判断,不用奇判断
  • 不要使用奇判断(i%2 == 1 ? “奇数” : “偶数”),使用偶判断(i%2 == 0 ? “偶数” : “奇数”)。原因Java中的取余(%标识符)算法:测试数据输入1 2 0 -1 -2,奇判断的时候,当输入-1时,也会返回偶数。

    public class Suggest21 {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            System.out.println(" 请输入多个数字判断奇偶: ");
            while (scanner.hasNextInt()) {
                int i = scanner.nextInt();
                String str = i + " ->" + (i%2 == 1 ? "奇数" : "偶数");
                System.out.println(str);
            }
        }
    }
    
    // 模拟取余计算,dividend被除数,divisor除数
    public static int remainder(int dividend, int divisor) {
        return dividend - dividend / divisor * divisor;
    }
    

    根据上面的模拟取余计算可知, 当输入 -1 的时候, 运算结果是 -1, 当然不等于1了,所以它就被判定为 偶数了, 也就是我们说的判断失误了。

建议22:用整数类型处理货币
  • 不要使用float或者double计算货币,因为在计算机中浮点数“有可能”是不准确的,它只能无限接近准确值,而不能完全精确。不能使用计算机中的二进制位来表示如0.4等的浮点数。

    public class Suggest22 {
        public static void main(String[] args) {
            System.out.println(10.00 - 9.60);
            // 解决,但涉及到四舍五入不推荐
            DecimalFormat f = new DecimalFormat("#.##");
            System.out.println(f.format(10.00-9.60));
        }
    }
    // 0.40000000000000036
    
    • 解决方案:

      1. 使用BigDecimal(优先使用)

        BigDecimal是专门为弥补浮点数无法精确计算的缺憾而设计的类,并且它本身也提供了加减乘除的常用数学算法。特别是与数据库Decimal类型的字段映射时,BigDecimal是最优的解决方案。

      2. 使用整型

        把参与运算的值扩大100倍,并转变为整型,然后在展现时再缩小100倍,这样处理的好处是计算简单、准确,一般在非金融行业(如零售行业)应用较多。此方法还会用于某些零售POS机,它们的输入和输出全部是整数,那运算就更简单。

建议23:不要让类型默默转换
  • 基本类型转换时,使用主动声明方式减少不必要的Bug

    public class Suggest23 {
        // 光速是 30万公里/秒 常量
        public static final int LIGHT_SPEED = 30 * 10000 * 1000;
    
    
        public static void main(String[] args) {
            System.out .println( "题目1:月亮光照射到地球需要1秒,计算月亮和地球的距离。");
            long dis1 = LIGHT_SPEED * 1 ;
    
            System.out.println("月亮与地球的距离是: " + dis1 + " 米");
            System.out.println ( " ---------------------");
            System.out .println( "题目2:太阳光照射到地球上需要8分钟,计算太阳到地球的距禺。" );
            // 可能要超出整数范围,使用long型
            long dis2 = LIGHT_SPEED * 60 * 8;
            System.out.println( "太阳与地球的距离是:" + dis2 + "米");
    
            long dis3 = LIGHT_SPEED * 60L * 8;
            System.out.println( "太阳与地球的距离是:" + dis2 + "米");
        }
    }
    
    题目1:月亮光照射到地球需要1秒,计算月亮和地球的距离。
    月亮与地球的距离是: 300000000 米
     ---------------------
    题目2:太阳光照射到地球上需要8分钟,计算太阳到地球的距禺。
    太阳与地球的距离是:-2028888064米
    太阳与地球的距离是:144000000000米
    

    以上两句在参与运算时会溢出,因为Java是先运算后再进行类型转换的。因为dis2的三个运算参数都是int类型,三者相乘的结果也是int类型,但是已经超过了int的最大值,所以越界了。解决方法,在运算参数60后加L即可(还没有超过 int 类型的范围时就已经转换为 long 型了, 扯底解决越界问题)。

    实际做法: long dis3 = 1L * LIGHT_SPEED * 60 * 8;

建议24:边界、边界、还是边界
  • 数字越界是检验条件失效,边界测试;检验条件if(order>0 && order+cur<=LIMIT),输入的数大于0,加上cur的值之后溢出为负值,小于LIMIT,所以满足条件,但不符合要求

    public class Suggest24 {
    
        // 一个会员拥有产品的最多数量
        public final static int LIMIT  = 2000;
    
        public static void main(String[] args) {
            // 会员当前拥有的产品数量
            int cur = 1000;
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入需要预定的数量: ");
            while (scanner.hasNextInt()) {
                int order = scanner.nextInt();
                // 当前拥有的与准备订购的产品数量之和
                if (order > 0 && order + cur <= LIMIT) {
                    System.out.println("你已成功预定的 " + order + "个产品");
                }
                else {
                    System.out.println("超过限额,预定失败!");
                }
            }
        }
    }
    
    请输入需要预定的数量: 
    800
    你已成功预定的 800个产品
    900
    你已成功预定的 900个产品
    2147483647
    你已成功预定的 2147483647个产品
    2001
    超过限额,预定失败!
    
建议25:不要让四舍五入亏了一方
  • Math.round(10.5)输出结果11;Math.round(-10.5)输出结果-10。这是因为Math.round采用的舍入规则所决定的(采用的是正无穷方向舍入规则),根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失

    public class Suggest25 {
        public static void main(String[] args) {
            System.out.println("10.5的近似值: " + Math.round(10.5));
            System.out.println("-10.5的近似值: " + Math.round(-10.5));
        }
    }
    
建议26:提防包装类型的null值
  • 泛型中不能使用基本类型,只能使用包装类型,null执行自动拆箱操作会抛NullPointerException异常,因为自动拆箱是通过调用包装对象的intValue方法来实现的,而访问null的intValue方法会报空指针异常。谨记一点:包装类参与运算时,要做null值校验,即(i!=null ? i : 0)

    public class Suggest26 {
    
        public static int f(List<Integer> list) {
            int count = 0;
            for (Integer i : list) {
                count += i;
            }
            return count;
        }
    
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(null);
            System.out.println(f(list));
        }
    }
    
    
建议27:谨慎包装类型的大小比较
  • 大于>或者小于<比较时,包装类型会调用intValue方法,执行自动拆箱比较。而==等号用来判断两个操作数是否有相等关系的,如果是基本类型则判断数值是否相等,如果是对象则判断是否是一个对象的两个引用,也就是地址是否相等。通过两次new操作产生的两个包装类型,地址肯定不相等。

    public class Suggest27 {
        public static void main(String[] args) {
            Integer i = new Integer(100);
            Integer j = new Integer(100);
            compare(i,j);
        }
    
        public static void compare(Integer i, Integer j) {
            System.out.println(i == j);
            System.out.println(i > j);
            System.out.println(i < j);
        }
    }
    
    
建议28:优先使用整型池
  • 自动装箱是通过调用valueOf方法来实现的,包装类的valueOf生成包装实例可以显著提高空间和时间性能)valueOf方法实现源码:

    public static Integer valueOf(int i) {	final int offset = 128;
                                          if (i >= -128 && i <=127) {
                                              return IntegerCache.cache[i + offset];
                                          }	
                                          return new Integer(i);
                                         }
    class IntegerCache {	
        static final Integer cache[] = new Integer[-(-128) + 127 + 1];	
        static {		
            for (int i = 0; i < cache.length; i++)
                cache[i] = new Integer(i - 128);	
        }
    }
    
  • cache是IntegerCache内部类的一个静态数组,容纳的是**-128到127**之间的Integer对象。通过valueOf产生包装对象时,如果int参数在-128到127之间,则直接从整型池中获得对象,不在该范围的int类型则通过new生成包装对象。在判断对象是否相等的时候,最好是利用equals方法,避免“==”产生非预期结果。

    public class Suggest28 {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextInt()) {
                int i = scanner.nextInt();
                System.out.println("\n====" + i + " 的相等判断");
                // 2 个通过  new 产生的 Integer 对象
                Integer one = new Integer(i);
                Integer two = new Integer(i);
    
                System.out.println("new 产生的对象: " + (one == two));
                one = i;
                two = i;
                System.out.println("基本类型转换的对象: " + (one == two));
    
                // 通过静态方法生成一个实例
                one = Integer.valueOf(i);
                two = Integer.valueOf(i);
                System.out.println("valueOf产生的对象: " + (one == two));
            }
        }
    }
    
建议29:优先选择基本类型
  • int参数先加宽转变成long型,然后自动转换成Long型。Integer.valueOf(i)参数先自动拆箱转变为int类型,与之前类似

    public class Suggest29 {
        public static void main(String[] args) {
            Suggest29 suggest29 = new Suggest29();
            int i = 140;
            suggest29.f(i);
            suggest29.f(Integer.valueOf(i));
        }
    
        public void f(long a) {
            System.out.println("基本方法类型被调用");
        }
    
        public void f(Long a) {
            System.out.println("包装方法类型被调用");
        }
    }
    
    基本方法类型被调用
    基本方法类型被调用
    

    自动装箱有一个重要的原则:基本类型可以先加宽,再转变成宽类型的包装类型,但不能直接转变成宽类型的包装类型。

建议30:不要随便设置随机种子
  • 若非必要,不要设置随机数种子)(Random r = new Random(1000);

    该代码中1000即为随机种子。在同一台机器上,不管运行多少次,所打印的随机数都是相同的。

    在Java中,随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:

    1. 种子不同,产生不同的随机数;

    2. 种子相同,即使实例不同也产生相同的随机。

    Random类默认种子(无参构造)是System.nanoTime()的返回值,这个值是距离某一个固定时间点的纳秒数,所以可以产生随机数。java.util.Random类与Math.random方法原理相同

    public class Suggest30 {
        public static void main(String[] args) {
            demo01();
            demo02();
        }
    
        private static void demo01() {
            Random r = new Random();
            for (int i = 0; i < 4; i++) {
                System.out.println("第 " + i + " 次: " + r.nextInt());
            }
        }
    
        private static void demo02() {
            System.out.println("======demo02=======");
            Random r = new Random(1000);
            for (int i = 0; i < 10; i++) {
                System.out.println("第 " + i + " 次: " + r.nextInt());
           }
        }
    }
    
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值