Java 字节的常用封装

简介: Java 字节的常用封装

一. 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 地址:https://github.com/fengzhizi715/bytekit


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
  • 支持对象的序列化、反序列化、深拷贝
  • 不依赖任何第三方库


image.png

bytes.png


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


2.2 支持 Immutable 对象


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


它的优点:


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


2.3 支持 Hash 加密


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

/**
     * 使用md5加密
     * @return
     */
    default Bytes md5() {
        return transform(new MessageDigestTransformer("MD5"));
    }
    /**
     * 使用sha1加密
     * @return
     */
    default Bytes sha1() {
        return transform(new MessageDigestTransformer("SHA-1"));
    }
    /**
     * 使用sha256加密
     * @return
     */
    default Bytes sha256() {
        return transform(new MessageDigestTransformer("SHA-256"));
    }


进行单元测试:

@Test
    public void testHash() {
        Bytes bytes = ByteArrayBytes.create("hello world");
        assertEquals("5eb63bbbe01eeed093cb22bb8f5acdc3", bytes.md5().toHexString());
        assertEquals("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", bytes.sha1().toHexString());
        assertEquals("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", bytes.sha256().toHexString());
    }


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


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

/**
     * 序列化对象,转换成字节数组
     * @param obj
     * @return
     */
    static byte[] serialize(Object obj) {
        byte[] result = null;
        ByteArrayOutputStream fos = null;
        try {
            fos = new ByteArrayOutputStream();
            ObjectOutputStream o = new ObjectOutputStream(fos);
            o.writeObject(obj);
            result = fos.toByteArray();
        } catch (IOException e) {
            System.err.println(e);
        } finally {
            IOUtils.closeQuietly(fos);
        }
        return result;
    }
    /**
     * 反序列化字节数字,转换成对象
     * @param bytes
     * @return
     */
    static Object deserialize(byte[] bytes) {
        InputStream fis = null;
        try {
            fis = new ByteArrayInputStream(bytes);
            ObjectInputStream o = new ObjectInputStream(fis);
            return o.readObject();
        } catch (IOException e) {
            System.err.println(e);
        } catch (ClassNotFoundException e) {
            System.err.println(e);
        } finally {
            IOUtils.closeQuietly(fis);
        }
        return null;
    }
    /**
     * 通过序列化/反序列化实现对象的深拷贝
     * @param obj
     * @param <T>
     * @return
     */
    static <T> T cloneObject(T obj) {
        return (T) deserialize(serialize(obj));
    }


进行单元测试:

@Test
    public void testSerializeAndDeserialize() {
        User u = new User();
        u.name = "tony";
        u.password = "123456";
        byte[] bytes = Bytes.serialize(u);
        User newUser = (User)Bytes.deserialize(bytes);
        assertEquals(u.name, newUser.name);
        assertEquals(u.password,newUser.password);
    }
    @Test
    public void testDeepCopy() {
        User u = new User();
        u.name = "tony";
        u.password = "123456";
        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.name 和 newUser.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() {
        return transform(new CopyTransformer(0, size()));
    }
    @Override
    public Bytes copy(int offset, int length) {
        return transform(new CopyTransformer(offset, length));
    }
    @Override
    public Bytes contact(byte[] bytes) {
        return transform(new ConcatTransformer(bytes));
    }
    @Override
    public Bytes reverse() {
        return transform(new ReverseTransformer());
    }


进行单元测试:

@Test
    public void testContact() {
        Bytes bytes = ByteBufferBytes.create("hello world").contact(" tony".getBytes());
        assertEquals(bytes.toString(), "hello world tony");
    }
    @Test
    public void testCopy() {
        Bytes bytes = ByteBufferBytes.create("hello world").contact(" tony".getBytes());
        assertEquals(bytes.toString(), bytes.copy().toString());
    }
    @Test
    public void testReverse() {
        Bytes bytes = ByteBufferBytes.create("hello world").contact(" tony".getBytes());
        assertEquals(bytes.toString(), bytes.reverse().reverse().toString());
    }


2.6 位操作


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

@Override
    public Bytes xor(byte[] bytes) {
        return transform(new BitWiseOperatorTransformer(bytes,BitWiseOperatorTransformer.Mode.XOR));
    }
    @Override
    public Bytes and(byte[] bytes) {
        return transform(new BitWiseOperatorTransformer(bytes, BitWiseOperatorTransformer.Mode.AND));
    }
    @Override
    public Bytes or(byte[] bytes) {
        return transform(new BitWiseOperatorTransformer(bytes, BitWiseOperatorTransformer.Mode.OR));
    }
    @Override
    public Bytes not(byte[] bytes) {
        return transform(new BitWiseOperatorTransformer(bytes, BitWiseOperatorTransformer.Mode.NOT));
    }


进行单元测试:

@Test
    public void testBitWise() {
        ByteBufferBytes bytes = (ByteBufferBytes)ByteBufferBytes.create("hello world").contact(" tony".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()); //两次xor 返回本身
    }


2.7 Base64 编码、解码

@Test
    public void testBase64() {
        ByteBufferBytes bytes = (ByteBufferBytes)ByteBufferBytes.create("hello world").contact(" tony".getBytes());
        String base64 = new String(bytes.encodeBase64());
        assertEquals(bytes.toString(), new String(Bytes.parseBase64(base64)));
    }


2.8 Bytes 转换成字节数组

@Test
    public void testToByteArray() {
        Bytes bytes = ByteBufferBytes.create("hello world").contact(" tony".getBytes());
        assertEquals(bytes.toString(), new 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 {
    private MmapBytes mmapBytes;
    private String file;
    @Before
    public void setUp() {
        file = "test";
        mmapBytes = new MmapBytes(file, (long) 1024 * 10); // 10M
    }
    @Test
    public void testWriteAndRead() throws Exception {
        mmapBytes.writeInt(12);
        mmapBytes.writeInt(34);
        mmapBytes.writeByte((byte) 5);
        mmapBytes.writeBytes(("this is tony").getBytes());
        mmapBytes.writeLong(6666L);
        mmapBytes.writeDouble(3.14d);
        assertEquals(12, mmapBytes.readInt());
        assertEquals(34, mmapBytes.readInt());
        assertEquals((byte) 5, mmapBytes.readByte());
        assertEquals("this is tony", new String(mmapBytes.readBytes(12)));
        assertEquals(6666L, mmapBytes.readLong());
        assertEquals(3.14d, mmapBytes.readDouble());
    }
    @Test
    public void testObject() throws Exception {
        User u = new User();
        u.name = "tony";
        u.password = "123456";
        mmapBytes.writeObject(u);
        User temp = (User)mmapBytes.readObject(117);
        assertEquals(u.name, temp.name);
        assertEquals(u.password, temp.password);
    }
    @Test
    public void testFree() throws Exception {
        mmapBytes.writeInt(12);
        mmapBytes.writeInt(34);
        mmapBytes.writeByte((byte) 5);
        mmapBytes.free();
        mmapBytes = new MmapBytes(file, (long) 1024 * 10); // 10M
        mmapBytes.writeInt(67);
        assertEquals(67, mmapBytes.readInt());
    }
    @After
    public void tearDown() {
        mmapBytes.free();
    }
}


四. 总结



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


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

相关文章
|
6月前
|
Java 数据库连接 数据库
Java 组件详细使用方法与封装实战指南
本指南详解Java核心组件使用与封装技巧,涵盖跨平台开发、面向对象编程、多线程、数据库操作等关键内容,并提供工具类、连接池、异常及响应结果的封装方法。结合Spring框架、MyBatis、Spring Boot等主流技术,助你掌握高质量Java组件设计与开发实践。
215 2
|
10月前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
10月前
|
安全 网络协议 Java
Java网络编程封装
Java网络编程封装原理旨在隐藏底层通信细节,提供简洁、安全的高层接口。通过简化开发、提高安全性和增强可维护性,封装使开发者能更高效地进行网络应用开发。常见的封装层次包括套接字层(如Socket和ServerSocket类),以及更高层次的HTTP请求封装(如RestTemplate)。示例代码展示了如何使用RestTemplate简化HTTP请求的发送与处理,确保代码清晰易维护。
|
9月前
|
人工智能 JSON Java
列表结构与树结构转换分析与工具类封装(java版)
本文介绍了将线性列表转换为树形结构的实现方法及工具类封装。核心思路是先获取所有根节点,将其余节点作为子节点,通过递归构建每个根节点的子节点。关键在于节点需包含 `id`、`parentId` 和 `children` 三个属性。文中提供了两种封装方式:一是基于基类 `BaseTree` 的通用工具类,二是使用函数式接口实现更灵活的方式。推荐使用后者,因其避免了继承限制,更具扩展性。代码示例中使用了 Jackson 库进行 JSON 格式化输出,便于结果展示。最后总结指出,理解原理是进一步优化和封装的基础。
301 0
|
11月前
|
Java
Java 面向对象编程的三大法宝:封装、继承与多态
本文介绍了Java面向对象编程中的三大核心概念:封装、继承和多态。
577 15
|
安全 Java 编译器
Java的封装详解
封装和多态是面向对象编程(OOP)的重要概念。封装通过私有属性和公共方法实现数据隐藏和保护,使类的内部细节对外部不可见;多态则通过方法重载和重写实现同一方法在不同对象上的不同表现形式,增强了代码的灵活性和可维护性。两者结合使用,可以使Java程序更加安全、灵活且易于维护。
366 82
|
Java
Java的封装详解
封装是Java中实现数据隐藏和保护的核心机制。它通过将对象的状态和行为结合并限制外部直接访问,确保类的内部细节对外不可见,仅能通过公共方法访问和修改对象状态。封装带来了数据隐藏、提高代码可维护性和增强安全性等好处。在Java中,封装主要通过将属性设为私有并提供getter和setter方法来实现。这种方式不仅保护了数据完整性,还允许在修改类内部实现时不影响外部代码,从而提升程序的健壮性和可读性。
634 80
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】