Java 字节的常用封装

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 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 接口即可。

相关文章
|
4月前
|
安全 Java 编译器
Java的封装详解
封装和多态是面向对象编程(OOP)的重要概念。封装通过私有属性和公共方法实现数据隐藏和保护,使类的内部细节对外部不可见;多态则通过方法重载和重写实现同一方法在不同对象上的不同表现形式,增强了代码的灵活性和可维护性。两者结合使用,可以使Java程序更加安全、灵活且易于维护。
267 82
|
4月前
|
Java
Java的封装详解
封装是Java中实现数据隐藏和保护的核心机制。它通过将对象的状态和行为结合并限制外部直接访问,确保类的内部细节对外不可见,仅能通过公共方法访问和修改对象状态。封装带来了数据隐藏、提高代码可维护性和增强安全性等好处。在Java中,封装主要通过将属性设为私有并提供getter和setter方法来实现。这种方式不仅保护了数据完整性,还允许在修改类内部实现时不影响外部代码,从而提升程序的健壮性和可读性。
321 80
|
4月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
4月前
|
SQL Java 编译器
Java——类与对象(封装)
封装是面向对象编程中的概念,指将数据(属性)和相关操作(方法)组合成独立单元(类),使外部无法直接访问对象的内部状态,只能通过提供的方法进行交互,从而保护数据安全。例如,手机将各种组件封装起来,只暴露必要的接口供外部使用。实现封装时,使用`private`关键字修饰成员变量,并提供`get`和`set`方法进行访问和修改。此外,介绍了包的概念、导入包的方式及其注意事项,以及`static`关键字的使用,包括静态变量和方法的初始化与代码块的加载顺序。
68 10
Java——类与对象(封装)
|
4月前
|
安全 Java 数据安全/隐私保护
Java 封装怎么理解
封装是Java中的一种重要机制,它将对象的状态(数据)和行为(方法)打包在一起并控制外部访问权限,以保护数据不被随意修改。封装的主要目的包括数据保护、接口设计和增强模块性。通过使用`private`、`protected`及`public`等访问控制修饰符,结合getter和setter方法,可以有效隐藏对象内部实现细节。下面是一个简单的`BankAccount`类示例,展示了如何通过封装保护类的内部状态,确保数据安全和一致性,简化类的使用。理解封装有助于编写高质量代码和设计优秀程序架构。
55 9
|
4月前
|
Java 数据安全/隐私保护
Java 封装详解
在 Java 中,封装是面向对象编程的关键特性,通过将对象的状态(数据)和行为(方法)结合并利用访问控制保护数据,防止外部随意访问和修改。主要特点包括访问控制(如 `private` 和 `protected`)、数据隐藏及方法暴露(如 getter 和 setter)。封装的优点在于保护数据、隐藏实现细节、易于维护以及提高代码可读性。下面是一个简单的 `Person` 类封装示例,展示了如何通过 getter 和 setter 控制对类内部状态的访问,并进行合法性检查。总结而言,封装有助于构建清晰、易用且可维护的代码结构,是编写高质量 Java 程序的重要原则。
87 7
|
4月前
|
安全 Java 开发者
Java修饰符与封装:理解访问权限、行为控制与数据隐藏的重要性
Java中的修饰符和封装概念是构建健壯、易维护和扩展的Java应用程序的基石。通过合理利用访问权限修饰符和非访问修饰符,开发者能够设计出更加安全、灵活且高效的代码结构。封装不仅是面向对象编程的核心原则之一,也是提高软件项目质量和可维护性的关键策略。
46 1
|
5月前
|
安全 Java
Java基础面试十四】、 封装的目的是什么,为什么要有封装?
这篇文章讨论了封装在面向对象编程中的目的,强调封装可以隐藏类的实现细节,通过方法控制对数据的访问,保证数据完整性,并提高代码的可维护性。
Java基础面试十四】、 封装的目的是什么,为什么要有封装?
|
5月前
|
缓存 前端开发 Java
【前端学java】java基础巩固复习巩固语法练习-工具类的封装(14)
【8月更文挑战第10天】java基础巩固,工具类的封装
34 1
|
5月前
|
存储 Java 数据库