详谈JAVA关于数据溢出处理

八大数据类型

基本类型 位数 范围 默认值
byte(字节) 8 -128至127 0
shot(短整型) 16 -32768至32767 0
int(整形) 32 -2^31至2^31-1 0
long(长整型) 64 -2^63至2^63-1 0
boolean 1 true/false false
char 16 0至2^16-1 0
float 32
double 64

数据溢出的处理

01:
public class demo3 {
	public static void main(String args[]){
		 final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
		 final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
		 System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
		
		 final long MICROS_PER_DAY1 = 24L * 60 * 60 * 1000 * 1000;
		 final long MILLIS_PER_DAY1 = 24L * 60 * 60 * 1000;
		 System.out.println(MICROS_PER_DAY1/MILLIS_PER_DAY1);
	}
}

以上输出结果分别是:
5
1000

解释如下:
这里的运算很明显可以用long型存放下,然而为什么第一个结果还是不对呢?因为表达式的计算是先运行右边,所以数据的存放都是以默认类型int存放的,只有最后赋值的时候才会被提升为long,所以这里就存放了一个错误的结果。
解决办法如下:
就是表达式右边计算的时候就用long型运算,正如我写的 24L,就代表24是long型,以long型来存放数据,所以答案就对了

02:
public class demo5 {
	 public static void main(String[] args){
		System.out.println( Long.toHexString(0x100000000L + 0xcafebabe));
		System.out.println( Long.toHexString(0x100000000L + 0xcafebabeL));
	 } 
}

以上的输出结果是:
cafebabe
1cafebabe

解释:
看看上面的两行输出,是不是感觉都差不多呀,感觉结果应该是一样的,然而这个问题和上面是一样的,一个整形数据,一个Long类型的数据,直接相加,位数不匹配

这个是把后面的int类型扩展为long类型,然后前面自动补位8位,也就是 右操作数只和左操作数的0相加,而左操作数的1和右操作数的F相加,正确的就是把右操作数也改成long

03:
public class demo8 {
	public static void main(String[] args){
		 char x = 'X';
		 int i = 0;
		 System.out.println(true ? x : 0);
		 System.out.println(false ? i : x);
		 System.out.println(false ? 65536 : x);
		 System.out.println(false ? 65535 : x);
	}
}

解释:
是不是看到上面的表达式感觉都差不多,输出应该是XXXX,这样就错了,上面的输出如下
X
88
88
X
是不是对以上的输出很迷呀,这里给你们三条规则,然后再解释:
1.如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。换句话说,你可以通过绕过混合类型的计算来避免大麻烦。
2.如果一个操作数的类型是 T,T 表示 byte、short 或 char,而另一个操作数是一个 int 类型的常量表达式,它的值是可以用类型 T 表示的,那么条件表达式的类型就是 T。
3.否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。
(注意,这里说的是常量表达试,而不是变量表达式)

这里的只有 0 65536 65535 是常量表达式哈,也就是i是变量表达式,所以第一个输出就是用的第二则规定,返回值是char,然后第二行就是用的第三则规定,返回值是int,然后第三行第四行分别是因为char类型的范围是65535 所以65536超出范围,需要提升

04:
public class demo9 {
	public static void main(String[] args) {
		short c=0;
		int a=123465;
		System.out.println(c+=a);
	}
}

这个输出是:
-7607

解释:
是不是感觉很困惑,不知道为什么呀,感觉应该是123465,然而现实却不是这样的,说不定这个就出现在你的面试题中哦。
这里主要考察的是复合运算的类型转换,复合赋值表达式自动地将它们所执行的计算的结果转型为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行一个窄化原始类型转换。

以上的运算如下进行:123465的二进制:0001 1110 0010 0100 1001,因为是转换为short类型,只有16位,所以保留1110 0010 0100 1001,然后我们都知道运算是用补码运算,第一位是符号位,也就是(1)110 0010 0100 1001->(1)001 1101 1011 0110(反码)->(1)001 1101 1011 0111(补码)=-7607

05:
public class demo6 {
    public static void main (String[] args){
		System.out.println((int)(char)(byte) -1);
		System.out.println((int)(short)(char)(byte) -1);
	} 
}

以上输出:
65535
-1

解释:
这里的问题主要是,扩展的问题,byte和int都是有符号类型,而char是无符号类型,扩展原则如下:
1.高精度缩小,直接砍掉前面多余的位数
2.低精度扩大,若原数为无符号数,就用0补位;若原数是有符号数,就用符号扩展(负数用1,正数用0)

所以本题在int->byte的时候 数字没变还是-1 但是变成char的时候,由于是char无符号,所以是16位1 然后变成int用0补位

如果你在将一个 char 数值 c 转型为一个宽度更宽的类型,并且你不希望有符号扩展,那么为清晰表达意图,可以考虑使用一个位掩码,即使它并不是必需的:
int i = c & 0xffff;

如果你在将一个 char 数值 c 转型为一个宽度更宽的整型,并且你希望有符号扩展,那么就先将 char 转型为一个 short,它与 char 具有同样的宽度,但是它是有符号的。在给出了这种细微的代码之后,你应该也为它书写一句注释:
int i = (short) c; //转型将引起符号扩展

如果你在将一个 byte 数值 b 转型为一个 char,并且你不希望有符号扩展,那么你必须使用一个位掩码来限制它。这是一种通用做法,所以不需要任何注释:
char c = (char) (b & 0xff);

总结:

这就是数据溢出的处理了,希望对大家有帮助,如果有什么补充或者什么地方有问题,欢迎评论区一起讨论

猜你喜欢

转载自blog.csdn.net/qq_37871033/article/details/86242187