christmas.jpg

一. Java 的字节

byte (字节) 是 Java 中的基本数据类型,一个 byte 包含 8 个 bit( 位),byte 的取值范围是 -128 到 +127。

byte 跟 Java 其他基本类型的关系:

基本类型 所占字节数 备注
byte 1
short 2
int 4
long 8
char 2
float 4
double 8
boolean 1、4 《Java 虚拟机规范》给出了 4 个字节,和 boolean 数组 1 个字节的定义,具体还要看虚拟机实现是否按照规范来

二. 常用封装

由于工作关系,我封装了一个操作字节的库

github 地址:github.com/fengzhizi71…

2.1 bytekit 的特点:

  • 支持多种方式创建 Bytes
  • 支持字节数组、ByteBuffer 的操作
  • 支持 Immutable 对象:ByteArrayBytes、ByteBufferBytes
  • 支持 Transformer: 内置 copy、contact、reverse、xor、and、or、not,也支持自定义 Transformer
  • 支持 Hash: 内置 md5、sha1、sha256
  • 支持转换成 16 进制字符串
  • 支持 mmap 常用读写操作:readByte/writeByte、readBytes/writeBytes、readInt/writeInt、readLong/writeLong、readDouble/writeDouble、readObject/writeObject
  • 支持对象的序列化、反序列化、深拷贝
  • 不依赖任何第三方库

bytes.png

Bytes 是一个接口,它有三个实现类:ByteArrayBytes、ByteBufferBytes、MmapBytes。其中,前面两个实现类是 Immutable 对象。

2.2 支持 Immutable 对象

Immutable 对象 (不可变对象),即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变。

它的优点:

  • 构造、测试和使用都很简单
  • 线程安全
  • 当用作类的属性时不需要保护性拷贝
  • 可以很好的用作 Map 键值和 Set 元素

2.3 支持 Hash 加密

对 Bytes 中的 byte[] 进行加密。在 Bytes 接口中,包含下面的默认函数:

    /**
     * 使用 md5 加密
     * @return
     */
    default Bytes md5() {
    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> MessageDigestTransformer(<span class="hljs-string">"MD5"</span>));
}

<span class="hljs-comment">/**
 * 使用sha1加密
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function"><span class="hljs-keyword">default</span> Bytes <span class="hljs-title">sha1</span><span class="hljs-params">()</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> MessageDigestTransformer(<span class="hljs-string">"SHA-1"</span>));
}

<span class="hljs-comment">/**
 * 使用sha256加密
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function"><span class="hljs-keyword">default</span> Bytes <span class="hljs-title">sha256</span><span class="hljs-params">()</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> MessageDigestTransformer(<span class="hljs-string">"SHA-256"</span>));
}
复制代码

进行单元测试:

    @Test
    public void testHash() {
    Bytes bytes = ByteArrayBytes.create(<span class="hljs-string">"hello world"</span>);

    assertEquals(<span class="hljs-string">"5eb63bbbe01eeed093cb22bb8f5acdc3"</span>, bytes.md5().toHexString());
    assertEquals(<span class="hljs-string">"2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"</span>, bytes.sha1().toHexString());
    assertEquals(<span class="hljs-string">"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"</span>, bytes.sha256().toHexString());
}
复制代码

2.4 序列化、反序列化、深拷贝

支持对象的序列化、反序列化以及深拷贝。在 Bytes 接口中,包含下面的静态函数:

    /**
     * 序列化对象,转换成字节数组
     * @param obj
     * @return
     */
    static byte[] serialize(Object obj) {
        byte[] result = null;
        ByteArrayOutputStream fos = null;
    <span class="hljs-keyword">try</span> {
        fos = <span class="hljs-keyword">new</span> ByteArrayOutputStream();
        ObjectOutputStream o = <span class="hljs-keyword">new</span> ObjectOutputStream(fos);
        o.writeObject(obj);
        result = fos.toByteArray();
    } <span class="hljs-keyword">catch</span> (IOException e) {
        System.err.println(e);
    } <span class="hljs-keyword">finally</span> {

        IOUtils.closeQuietly(fos);
    }

    <span class="hljs-keyword">return</span> result;
}

<span class="hljs-comment">/**
 * 反序列化字节数字,转换成对象
 * <span class="hljs-doctag">@param</span> bytes
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function"><span class="hljs-keyword">static</span> Object <span class="hljs-title">deserialize</span><span class="hljs-params">(<span class="hljs-keyword">byte</span>[] bytes)</span> </span>{
    InputStream fis = <span class="hljs-keyword">null</span>;

    <span class="hljs-keyword">try</span> {
        fis = <span class="hljs-keyword">new</span> ByteArrayInputStream(bytes);
        ObjectInputStream o = <span class="hljs-keyword">new</span> ObjectInputStream(fis);
        <span class="hljs-keyword">return</span> o.readObject();
    } <span class="hljs-keyword">catch</span> (IOException e) {
        System.err.println(e);
    } <span class="hljs-keyword">catch</span> (ClassNotFoundException e) {
        System.err.println(e);
    } <span class="hljs-keyword">finally</span> {

        IOUtils.closeQuietly(fis);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}

<span class="hljs-comment">/**
 * 通过序列化/反序列化实现对象的深拷贝
 * <span class="hljs-doctag">@param</span> obj
 * <span class="hljs-doctag">@param</span> &lt;T&gt;
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-keyword">static</span> &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">cloneObject</span><span class="hljs-params">(T obj)</span> </span>{
    
    <span class="hljs-keyword">return</span> (T) deserialize(serialize(obj));
}
复制代码

进行单元测试:

    @Test
    public void testSerializeAndDeserialize() {
    User u = <span class="hljs-keyword">new</span> User();
    u.name = <span class="hljs-string">"tony"</span>;
    u.password = <span class="hljs-string">"123456"</span>;

    <span class="hljs-keyword">byte</span>[] bytes = Bytes.serialize(u);

    User newUser = (User)Bytes.deserialize(bytes);
    assertEquals(u.name, newUser.name);
    assertEquals(u.password,newUser.password);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testDeepCopy</span><span class="hljs-params">()</span> </span>{

    User u = <span class="hljs-keyword">new</span> User();
    u.name = <span class="hljs-string">"tony"</span>;
    u.password = <span class="hljs-string">"123456"</span>;

    User newUser = Bytes.cloneObject(u);
    System.out.println(u);
    System.out.println(newUser);
    assertNotSame(u,newUser);
    assertNotSame(u.name,newUser.name);
}
复制代码

testDeepCopy() 执行后,u 和 newUser 地址的不同,u.namenewUser.name 指向的内存地址也不同。

com.safframework.bytekit.domain.User@2b05039f
com.safframework.bytekit.domain.User@17d10166
复制代码

2.5 copy、contact、reverse

copy、contact、reverse 都是采用 Transformer 的方式。在 AbstractBytes 类中,包含下面的函数:

    @Override
    public Bytes copy() {
    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> CopyTransformer(<span class="hljs-number">0</span>, size()));
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Bytes <span class="hljs-title">copy</span><span class="hljs-params">(<span class="hljs-keyword">int</span> offset, <span class="hljs-keyword">int</span> length)</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> CopyTransformer(offset, length));
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Bytes <span class="hljs-title">contact</span><span class="hljs-params">(<span class="hljs-keyword">byte</span>[] bytes)</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> ConcatTransformer(bytes));
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Bytes <span class="hljs-title">reverse</span><span class="hljs-params">()</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> ReverseTransformer());
}
复制代码

进行单元测试:

    @Test
    public void testContact() {
    Bytes bytes = ByteBufferBytes.create(<span class="hljs-string">"hello world"</span>).contact(<span class="hljs-string">" tony"</span>.getBytes());

    assertEquals(bytes.toString(), <span class="hljs-string">"hello world tony"</span>);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testCopy</span><span class="hljs-params">()</span> </span>{

    Bytes bytes = ByteBufferBytes.create(<span class="hljs-string">"hello world"</span>).contact(<span class="hljs-string">" tony"</span>.getBytes());

    assertEquals(bytes.toString(), bytes.copy().toString());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testReverse</span><span class="hljs-params">()</span> </span>{

    Bytes bytes = ByteBufferBytes.create(<span class="hljs-string">"hello world"</span>).contact(<span class="hljs-string">" tony"</span>.getBytes());

    assertEquals(bytes.toString(), bytes.reverse().reverse().toString());
}
复制代码

2.6 位操作

xor、and、or、not 也是采用 Transformer 的方式。在 AbstractBytes 类中,包含下面的函数:

    @Override
    public Bytes xor(byte[] bytes) {
    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> BitWiseOperatorTransformer(bytes,BitWiseOperatorTransformer.Mode.XOR));
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Bytes <span class="hljs-title">and</span><span class="hljs-params">(<span class="hljs-keyword">byte</span>[] bytes)</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> BitWiseOperatorTransformer(bytes, BitWiseOperatorTransformer.Mode.AND));
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Bytes <span class="hljs-title">or</span><span class="hljs-params">(<span class="hljs-keyword">byte</span>[] bytes)</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> BitWiseOperatorTransformer(bytes, BitWiseOperatorTransformer.Mode.OR));
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Bytes <span class="hljs-title">not</span><span class="hljs-params">(<span class="hljs-keyword">byte</span>[] bytes)</span> </span>{

    <span class="hljs-keyword">return</span> transform(<span class="hljs-keyword">new</span> BitWiseOperatorTransformer(bytes, BitWiseOperatorTransformer.Mode.NOT));
}
复制代码

进行单元测试:

    @Test
    public void testBitWise() {
    ByteBufferBytes bytes = (ByteBufferBytes)ByteBufferBytes.create(<span class="hljs-string">"hello world"</span>).contact(<span class="hljs-string">" tony"</span>.getBytes());

    assertEquals(bytes.toString(), bytes.and(bytes.toByteArray()).or(bytes.toByteArray()).toString());
    assertEquals(bytes.toString(), bytes.not(bytes.toByteArray()).not(bytes.toByteArray()).toString());
    assertEquals(bytes.toString(), bytes.xor(bytes.toByteArray()).xor(bytes.toByteArray()).toString()); <span class="hljs-comment">//两次xor 返回本身</span>
}
复制代码

2.7 Base64 编码、解码

    @Test
    public void testBase64() {
    ByteBufferBytes bytes = (ByteBufferBytes)ByteBufferBytes.create(<span class="hljs-string">"hello world"</span>).contact(<span class="hljs-string">" tony"</span>.getBytes());

    String base64 = <span class="hljs-keyword">new</span> String(bytes.encodeBase64());
    assertEquals(bytes.toString(), <span class="hljs-keyword">new</span> String(Bytes.parseBase64(base64)));
}
复制代码

2.8 Bytes 转换成字节数组

    @Test
    public void testToByteArray() {
    Bytes bytes = ByteBufferBytes.create(<span class="hljs-string">"hello world"</span>).contact(<span class="hljs-string">" tony"</span>.getBytes());

    assertEquals(bytes.toString(), <span class="hljs-keyword">new</span> String(bytes.toByteArray()));
}
复制代码

三. mmap 的操作

Linux 的 mmap 是一种内存映射文件的方法。

mmap 将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap 在用户空间映射调用系统中作用很大。 mmap 系统调用是将一个打开的文件映射到进程的用户空间,mmap 系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用 read()、write() 等操作。

import com.safframework.bytekit.domain.User;
import com.safframework.bytekit.jdk.mmap.MmapBytes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static junit.framework.TestCase.assertEquals;

/**
* Created by tony on 2018-12-24.
*/

public class MmapBytesTest {

<span class="hljs-keyword">private</span> MmapBytes mmapBytes;
<span class="hljs-keyword">private</span> String file;

<span class="hljs-meta">@Before</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setUp</span><span class="hljs-params">()</span> </span>{

    file = <span class="hljs-string">"test"</span>;
    mmapBytes = <span class="hljs-keyword">new</span> MmapBytes(file, (<span class="hljs-keyword">long</span>) <span class="hljs-number">1024</span> * <span class="hljs-number">10</span>); <span class="hljs-comment">// 10M</span>
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testWriteAndRead</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{

    mmapBytes.writeInt(<span class="hljs-number">12</span>);
    mmapBytes.writeInt(<span class="hljs-number">34</span>);
    mmapBytes.writeByte((<span class="hljs-keyword">byte</span>) <span class="hljs-number">5</span>);
    mmapBytes.writeBytes((<span class="hljs-string">"this is tony"</span>).getBytes());
    mmapBytes.writeLong(<span class="hljs-number">6666L</span>);
    mmapBytes.writeDouble(<span class="hljs-number">3.14</span>d);

    assertEquals(<span class="hljs-number">12</span>, mmapBytes.readInt());
    assertEquals(<span class="hljs-number">34</span>, mmapBytes.readInt());
    assertEquals((<span class="hljs-keyword">byte</span>) <span class="hljs-number">5</span>, mmapBytes.readByte());
    assertEquals(<span class="hljs-string">"this is tony"</span>, <span class="hljs-keyword">new</span> String(mmapBytes.readBytes(<span class="hljs-number">12</span>)));
    assertEquals(<span class="hljs-number">6666L</span>, mmapBytes.readLong());
    assertEquals(<span class="hljs-number">3.14</span>d, mmapBytes.readDouble());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testObject</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{

    User u = <span class="hljs-keyword">new</span> User();
    u.name = <span class="hljs-string">"tony"</span>;
    u.password = <span class="hljs-string">"123456"</span>;

    mmapBytes.writeObject(u);

    User temp = (User)mmapBytes.readObject(<span class="hljs-number">117</span>);

    assertEquals(u.name, temp.name);
    assertEquals(u.password, temp.password);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testFree</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{

    mmapBytes.writeInt(<span class="hljs-number">12</span>);
    mmapBytes.writeInt(<span class="hljs-number">34</span>);
    mmapBytes.writeByte((<span class="hljs-keyword">byte</span>) <span class="hljs-number">5</span>);

    mmapBytes.free();

    mmapBytes = <span class="hljs-keyword">new</span> MmapBytes(file, (<span class="hljs-keyword">long</span>) <span class="hljs-number">1024</span> * <span class="hljs-number">10</span>); <span class="hljs-comment">// 10M</span>
    mmapBytes.writeInt(<span class="hljs-number">67</span>);

    assertEquals(<span class="hljs-number">67</span>, mmapBytes.readInt());
}

<span class="hljs-meta">@After</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">tearDown</span><span class="hljs-params">()</span> </span>{
    mmapBytes.free();
}

}

复制代码

四. 总结

bytekit 是一个操作字节的工具库,不依赖任何第三方库。它封装了字节数组、ByteBuffer 的操作,支持 mmap 常用的读写。

当然,它还可以封装 protobuf 的 ByteString 或者 Android 中的 Parcel,只需实现 Bytes 接口即可。

参考资料:

  1. 你真的知道 Java 中 boolean 类型占用多少个字节吗?

Java 与 Android 技术栈:每周更新推送原创技术文章,欢迎扫描下方的公众号二维码并关注,期待与您的共同成长和进步。

  • Java

    Java,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台的总称。用 Java 实现的 HotJava 浏览器(支持 Java applet)显示了 Java 的魅力:跨平台、动态的…

    380 引用 • 6 回帖
感谢    赞同    分享    收藏    关注    反对    举报    ...