文章目录

  • 简介
  • Number的范围
  • 区分位运算和算数运算
  • 注意不要使用0作为除数
  • 兼容C++的无符号整数类型
  • NAN和INFINITY
  • 不要使用float或者double作为循环的计数器
  • BigDecimal的构建
  • 类型转换问题

简介

java中可以被称为Number的有byte,short,int,long,float,double和char,我们在使用这些Nubmer的过程中,需要注意些什么内容呢?一起来看看吧。

Number的范围

每种Number类型都有它的范围,我们看下java中Number类型的范围:

考虑到我们最常用的int操作,虽然int的范围够大,但是如果我们在做一些int操作的时候还是可能超出int的范围。

超出了int范围会发送什么事情呢?看下面的例子:

    public void testIntegerOverflow(){System.out.println(Integer.MAX_VALUE+1000);}

运行结果:-2147482649。

很明显Integer.MAX_VALUE+1000将会超出Integer的最大值范围,但是我们没有得到异常提醒,反而得到了一个错误的结果。

正确的操作是如果我们遇到了Overflow的问题,需要抛出异常:ArithmeticException。

怎么防止这种IntegerOverflow的问题呢?一般来讲,我们有下面几种方式。

  • 第一种方式:在做Integer操作之前,进行预判断是否超出范围:

举个例子:

    static final int safeAdd(int left, int right) {if (right > 0 ? left > Integer.MAX_VALUE - right: left < Integer.MIN_VALUE - right) {throw new ArithmeticException("Integer overflow");}return left + right;}

上面的例子中,我们需要进行两个整数相加操作,在相加之前,我们需要进行范围的判断,从而保证计算的安全性。

  • 第二种方式:使用Math的addExact和multiplyExact方法:

Math的addExact和multiplyExact方法已经提供了Overflow的判断,我们看下addExact的实现:

    public static int addExact(int x, int y) {int r = x + y;// HD 2-12 Overflow iff both arguments have the opposite sign of the resultif (((x ^ r) & (y ^ r)) < 0) {throw new ArithmeticException("integer overflow");}return r;}

看下怎么使用:

    public int addUseMath(int a, int b){return Math.addExact(a,b);}
  • 第三种方式:向上转型

既然超出了Integer的范围,那么我们可以用范围更大的long来存储数据。

    public static long intRangeCheck(long value) {if ((value < Integer.MIN_VALUE) || (value > Integer.MAX_VALUE)) {throw new ArithmeticException("Integer overflow");}return value;}public int addUseUpcasting(int a, int b){return (int)intRangeCheck((long)a+(long)b);}

上面的例子中,我们将a+b转换成了两个long相加,从而保证不溢出范围。

然后进行一次范围比较,从而判断相加之后的结果是否仍然在整数范围内。

  • 第四种方式:使用BigInteger

我们可以使用BigInteger.valueOf(a)将int转换成为BigInteger,再进行后续操作:

    public int useBigInteger(int a, int b){return BigInteger.valueOf(a).add(BigInteger.valueOf(b)).intValue();}

区分位运算和算数运算

我们通常会对Integer进行位运算或者算数运算。虽然可以进行两种运算,但是最好不要将两种运算同时进行,这样会造成混淆。

比如下面的例子:

x += (x << 1) + 1;

上面的例子是想做什么呢?其实它是想将3x+1的值赋给x。

但是这样写出来让人很难理解,所以我们需要避免这样实现。

再看下面的一个例子:

    public void testBitwiseOperation(){int i = -10;System.out.println(i>>>2);System.out.println(i>>2);System.out.println(i/4);}

本来我们想做的是将i除以4,结果发现只有最后一个才是我们要的结果。

我们来解释一下,第一个i>>>2是逻辑右移,将会把最左边的填充成0,所以得出的结果是一个正值1073741821。

第二个i>>2是算数右移,最左边的还是会填充成1,但是会向下取整,所以得出结果是-3.

直接使用i/4,我们是向上取整,所以得出结果是-2.

注意不要使用0作为除数

我们在使用变量作为除数的时候,一定要注意先判断是否为0.

兼容C++的无符号整数类型

在java中只有16位的char表示的是无符号整数,而int实际上表示的是带符号的整数。

而在C或者C++中是可以直接表示无符号的整数的,那么,如果我们有一个32位的无符号整数,该怎么用java来处理呢?

    public int readIntWrong(DataInputStream is) throws IOException {return is.readInt();}

看上面的例子,我们从Stream中读取一个int值,如果是一个32位的无符号整数,那么读出来int就变成了有符号的负整数,这和我们的期望是相符的。

考虑一下,long是64位的,我们是不是可以使用long来表示32位的无符号整数呢?

    public long readIntRight(DataInputStream is) throws IOException{return is.readInt() & 0xFFFFFFFFL; // Mask with 32 one-bits}

看上面的例子,我们返回的是long,如果将32位的int转换成为64位的long,会自动根据符号位进行补全。

所以这时候我们需要和0xFFFFFFFFL进行mask操作,将高32位重置为0.

NAN和INFINITY

在整型运算中,除数是不能为0的,否则直接运行异常。但是在浮点数运算中,引入了NAN和INFINITY的概念,我们来看一下Double和Float中的定义。

public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
public static final double NaN = 0.0d / 0.0;
public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
public static final float NaN = 0.0f / 0.0f;

1除以0就是INFINITY,而0除以0就是NaN。

接下来,我们看一下NAN和INFINITY的比较:

public void compareInfinity(){System.out.println(Double.POSITIVE_INFINITY == Double.POSITIVE_INFINITY);}

运行结果是true。

    public void compareNaN(){System.out.println(Double.NaN == Double.NaN);}

运行结果是false。

可以看到NaN和NaN相比是false。

那么我们怎么比较NaN呢?

别急,Double提供了一个isNaN方法,我们可以这样使用:

System.out.println(Double.isNaN(Double.NaN));

接下来我们看一个在代码中经常会用到的一个Double解析:

    public void incorrectParse(String userInput){double val = 0;try {val = Double.valueOf(userInput);} catch (NumberFormatException e) {}//do something for val}

这段代码有没有问题?咋看下好像没有问题,但是,如果我们的userInput是NaN,Infinity,或者-Infinity,Double.valueOf是可以解析得到结果的。

    public void testNaN(){System.out.println(Double.valueOf("NaN"));System.out.println(Double.valueOf("Infinity"));System.out.println(Double.valueOf("-Infinity"));}

运行输出:

NaN
Infinity
-Infinity

所以,我们还需要额外去判断NaN和Infinity:

public void correctParse(String userInput){double val = 0;try {val = Double.valueOf(userInput);} catch (NumberFormatException e) {}if (Double.isInfinite(val)){// Handle infinity error}if (Double.isNaN(val)) {// Handle NaN error}//do something for val}

不要使用float或者double作为循环的计数器

考虑下面的代码:

for (float x = 0.1f; x <= 1.0f; x += 0.1f) {System.out.println(x);
}

上面的代码有什么问题呢?

我们都知道java中浮点数是不准确的,但是不一定有人知道为什么不准确。

这里给大家解释一下,计算机中所有与的数都是以二进制存储的,我们以0.6为例。

0.6转成为二进制格式是乘2取整,0.6x2=1.2,取整剩余0.2,继续上面的步骤0.2x2=0.4,0.4x2=0.8,0.8x2=1.6,取整剩余0.6,产生了一个循环。

所以0.6的二进制格式是.1001 1001 1001 1001 1001 1001 1001 … 无限循环下去。

所以,有些小数是无法用二进制精确的表示的,最终导致使用float或者double作为计数器是不准的。

BigDecimal的构建

为了解决float或者Double计算中精度缺失的问题,我们通常会使用BigDecimal。

那么在使用BigDecimal的时候,请注意一定不要从float构建BigDecimal,否则可能出现意想不到的问题。

    public void getFromFloat(){System.out.println(new BigDecimal(0.1));}

上面的代码,我们得到的结果是:0.1000000000000000055511151231257827021181583404541015625。

这是因为二进制无法完美的展示所有的小数。

所以,我们需要从String来构建BigDecimal:

    public void getFromString(){System.out.println(new BigDecimal("0.1"));}

类型转换问题

在java中各种类型的Number可以互相进行转换:

比如:

  • short to byte or char
  • char to byte or short
  • int to byte, short, or char
  • long to byte, short, char, or int
  • float to byte, short, char, int, or long
  • double to byte, short, char, int, long, or float

或者反向:

  • byte to short, int, long, float, or double
  • short to int, long, float, or double
  • char to int, long, float, or double
  • int to long, float, or double
  • long to float or double
  • float to double

从大范围的类型转向小范围的类型时,我们要考虑是否超出转换类型范围的情况:

    public void intToByte(int i){if ((i < Byte.MIN_VALUE) || (i > Byte.MAX_VALUE)) {throw new ArithmeticException("Value is out of range");}byte b = (byte) i;}

比如上面的例子中,我们将int转换成为byte,那么在转换之前,需要先判断int是否超出了byte的范围。

同时我们还需要考虑到精度的切换,看下面的例子:

    public void intToFloat(){System.out.println(subtraction(1111111111,1111111111));}public int subtraction(int i , float j){return i - (int)j;}

结果是多少呢?

答案不是0,而是-57。

为什么呢?

因为这里我们做了两次转换,第一次从1111111111转换到float,float虽然有32位,但是只有23位是存放真正的数值的,1位是符号位,剩下的8位是指数位。

所以从1111111111转换到float发送了精度丢失。

我们可以把subtraction方法修改一下,首先判断float的范围,如果超出了23bit的表示范围,则说明发送了精度丢失,我们需要抛出异常:

    public int subtraction(int i , float j){System.out.println(j);if ((j > 0x007fffff) || (j < -0x800000)) {throw new ArithmeticException("Insufficient precision");}return i - (int)j;}

当然还有一种办法,我们可以用精度更高的double来做转换,double有52位来存放真正的数据,所以足够了。

    public int subtractionWithDouble(int i , double j){System.out.println(j);return i - (int)j;}

本文的代码:

learn-java-base-9-to-20/tree/master/security

本文已收录于 http://www.flydean.com/java-security-code-line-number/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

java安全编码指南之:Number操作相关推荐

  1. java中的getnumber怎么用_java安全编码指南之:Number操作详解

    简介 java中可以被称为Number的有byte,short,int,long,float,double和char,我们在使用这些Nubmer的过程中,需要注意些什么内容呢?一起来看看吧. Numb ...

  2. java中的getnumber怎么用_java安全编码指南之:Number操作

    简介 java中可以被称为Number的有byte,short,int,long,float,double和char,我们在使用这些Nubmer的过程中,需要注意些什么内容呢?一起来看看吧. Numb ...

  3. java安全编码指南之:文件IO操作

    文章目录 简介 创建文件的时候指定合适的权限 注意检查文件操作的返回值 删除使用过后的临时文件 释放不再被使用的资源 注意Buffer的安全性 注意 Process 的标准输入输出 InputStre ...

  4. java安全编码指南之:输入校验

    文章目录 简介 在字符串标准化之后进行校验 注意不可信字符串的格式化 小心使用Runtime.exec() 正则表达式的匹配 简介 为了保证java程序的安全,任何外部用户的输入我们都认为是可能有恶意 ...

  5. java安全编码指南之:字符串和编码

    文章目录 简介 使用变长编码的不完全字符来创建字符串 char不能表示所有的Unicode 注意Locale的使用 文件读写中的编码格式 不要将非字符数据编码为字符串 简介 字符串是我们日常编码过程中 ...

  6. java安全编码指南之:拒绝Denial of Service

    文章目录 简介 为什么会有DOS 不合理的资源使用 请求用于矢量图的SVG文件和字体文件 字符串或二进制表示的图片转换 zip炸弹 billion laughs attack hashMap中插入太多 ...

  7. java安全编码指南之:异常处理

    简介:异常是java程序员无法避免的一个话题,我们会有JVM自己的异常也有应用程序的异常,对于不同的异常,我们的处理原则是不是一样的呢? 一起来看看吧. 简介 异常是java程序员无法避免的一个话题, ...

  8. java安全编码指南之:文件和共享目录的安全性

    文章目录 简介 linux下的文件基本权限 linux文件的特殊权限 Set UID 和 Set GID Sticky Bit SUID/SGID/SBIT权限设置 文件隐藏属性 特殊文件 java中 ...

  9. java安全编码指南之:ThreadPool的使用

    文章目录 简介 java自带的线程池 提交给线程池的线程要是可以被中断的 正确处理线程池中线程的异常 线程池中使用ThreadLocal一定要注意清理 简介 在java中,除了单个使用Thread之外 ...

最新文章

  1. 阿里云:国际化是云计算技术能力的照妖镜
  2. 轻量级HTTP服务器Nginx(配置与调试Nginx)
  3. 初探Object Pascal的类(三)
  4. php万年历上个月下个月,php 万年历
  5. C语言操作MYSQL小例子
  6. 河北2018年职称计算机开始,2018河北职称计算机考试操作题答案(8页)-原创力文档...
  7. linux修图,修图只知道Photoshop?11款高逼格修图工具快来get!
  8. python17个常见问题_Python 常见的17个错误分析
  9. 互联网系统性能优化方向
  10. 物联网平台之争 巨头竞合并存
  11. linux落字效果代码,Linux 用echo输出带特效的字体
  12. Markdown初使用
  13. arcgis 10 扩展模块授权无效的解决方法
  14. swat weather 输入数据注意事项
  15. struts2 超链接a标签执行action
  16. 深度学习卷积算法指南
  17. RK3399平台开发系列讲解(IIO子系统)4.43、IIO数据的获取方式介绍
  18. C语言GUI编程之数字记忆游戏——项目构建
  19. 什么是UNIX时间戳? 时间戳有什么用?
  20. 推荐一篇好文《佛教真像大家所认为的那样消极吗?》

热门文章

  1. EXE和SYS通信(ReadFile WriteFile DO_BUFFERED_IO) 缓冲区方式
  2. PostgreSQL中表名、字段名大小写问题
  3. 网页出现不河蟹弹窗?那是被劫持了!
  4. 数据结构与算法 | 循环队列
  5. 从零开始玩转JMX(三)——Model MBean
  6. 它来了,阿里架构师的“Java多线程+并发编程”知识点详解手册,限时分享
  7. 做好项目,从正确定义问题开始!
  8. 计算机基础- -认识磁盘
  9. 【今晚七点半】:多媒体开源PI
  10. LiveVideoStackCon 2020北京站-售票通道关闭倒计时2天