//对于原码, 反码, 补码而言, 需要注意以下几点:
//(1) 二进制的最高位是符号位, 0表示正数, 1表示负数;
//(2) 正数的原码, 反码, 补码都一样;
//(3) 负数的原码=对应正数的二进制原码,最高位设为1;
//(4) 负数的反码=它的原码符号位不变, 其他位取反;
//(5) 负数的补码=它的反码+1;
//(6) 0的反码, 补码都是0;
//(7) 在计算机运算的时候, 都是以补码的方式来运算的.
//原码的得来:(负数的原码,直接把对应正数的最高位改为1),原码能够直观的表示一个负数(能直观的把真值显示出来,-1的原码10000001)。
//补码的设计目的是:使符号位能与有效值部分一起参加运算,从而简化运算规则。使减法运算转换为加法运算。
//java中byte short int long都是有符号整数,char是无符号整数
//有符号整数的最高位为符号位:0表示整数、1表示负数。
//当把二进制转换为十进制时,正整数直接计算结果。
//负数的十进制结果为:负数的二进制补码(带符号位一起)取反(^) + 1得到无符号二进制表示,计算无符号二进制的十进制结果,然后在结果前加负号(-)。
//十进制负数转化为二进制表示:去除负号,将对应的十进制正数转为无符号二进制数表示,对二进制码取反(^) + 1得到有符号二进制表示。
//byte的表示范围为:-128 to 127,其中负数范围-128 to -1, 整数范围0 to 127,各代表2的n-1次方个数。
//-128的二进制补码为10000000,^(10000000) + 1 = 10000000(无符号二进制),十进制结果为128加负号 = -128。
//-5转换二进制补码:00000101(对应正数的二进制),^(00000101) + 1 = 11111011(负数补码)。
//java中的减法转化为加法:5 - 3 实际执行的是 5 + (-3),00000101 + 11111101 = 00000010 (高位溢出) = 2
//(byte)0x90 -> (byte)00000000000000000000000010010000 -> 10010000(byte负数) -> -(10010000^ + 1) = -(01110000) = -112
System.out.println((byte)0x90); //-112
//Java 使用2 的补码的算术运算,它是非对称的。对于每一种有符号的整数类型(int、long、byte 和short),
//负的数值总是比正的数值多一个,这个多出来的值总是这种类型所能表示的最小数值。
//-(-128) -> -(10000000) -> 10000000^ + 1 -> 10000000 = -128
//-128 - 1 = 10000000 + (-1) = 10000000 + 11111111 = 01111111(高位溢出) = 127
//127 + 1 = 01111111 + 1 = 10000000 = -128
i = Integer.MIN_VALUE;
System.out.println(i != 0 && i == -i); //true
//复合赋值操作符包括*=、/=、%=、+=、-=、<<=、>>=、>>>=、&=、^=和|=
//在运算时自动提升到int,赋值时它们可能会自动地执行窄化原始类型转换。
//死循环:(short)-1 = 0xffff -> 执行符号扩展0xffffffff -> 无符号右移 1位,高位0填充 -> 0x7fffffff -> (short)0x7fffffff -> (short)0xffff = (short)-1
short i = -1;
while (i != 0) {
i >>>= 1;
}
//会导致精度丢失的三种拓宽原始类型转换:从int 到float、从long 到float、从long 到double。
//丢失原因:毗邻的浮点数值之间的距离被称为一个ulp,它是“最小单位(unit in the last place)”的首字母缩写词。
//当float和double足够大时,将一个小于最小单位ulp的浮点数加到一个很大的浮点数上时,将不会改变大的浮点数的值。
System.out.println((float)2000000000 == (float)2000000050); //true
//byte short char 类型在使用int常量赋值时,如果常量值超出类型表示范围需要强制类型转换。否则编译错误。
//编译错误: int常量128超过byte的表示范围,丢失精度出错。 byte bValue = 128;
//编译错误:int常量2147483648超过int的表示范围,越界出错。 long lValue = 2147483648;
byte bValue = 127;
long lValue = 2147483648L;
//所有的int 变量都是小于或等于Integer.MAX_VALUE 的。因为它被定义为所有int 数值中的最大值。
//int 不能表示所有的整数。无论你在何时使用了一个整数类型,都要意识到其边界条件。
//如果其数值下溢或是上溢了,通常最好是使用一个取值范围更大的类型。(整数类型包括byte、char、short、int 和long。)
//当i 达到Integer.MAX_VALUE,并且再次被执行增量操作时,它就又绕回到了Integer.MIN_VALUE。
//无限循环
for (int i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++)
//正确的做法:
for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++)
//通常最好是在条件表达式中使用类型相同的第二和第三操作数。
//确定条件表达式结果类型的规则:
//如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。
//如果一个操作数的类型是T,T 表示byte、short 或char,而另一个操作数是一个int 类型的常量表达式,
//它的值是可以用类型T 表示的,那么条件表达式的类型就是T。
//否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。
char x = 'X';
int i = 0;
System.out.println(true ? 88 : x); //X
System.out.println(true ? x : 65535); //X
System.out.println(true ? x : 65536); //88
System.out.println(false ? i : x); //88
System.out.println(true ? x : i); //88
//复合赋值表达式自动地将它们所执行的计算的结果转型为其左侧变量的类型。
//请不要将复合赋值操作符作用于byte、short 或char 类型的变量上。
//如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行 一个窄化原始类型转换。
//复合赋值 E1 op= E2 等价于简单赋值E1 = (T)((E1)op(E2)),其中T 是E1 的类型。
int i1 = 123456;
short s = 0;
s += i1;
System.out.println(s); //-7616
//混合类型的计算可能会产生混淆,尤其是十六进制和八进制字面常量无需显式的减号符号就可以表示负的数值。
//为了避免这种窘境,通常最好是避免混合类型的计算。
//负的十进制常数可以很明确地用一个减号符号来标识。但是十六进制和八进制字面常量并不是这么回事,它们可以具有正的以及负的数值。
//如果十六进制和八进制字面常量的最高位被置位了,那么它们就是负数。
System.out.println(Long.toHexString(0x100000000L + 0xcafebabe)); //cafebabe
System.out.println(0x100000000L + 0xcafebabe); //3405691582
System.out.println(Integer.toHexString((int)(0x100000000L + 0xcafebabe))); //cafebabe
System.out.println((int)(0x100000000L + 0xcafebabe)); //-889275714
//在long 型字面常量中,一定要用大写的L,千万不要用小写的l。
//要避免使用单独的一个l 字母作为变量名。
//数字1 的水平笔划和垂直笔划之间是一个锐角,而与此相对照的是,小写字母l的是一个直角。
//正确的做法:System.out.println(12345+5432L);
System.out.println(12345+5432l); //17777
//当你在操作很大的数字时,千万要提防溢出——它可是一个缄 默杀手。
//即使用来保存结果的变量已显得足够大,也并不意味着要产生结果的计
//算具有正确的类型。当你拿不准时,就使用long 运算来执行整个计算。
//在运算时使用的是int,在将int结果赋值给long之前,溢出部分被截取,导致结果错误。
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); //5
//正确的做法: final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
//在需要精确答案的地方,要避免使用float 和double。
//并不是所有的小数都可以用二进制浮点数来精确表示的。
//当一个小数不能用二进制表示时,它被表示成为最接近它的double 值。
//对于货币计算,要放大使用int、long 或BigDecimal。
//一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double)。
//后一个构造器将用它的参数的“精确”值来创建一个实例。
System.out.println(2.00 - 1.10); //0.8999999999999999
//无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号。
//错误的方法,在i为负数时总是返回false
public static boolean isOdd(int i){
return i % 2 == 1;
}
//正确的方法
public static boolean isOddCorrect(int i){
return i % 2 != 0;
}
//全等关系必须是是自反的、传递的和对称的。
//当比较两个原始类型数值时,操作符 == 首先进行二进制数据类型提升(binary numeric promotion)。
//这会导致这两个数值中有一个会进行拓宽原始类型转换(widening primitive conversion)。
//大部分拓宽原始类型转换是不会有问题的,但有三个值得注意的异常情况:
//将int或long 值转换成float 值,或long 值转换成double 值时,均会导致精度丢失。
//这种精度丢失可以证明 == 操作符的不可传递性。
long x = Long.MAX_VALUE;
double y = (double) Long.MAX_VALUE;
long z = Long.MAX_VALUE - 1;
System.out.print((x == y) + " " + (y == z) + " ");
System.out.println(x == z); //true true false
//在两种情况下,插入一对看上去没有影响的括号可能会令合法的Java 程序变得不合法。
//这种奇怪的情况是由于数值的二进制补码的不对称性引起的。
//Java 不支持负的十进制字面常量,int 和long 类型的负数常量都是由正数十进制字面常量前加一元负操作符(-)构成。
//符号-2147483648 构成了一个合 法的Java表达式,它由一元负操作符加上一个int型字面常量2147483648组成。
//通过添加一对括号来注解(很不重要的)赋值顺序,即写成-(2147483648), 就会破坏这条规则。
int i = -2147483648;
//超出范围,编译错误
//int i = -(2147483648);
//long j = -(9223372036854774808L);
//千万不要在一个整型字面常量的前面加上一个0;这会使它变成一个八进制字面常量。
//有意识地使用八进制整型字面常量的情况相当少见,你应该对所有的这种特殊用法增加注释。
System.out.println(012); //10
//Math.abs返回return (a < 0) ? -a : a;
//在参数为Integer.MIN_VALUE和Long.MIN_VALUE时,那么它将返回它的参数。
//因为-Integer.MIN_VALUE = Integer.MIN_VALUE
System.out.println(Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE); //true
//不要使用基于减法的比较器,除非你能够确保要比较的数值之间的差永远不会大于Integer.MAX_VALUE。
//要意识到int 的溢出。
Random rnd = new Random();
Integer[ ] arr = new Integer[100];
for (int i = 0; i < arr.length; i++)
arr[i] = rnd.nextInt();
//错误的做法:会有1/4的运算结果溢出,导致排序混乱
/*
Comparator<Integer> cmp = new Comparator<Integer>() {
@Override public int compare(Integer i1, Integer i2) {
return i2 - i1;
}
};*/
//正确的做法
Comparator<Integer> cmp = new Comparator<Integer>() {
@Override public int compare(Integer i1, Integer i2) {
int val1 = i1.intValue();
int val2 = i2.intValue();
return (val1 < val2 ? -1 : (val1 == val2 ? 0 : 1));
//或者不使用等价比较符 return (i1 < i2 ? -1 : (i1 > i2) 1 : 0);
}
};
Arrays.sort(arr, cmp);
//在5.0 版中,自动包装(autoboxing)和自动反包装(auto-unboxing)被添加到了Java 语言中。
//(被包装的数字类型有:Byte、Character、Short、Integer、Long、Float 和Double。)
//Java 的判等操作符(==和!=)在作用于对象引用时,执行的是引用ID 的比较,而不是值的比较。这是为了兼容5.0以前版本。
//判等操作符在其两个操作数中只有一个是被包装的数字类型,而另一个是原始类型时,执行的确实是数值比较。这种比较在5.0以前是非法的,所以不会出现不兼容。
//数值比较操作符(>、 >=、 <、 <=):
//当两个操作数都是被包装的数字类型时,数值比较操作符和判等操作符的行为存在着根本的差异:
//数值比较操作符执行的是值比较,而判等操作符执行的是引用标识的比较。
Integer obj1 = new Integer(0);
Integer obj2 = new Integer(0);
System.out.println(obj1 <= obj2 && obj1 >= obj2 && obj1 != obj2); //true
标签:java,运算,二进制,System,long,int,println,Integer,out From: https://blog.51cto.com/u_16131764/6370063