在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?
自问自答:在 跨平台 的时候,就凸显神功了。比如说 文件读写 , 数据通信 ,还有Java编译后的 字节码文件 。下面会有个数据通信的例子哦。
Java对对象实现 Serializablle 接口,就可以将其转化为一系列 字节 ,而在通信中,不必要关系数据如何在不同机器表示和字节的顺序。这里泥瓦匠对 Serializablle 接口,不做详细讲解,以后单独详解。
Java进制转换
首先认识下Java中的 数据类型 :
1、Int整型:byte(8位,-128~127)、short(16位)、int(32位)、long(64位)
2、Float型:float(32位)、double(64位)
2、char字符:unicode字符(16位)
也就是说, 一个int等价于长度为4的字节数组。
Java中进制如何转换呢?
在Java中,Int整形以及char字符型 被包装的类 中提供了一系列的 操作方法 。比如 java.lang.Integer 中 , api如图所示 :
下面泥瓦匠写个demo,验证下。
package javaBasic.oi.byteoper;
public class IntegerOper
{
public static void main(String[] args)
{
System.out.println("17的十六进制: " + Integer.toHexString(17));
System.out.println("17的八进制: " + Integer.toOctalString(17));
System.out.println("17的二进制: " + Integer.toBinaryString(17));
System.out.println(Integer.valueOf("11", 16));
System.out.println(Integer.valueOf("21", 8));
System.out.println(Integer.valueOf("00010001", 2));
}
}
右键Run一下,我们可以在控制台中看到如下输出:
的十六进制: 11
的八进制: 21
的二进制: 10001
补充: 如果值太大,则需要调用 java.lang.Long 提供的方法。
Java基本类型和字节神奇转换
这里泥瓦匠想到了自己是个学生,典型的 OO 思想。那学号:1206010035是整型,怎么转成字节呢,上面说的拥有字节码的对象能通信。所以,学校关于学号这个都是这样的方式通信的。因此,要将学号转成字节码才行。
泥瓦匠就写了个工具类 IntegerConvert.java:
package javaBasic.oi.byteoper;
public class IntegerConvert
{
/**
* Int转字节数组
*/
public static byte[] int2Bytes(int inta)
{
// 32位Int可存于长度为4的字节数组
byte[] bytes = new byte[4];
for (int i = 0; i < bytes.length; i++)
bytes[i] = (byte)(int)((inta >> i * 8) & 0xff);// 移位和清零
return bytes;
}
/**
* 字节数组转Int
*/
public static int bytes2Int(byte[] bytes)
{
int inta = 0;
for (int i = 0; i < bytes.length; i++)
inta += (int)((bytes[i] & 0xff) << i * 8);// 移位和清零
return inta;
}
public static void main(String[] args)
{
// 将我的学号转换成字节码
byte[] bytes = IntegerConvert.int2Bytes(1206010035);
System.out.println(bytes[0] + " " + bytes[1] + " " + bytes[2] + " " + bytes[3]);
// 字节码就可以转换回学号
System.out.println(IntegerConvert.bytes2Int(bytes));
}
}
跑一下,右键Run,可以看到以下输出:
-77 64 -30 71
010035
代码详细解释如下:
1、(inta >> i * 8) & 0xff
移位 清零从左往右,按8位获取1字节 。
2、这里使用的是 小端法 。地位字节放在内存低地址端,即该值的起始地址。补充:32位中分大端模式(PPC)和小段端模式(x86)。
自然,Long也有其转换方法,如下:
public class LongConvert
{
/**
* long 转 byte数组
*/
public static byte[] long2Bytes(long longa)
{
byte[] bytes = new byte[8];
for (int i = 0; i < bytes.length; i++)
bytes[i] = (byte)(long)(((longa) >> i * 8) & 0xff); // 移位和清零
return bytes;
}
/**
* byte数组 转 long
*/
public static long bytes2Long(byte[] bytes)
{
long longa = 0;
for (int i = 0; i < bytes.length; i++)
longa += (long)((bytes[i] & 0xff) << i * 8); // 移位和清零
return longa;
}
}
那字符串,字符数组呢?比如泥瓦匠的名字:李强强
Java也提供了一系列的方法,其实 java.lang.String 封装了 char[] ,其中本质还是 对char数组的操作 。代码如下:
package javaBasic.oi.byteoper;
public class StringConvert
{
public static void main(String[] args)
{
String str = "李强强";
byte[] bytes = str.getBytes();
// 打印字节数组
System.out.println("'李强强'的字节数组为:");
for (int i = 0; i < bytes.length; i++)
System.out.print("\t" + bytes[i]);
}
}
右键Run一下,可以看到以下输出:
'李强强'的字节数组为:
-64 -18 -57 -65 -57 -65
论证 :这里我们论证了一个中文,需要 两个字节 表示,也就是说 一个中文是16位 。
浅谈Java通信中的数据
下面简单把泥瓦匠学生的故事延续。
如图,库表中一个学生对象,有个属性是学号。这时候客户端要向服务端发送这个对象。过程如下:
1、对象实现Serializable接口。
实现了 Serializable接口 的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。
2、其学号属性值 1206010035,由客户端转换为字节码。
3、字节码传输至服务端
4、服务端接收并转换为对象属性值。
java中的I/O流小结
java.io包中约有75个类和接口。 java.io包设计的目的是处理数据和对象的IO操作。程序员需要使用java.io包把数据写到磁盘文件,套接子,URL,以及系统控制台上,并从中读取输入数据。java.io包也支持字符串数据的格式化处理,以及zip和jar文本处理。
java中的I/O操作大量采用包装处理,因此需要执行两到三个类才能实现简单的I/O处理。
所谓包装(wrapper)就是通过其他对象才能访问自己特性的对象。包装对对象将会增强或改善被包装对象的可用性。Java I/O库广泛采用此技术。包装是一种设计模式,这种设计模式也成为“装饰”(decorator)设计模式。java中的流采用包装主要是提高读取或者输出数据的效率。
java中有两种流,字节流和字符流。每种流又分为输入流和输出流。那么在进行I/O操作时候如何选择使用字节流还是字符流呢?
以输出为例,主要从一下三个方面解决
1.确定输出内容是否包含任何字符。如果包括字符,是输出16位编码的字符还是输出8位编码的字符?
如果在美国或欧洲打印一个报表,也许需要选用8位编码的字符输出。如果有一个使用了16位Unicode字符的数据库,则应选用16位编码的字符输出。注意在做出选择时不应该破坏数据。
1)选用8位编码的字符输出时,应用OutputStream(默认情况)
2)选用16位编码的字符输出时,应用Writer类。
2. 确定数据输出的目的
JAVA程序访问外部数据是通过数据流实现的。大多数物理输出目的都有一个专门的底层类可用
基于输出目的和数据宽度确定底层类 输出目的 输出8位字符的OutputStream类或方法 输出16位字符的Writer类
文件 java.io.FileOutputStream java.io.FileWriter
套接子 java.net.Socket.getOutputStream() 套接子从不使用16位编码的数据流
URL(GET/POST) java.net.URLConnection.getOutputStream() URL建立在套接子基础上,所以无
管道 java.io.PipedOutputStream java.io.PipedWriter
内存中的数组 java.io.ByteArrayOutputStream java.io.CharArrayWriter
3. 确定输出数据类型,二进制数据?还是可打印的数据?
根据二进制数据,ASCII字符和字符串输出之间的不同确定顶层类 类型 格式 十六进制数值 用到的类
二进制数据 4字节的二进制整数 java.io.DataOutputStream
ASCII字符 连续的字节
java.io.PrintOutputStream
字符串 连续的双字节字符 java.io.PrintWriter
我们把这一步选用的类成为顶层类。
举个例子
假定有一个数组,其中包含1000个整数值,我们想以二进制数据形式把它们写到一个文件中代码如下
FileOutputStream fos = new FileOutputStream("ints.dat");
DataOutputStrea dos = new DataOutputStream(fos);
for (int i=0;i<1000;i++){
dow.writeInt(myArray[i])
}
dos.close();