第十九章 JAVA进制之初学习
来源:互联网 发布:深入浅出python 中文 编辑:程序博客网 时间:2024/06/16 13:38
第一节 进制的基本概念
位进制是人们为了计数和运算方便而约定的计数系统,我们的计算机中一般采用的就是二进制。那什么是二进制呢?十进制是指10个数字,0-9,逢10进1;二进制则是指2个数,0和1,逢2进1,一个数在不同的位置上所代表的值会不同。十进制是我们现实生活中经常使用的,我们也比较熟悉,那二进制是如何用来表示一个数呢?用十进制的数举例来说8=1000;3=0011;2=0010;9=1001;6=0110。整数8的二进制=1000,从右往左数,前三位是0,第四位是1,也就是2的3次方,2^3 * 1+2^2 * 0+2^1 * 0+2^0 * 0=8。
十进制转换成二进制的方法,我们一般把需要转换的数作为一个除数,除以2,得到的余数不是1就是0,把取到的余数取出倒着读就是该数的二进制。计算机中广泛使用二进制,可以使其运算简单,简化了计算机的结构。当然计算机中还有其它的进制,如八进制(0-7,逢八进一,标志的开头用0表示),十六进制(0-9,A,B,C,D,E,F,标志的开头用0x表示 ),在这里可以看到8和16分别是2^3和2^4,因为都是2的整数次方,所以会常用一些。
十进制转换二进制,使用需要转换的数除以2取余,倒着输出余数就是该数的二进制,八进制同理,也是用数除以8,然后得到余数从下往上数即可,十进制转换八进制,十六进制都是一样的。二进制,八进制,16进制转换10进制,也是按权展开,相加即得十进制数。
第二节 二进制的位运算
>>
>>>
程序中所有的数都是以二进制存储的,位运算就是直接对二进制进行的位操作。
1.按位与&运算,两位全为1,结果才为1。0&0=0;0&1=0;1&0=0;1&1=1。与位算的特殊用法:
- 清零,如果想将一个单元清零,即使其全部二进制为1,只需要与一个各位都是0的数值相与,结果为零;
- 取一个数中的指定位。举例说取某数的低四位,只需要将这后四位与1111相与,是1则还是1,是0则还是0。10101110&00001111=00001110
2.按位或|运算,两位只要有一数为1,结果就为1。0|0=0;0|1=1;1|0=1;1|1=1 或运算的特殊用法:
常常用来对数的某些位,置1,设置为1。取一个X=10100000 x|00001111=10101111。
3.异或^运算,两个相应位为异,值不同,则该结果为1,否则为0,0^0=0; 0^1=1; 1^0=1; 1^1=0;异或运算的特殊用法:
- 使特定位反转,对应某数要反转的位置,使其与1进行异或运算,如原数为0,则0^1=1;原数为1,1^1=0;
- 与0相异或,保留原值。
- 与本身异或值为0。
4.取反运算~,对一个二进制数按位取反,即0变成1,1变成0。
5.左移运算<<,将一个运算对象的各二进制位全部向左移若干位(左边的二进制位丢弃,右边补0)。若左移时舍弃的最高位不包含1,则每左移一位相当于该数*2。举例来说:2的二进制码为10,这里10的前面实际是有若干个0,32位:00000000 00000000 00000000 00000010 左移1位变为100。2<<1=4。
6.右移运算>>,将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。操作数每右移1位,相当于该数除以2。注意:左边补0还是补1,需要看这个被移数是正数还是负数。
7.无符号右移运算>>>,各个位向右移指定的位数,右移后左边的位用0填充,移出右边的位则被丢弃。
在二进制中,负数以其正值的补码形式来表示。要找出一个负数的二进制,只需要找到这个正数的补码。
原码:一个整数按照绝对值大小,转换成的二进制数,我们称为原码。
反码:将二进制数按位取反,所得的新的二进制数我们称这是原二进制的反码。
注意:这两个码是互为反码的。
举例来说:00000000 00000000 00000000 00001110(32位)与11111111 11111111 11111111 11110001互为反码。当我们得到反码之后,反码+1得到的结果就是我们的补码。即11111111 11111111 11111111 11110010。同理知道补码后,用补码-1,再取反就是我们的原码,然后转换成二进制数就是我们表示的值。
第三节 JDK中内置的进制转换
十进制转换二进制:Integer.toBinaryString(int i)
十进制转换八进制:Integer.toOctalString(int i)
十进制转换十六进制:Integer.toHexString(int i)
二进制转换十进制:Integer.valueOf(“111001”,2).toString()
八进制转换十进制:Integer.valueOf(“27”,8).toString()
十六进制转换十进制:Integer.valueOf(“A8”,16).toString()
二进制转换十进制:Integer.parseInt(“111001”,2)
八进制转换十进制:Integer.parseInt(“27”,8)
十六进制转换十进制:Integer.parseInt(“A8”,16)
转十进制有两种转换方式,区别是返回值类型不同,Integer.parseInt返回的是int类型,Integer.valueOf返回的是Integer类型,其后面toString可以不用,用了代表以字符串形式输出。
public class RadixMain { public static void main(String[] args) { //十进制转换成其他进制 System.out.println(Integer.toHexString(112));//十六进制 System.out.println(Integer.toOctalString(112));//八进制 System.out.println(Integer.toBinaryString(112));//二进制 //其它进制转换成十进制 System.out.println(Integer.parseInt("111001",2));//二进制 System.out.println(Integer.parseInt("27",8));//八进制 System.out.println(Integer.parseInt("A8",16));//十六进制 System.out.println(Integer.valueOf("A8",16).toString());//十六进制 System.out.println(Integer.valueOf("27",8).toString());//八进制 System.out.println(Integer.valueOf("111001",2).toString());//二进制 }}
第四节 JAVA中的进制
在JAVA中二进制,平时我们的开发中,进制转换或者位操作用的不多,java处理的是高层,但在跨平台时,进制会用的比较多,如文件读写以及数据通信中。例如,客户机和服务器,客户端是用java开发的程序,服务器端也是用java开发的程序,客户端向服务器端发送对象数据,我们就可以把要发送的数据序列化,得到序列化的数据后就可以反序列化。随着客户访问的增大, 我们不得不考虑服务的性能,其中一个可用的方案就是把java语言改成C语言,C语言作为底层语言比java语言快,而此时客户端传送的还是序列化的对象,服务器端就无法解析,怎么办呢?这时我们就可以改成用0、1进制,客户端要传送的数据用二进制来表示,这样的话服务器端就可以解析这些数据。那客户机和服务器的数据是如何用二进制来表示呢?
JAVA中的基本数据类型有以下四种:
- 整型int数据包括byte(8bit一个字节,-128—127);short(16bit两个字节);int(32bit,4个字节);long(64bit,8个字节)。
- 浮点型数据包括单精度float(32bit),双精度double(64bit)。
- boolean类型 true,false 1bit。
- char字符型 unicode字符 16位,2个字节。
对应的包装类类型:Byte,Short,Integer,Long,Float,Double,Boolean,Character。
数据类型转换成字节,我们用一个实际的例子来表示。
Employee//故宫 int id;//(8143) String name;//(张三丰) String describe;//(我每天练功,我天下无敌.....)
下面我们来看下,8143这个数我们是如何把它转换成字节的。8143二进制为(00000000 00000000 00011111 11001111),32位的,把它转换成字节应该是4个字节,这里我们给出结果=byte[] b=[-49,31,0,0]。
第一个低端字节:8143>>0*8 & 0xFF=11001111=207 ,如果是负数即-49(11001111-1=11001110,反码00110001,转换为49,负数即-49);
注意:这里就是从右边数的第一个字节11001111和11111111取与,两位全为1结果才为1,所以结果还是11001111。这个运算操作相当于是与运算取出指定位的操作,后八位与11111111相与结果还是本身。后面四个操作分别就是取出指定字节的二进制数。(与运算与0相与取值是0,两者都为1,取值为1)
第二个低端字节:8143>>1*8 & 0xFF=00011111=31;
右移一个字节,等于最后一个字节相当于第二个低端字节,然后相与11111111,得到的结果还是第二个低端字节自身。
第三个低端字节:8143>>2*8 & 0xFF=00000000=0;
第四个低端字节:8143>>3*8 & 0xFF=00000000=0。
在这里的低端高端是什么呢?实际应该是大端法和小端法。
小端法(Little-Endian) 低位字节排放在内存的低地址端即该值的起始地址,高位字节排放在内存的高地址端。
大端法(Big-Endian)高位排放在内存的低地址端即该值的起始地址,低位字节排放在内存的高地址端。
刚才的8143,就是低位字节排放在内存的低地址端,把它的低位字节-49放在了内存的低地址上,所以说它其实就是小端法。
这里我们来看另一个例子,32位宽的数,我们用16位来表示0x12,0x34,0x56,0x78
在小端法Little-Endian模式CPU中的存放方式(假设从地址0x4000开始存放)为:
而在Big-Endian模式CPU中的存放方式则为:
下面我们来看一下字符串转化为字节
字符串转化字节数组
String s;byte[] bs=s.getBytes();
byte[] bs=new byte[int];String s=new String(bs);//还有一种方法或String s=new String(bs,encode);//encode指编码方式“gb2312,utf-8”
下面我们通过实际的例子来熟悉一下如何用代码实现数据类型向字节数组的转化
public class convert { /** * 建立一个方法可以输出一个数的字节数组 * static静态方法,类名就可以直接调用,不需要对象的实例 * 返回值类型需要是一个数组,需要有一个参数传入进去这个数 */ public static byte[] int2Bytes(int num){ //因为要输出一个数组,所以要现有一个数组去承装它,已知输出应该是4位。 byte[] arr=new byte[4]; for(int i=0;i<4;i++){ arr[i]=(byte)(((num>>(i*8))&0xff)); // 这里int类型的数位右移操作,得到的还是int类型, // 与11111111(0xff)与运算后输出还是整数,转换成byte类型是强制转换。 // 这里为什么可以强制转换呢?因为截取低端位后,这8位的取值范围是-128-127 // 8位第一位是代表正负数,由此进行强制转换也就没有问题了。 } return arr; } /** * 定义一个方法提供字节数组作为参数来返回原来的值 */ public static int bytes2Int(byte[] arr){ //低端法第一个值就是它的最低位。 int result=0; for(int i=0;i<arr.length;i++){ result+=(int)((arr[i]&0xff)<<(i*8)); // 在这第二个循环里,我们看到,首先把字节数组里的数字 // 与0xff相与运算,得其自身,之后左移八位,相加就是原来的整数 // 测试了下这里int强制转换添加不添加都是可以计算的。 } return result; } /** * long型转换成byte */ public static byte[] long2Bytes(long num){ byte[] arr=new byte[8]; //这里的数组应该是8位 for(int i=0;i<arr.length;i++){ arr[i]=(byte)(((num>>(i*8))&0xff)); } return arr; } /** * byte型转换成long型 */ public static long bytes2Long(byte[] arr){ long result=0L; for(int i=0;i<arr.length;i++){ result+= ((long)(arr[i]&0xff))<<(i*8); //后面八个是for循环单独的写法,性质是一样的。 } long s1=(long)(((long)(arr[0]&0xff))<<(0*8)); long s2=(long)(((long)(arr[1]&0xff))<<(1*8)); long s3=(long)(((long)(arr[2]&0xff))<<(2*8)); long s4=(long)(((long)(arr[3]&0xff))<<(3*8)); long s5=(long)(((long)(arr[4]&0xff))<<(4*8)); long s6=(long)(((long)(arr[5]&0xff))<<(5*8)); long s7=(long)(((long)(arr[6]&0xff))<<(6*8)); long s8=(long)(((long)(arr[7]&0xff))<<(7*8)); long s=s1|s2|s3|s4|s5|s6|s7|s8; return result; } public static void main(String[] args) { byte[] arr=convert.int2Bytes(8143); for(byte b:arr) { System.out.print(b+" "); } System.out.println(convert.bytes2Int(arr)); //字符串和字节数组的转换 String describe="我每天都练功,我天下无敌..."; byte[] barr=describe.getBytes(); String des=new String(barr); for(byte b1:barr){ System.out.print(b1+" "); } System.out.println(des); byte[] arr3=convert.long2Bytes(999999999999L); for(byte c:arr3){ System.out.print(c+" "); } System.out.println(convert.bytes2Long(arr3)); }}
在这里遇到了哪些问题呢?
arr[i]=(byte)(((num>>(i*
8))&0xff)); 在这里的强制转换为什么不怕byte是小类型,不怕出现问题吗?这里实际是因为截取了低端位,8位字节取值范围就是-128-127,所以可以直接转换,自己也做了试验,当直接右移之后得到的数还是int型,取于之后一样还是整型。在后面byte转换long型时也出现了问题,众所周知,long是64位,在逆推将数组转换long型时,第一步是与0xff取与,然后向左位移i*
8位。此时如果不指定类型,与0xff取余后的数就是int,左移的位数如果超过32位,就会出现问题。所以在这里,如果使用原来的代码不指定转换后的类型,int以内的数还可以计算,超出的会出现错误。修改后就没有问题了result+= ((long)(arr[i]&0xff))<<(i*8);
还有就是关于int强转byte时,因为byte只能表示-128~127之间的数,如果该值大于127时,会直接截取后低八位来作为byte的结果。可能会出现负数还有其他各种不对的值。
第五节 JAVA中位运算总结
Java移位运算符不外乎就这三种:<<(左移)、>>(带符号右移)和>>>(无符号右移)。
1.左移运算符,左移规则丢弃最高位,0补最低位,如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模。如对int型移动33位,实际上只移动了33%32=1位。当左移的运算数是byte 和short类型时,将自动把这些类型扩大为 int 型。在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。当移位该值符号位那么该值将变为负值。如果移进后是高阶位(31或63位)为1,那么该值将变为负值。32位和64位的下标最大位数就是31位和64位。
注:n位二进制,最高位为符号位,因此表示的数值范围-2^(n-1) ——2^(n-1) -1,所以模为2^(n-1)。
2.右移运算规则正数左补0,负数左补1,右边丢弃,实际就是符号位不要变,左边补上符号位。如果要移走的值为负数,每一次右移都在左边补1,如果要移走的值为正数,每一次右移都在左边补0,这叫做符号位扩展(保留符号位)(sign extension )。当右移的运算数是byte 和short类型时,将自动把这些类型扩大为 int 型。右移一位相当于除2,右移n位相当于除以2的n次方。
3.无符号右移运算符>>>只需要记住一点:忽略了符号位扩展,0补最高位,只对32位和64位的值有意义。
- 第十九章 JAVA进制之初学习
- java之初学习
- 通译之第十九章:国际化
- 第十九、Java面向对象之static
- 《Java 编程思想》--第十九章:枚举类型
- 《java编程思想》第十九章 枚举
- 第十九章
- java web之jsp初学习
- 初学JAVA之二
- 初学JAVA之三
- 初学JAVA之四
- java初学之static
- JAVA初学之容器
- 初学Java之Button
- 初学Java之LayoutManager
- 初学Java之九九乘法
- java初学之arrayList
- java初学之界面设计
- 内存池作用
- Mac下 vim快捷键
- Java package包,导入包import
- //TODO //XXX //FIXME注释说明
- List传值覆盖问题
- 第十九章 JAVA进制之初学习
- 2017.04.14:python数据可视化02
- nyoj 114 某种序列
- c++书第二章上的例子以及我在运行时发现的问题
- 我的日期工具类
- adb remount
- 不使用临时变量交换两个变量的值
- View 的位置参数
- 四种交换两个变量的值的方法