Java开发之基本数据类型使用相关建议

建议1:用偶判断,不用奇判断

import java.util.Scanner;

public class Client {
  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);
      String str2 = i + "->" + (remainder(i, 2) == 1 ? "奇数" : "偶数");
      System.out.println(str2);
      System.out.println(remainder(i, 2));
    }
  }
  //Java的取余(%)算法模拟代码
  public static int remainder(int dividend, int divisor) {
    return dividend - dividend / divisor * divisor;
  }
}

    当输入的数值为负奇数时(如:-1),该方法得到的结果却是偶数。

    正确判断应该是:i % 2 == 0? "偶数" : "奇数"

建议2:用整数类型处理货币

public class Client {
  public static void main(String[] args) {
    System.out.println(10.00 - 9.60);
  }
}

    得出结果:0.40000000000000036。利用乘2取整,顺序排列法,不能将结果0.4从十进制小数转换为二进制小数。

    解决办法:

  • 使用Bigdecimal:常用于金融行业
  • 使用整型:一般在非金融行业应用较多

建议3:不要让类型默默转换

    问题:光速每秒30万公里,根据光线旅行的时间,计算月亮与地球、太阳与地球之间的距离

public class Client {
  //光速:30万公里每秒,相当于30*10000*1000米每秒
  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("题目2:月亮光照射到地球需要8分钟,计算月亮和地球的距离:");
    long dis2 = LIGHT_SPEED * 60 * 8;
    System.out.println("月亮与地球的距离为:" + dis2);
  }
}

    输出结果如下:

题目1:月亮光照射到地球需要1秒,计算月亮和地球的距离:
月亮与地球的距离为:300000000
题目2:太阳光照射到地球需要8分钟,计算月亮和地球的距离:
太阳与地球的距离为:-2028888064

    太阳与地球的距离竟然是负值,这是因为Java是先运算然后再进行类型转换的,即先计算LIGHT_SPEED * 60 * 8的值,这时计算出的值已经超过int的最大值,所以就成了负值,最后再转换成long型,结果还是负值。

    解决这类问题的办法是:主动声明式类型转换,如long dis2 = 1L * LIGHT_SPEED * 60 * 8;

    结论:基本类型转换时,使用主动声明方式减少不必要的Bug。

建议4:边界,边界,还是边界

import java.util.Scanner;

public class Client {

  //一个会员拥有产品的最多数量
  public static final int LIMIT = 2000;

  public static void main(String[] args) {
    //会员当前拥有的产品数量
    int cur = 1000;
    Scanner input = new Scanner(System.in);
    System.out.println("请输入预订的数量:");
    while (input.hasNextInt()) {
      int order = input.nextInt();
      //当前拥有的与准备订购的产品数量之和
      if (order > 0 && (order + cur) <= LIMIT) {
        System.out.println("你已经成功预订" + order + "个产品");
      } else {
        System.out.println("超过限额,预订失败!");
      }
    }
  }
}

    可以得到如下结果:

请输入预订的数量:
800
你已经成功预订800个产品
2147483647
你已经成功预订2147483647个产品

    2147483647int类型的最大值,再加上1000就超出了int的范围了,其结果是-2147482649,当然是小于2000了!原因就在于:数字越界使校验条件失效。

    在单元测试中,有一项测试叫做边缘测试(或临界测试),如果一个方法接收的是int类型的参数,那以下三个值是必测的:0正最大负最小,其中正最大和负最小是边界值,如果这三个值都没有问题,方法才是比较安全可靠的。

建议5:不要让四舍五入亏了一方

场景:银行结息方式
   场景:银行结息方式
  • 四舍。舍弃的数值:0.000、0.001、0.002、0.003、0.004
  • 五入:进位的数值:0.005、0.006、0.007、0.008、0.009

    因为舍弃和进位的数字是在0到9之间均匀分布的,所以对于银行家来说,每10笔存款的利息因采用四舍五入而获得的盈利是:0.000+0.001+0.002+0.003+0.004-0.005-0.00-60.007-0.008-0.009=-0.005,也就是说,每10笔的利息计算中就损失0.005元,即每笔利息计算损失0.005元。

    这个算法误差是由美国银行家发现的,并对此提出了一个修正算法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一

Java 5以上提供的舍入法则:直接使用RoundingMode类提供的Round模式即可
import java.math.BigDecimal;
import java.math.RoundingMode;

public class Client {
  public static void main(String[] args) {
    //存款
    BigDecimal d = new BigDecimal(888888);
    //月利率,乘3计算季利率
    BigDecimal r = new BigDecimal(0.0018 * 75 * 3);
    //计算利息,setScale方法设置精度和舍入法则
    BigDecimal i = d.multiply(r).setScale(2, RoundingMode.HALF_EVEN);
    System.out.println("季利息是:" + i);
  }
}
目前Java支持的舍入方式
  • ROUND_UP:远离零方向舍入,只要舍弃位非零即进位
  • ROUND_DOWN:趋向零方向舍入,所有的位都舍弃,不存在进位情况
  • ROUND_CEILING:向正无穷方向舍入,向正最大方向靠拢
  • ROUND_FLOOR:向负无穷方向舍入,向负无穷方向靠拢
  • ROUND_HALF_UP:最近数字舍入(5进),经典四舍五入法则
  • ROUND_HALF_DOWN:最近数字舍入(5舍)
  • ROUND_HALF_EVEN:银行家算法
结论:根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。

建议6:提防包装类型的null值

import java.util.ArrayList;
import java.util.List;

public class Client {
  public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    //Exception in thread "main" java.lang.NullPointerException
    list.add(null);
    System.out.println(f(list));
  }
  public static int f(List<Integer> list) {
    int count = 0;
    for (int i : list) {
      count += i;
    }
    return count;
  }
}
结论
  • 包装类型参与运算时,要做null值校验
  • 包装类型作为函数实参传递给基本类型的形参,如果传入的参数是null,同样会报空指针异常

建议7:谨慎包装类型的大小比较

public class Client {
  public static void main(String[] args) {
    Integer i = new Integer(100);
    Integer j = new Integer(100);
    System.out.println(i == j);
    System.out.println(i > j);
    System.out.println(i < j);
  }
}
Java中的判断
  • ==:用来判断两个操作数是否相等,基本数据类型判断值是否相等,对象则判断引用的地址是否相等
  • ><:用来判断两个数字类型的大小关系,注意只能是数字类型,对于Integer等包装类型,是根据其intValue()方法的返回值(即相对应的基本类型)进行比较的,很显然,两者不可能有大小关系的。

解决办法:直接使用Integer实例的compareTo()方法即可

建议8:优先使用整型池

import java.util.Scanner;

public class Client {
  public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    System.out.println("请输入一个整数数字:");
    while (input.hasNextInt()) {
      int ii = input.nextInt();
      System.out.println("\n======" + ii + "的相等判断======");
      //通过new产生的Integer对象
      Integer i = new Integer(ii);
      Integer j = new Integer(ii);
      System.out.println("new产生的对象:" + (i == j));
      //基本类型转换位包装类型后比较
      i = ii;
      j = ii;
      System.out.println("基本类型转换的对象:" + (i == j));
      //通过静态方法生成一个实例
      i = Integer.valueOf(ii);
      j = Integer.valueOf(ii);
      System.out.println("valueOf产生的对象:" + (i == j));
    }
  }
}
输出结果:
127
====== 127 的相等判断 ======
new产生的对象:false
基本类型转换的对象:true
valueOf产生的对象:true
128
====== 128 的相等判断 ======
new产生的对象:false
基本类型转换的对象:false
valueOf产生的对象:false
555
====== 555 的相等判断 ======
new产生的对象:false
基本类型转换的对象:false
valueOf产生的对象:false

源码
public static Integer valueOf(int i) {
  if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
  return new Integer(i);
}

    通过valueOf()产生包装对象时,如果int参数在-128127之间,则直接从整型池中获得对象,不在该范围的int类型则通过new生成包装对象。

结论:通过包装类的valueOf生成包装实例可以显著提高空间和时间性能

建议9:优先选择基本类型

public class Client {
  public static void main(String[] args) {
    Client client = new Client();
    int i = 140;
    //分别传递int类型和Integer类型
    client.f(i);
    client.f(Integer.valueOf(i));
  }
  public void f(long a) {
    System.out.println("基本类型的方法被调用");
  }
  public void f(Long a) {
    System.out.println("引用类型的方法被调用");
  }
}
输出结果:
基本类型的方法被调用
基本类型的方法被调用
分析

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

结论:基本类型优先考虑

    基本类型优先考虑。

建议10:不要随便设置随机种子

import java.util.Random;

public class Client {

  public static void main(String[] args) {
    Random random = new Random(1000);
    for (int i = 0; i < 4; i++) {
      System.out.println("第" + i + "次:" + random.nextInt());
    }
  }
}
输出结果
第0次:-1244746321
第1次:1060493871
第2次:-1826063944
第3次:1976922248
分析:每次执行时产生的结果都是相同的

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

  • 种子不同,产生不同的随机数
  • 种子相同,即使实例不同也产生相同的随机数

    new Random(1000)显式地设置了随机种子为1000,运行多次,显然实例不同,但都会获得相同的三个随机数。

结论:若非必要,不要设置随机数种子
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值