我们平时如何实现浅克隆?
- 实现Closeable接口
- 重写close()方法
一、Unsafe实现浅克隆
浅克隆工具类
package com.liziba.unsafe.clone;
import com.liziba.unsafe.UnsafeFactory;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* <p>
* 浅克隆工具类
* </p>
*
* @Author: Liziba
* @Date: 2021/5/26 21:08
*/
public class ShallowCloneUtil {
/**
* 获取对象的内存地址
*
* @Description
* Unsafe类没有提供直接获取实例对象内存地址的方法,但是可以通过以下方式间接获取。
* 构建对象A,A包含了我们需要获取内存地址的B对象的引用,这样只有获取到A对象持有的B对象的引用地址,就可以知道B对象的地址了。
* 我们可以通过Unsafe类获取内存地址的方法public native long getLong(Object var1, long var2)来获取;
* 此处我们为了方便,通过数组Object[] 添加Object元素,持有Object的引用
*
* @return
*/
public static Long getAddress(Object obj) {
Object[] objects = new Object[] {obj};
Unsafe unsafe = UnsafeFactory.getUnsafe();
int arrayBaseOffset = unsafe.arrayBaseOffset(Object[].class);
return unsafe.getLong(objects, arrayBaseOffset);
}
/**
* 获取对象的大小
*
* @Dscription
* Java中实例化一个对象时,JVM会在堆中分配非static的Field的内存,其他的static属性或者method在类加载期间或者JVM启动时已经存放在内存中。
* 所以我们计算对象的大小的时候只需要求和Field的大小就行了,JVM分配内存时,单个实例对象中的Field内存是连续不断地,
* 因此我们只需获取最大偏移量Filed的偏移量 + 最大偏移量Filed本身的大小即可
*
* Java中基本数据类型所占的字节数
* byte/boolean 1 字节
* char/short 2 字节
* int/float 4 字节
* long/double 8 字节
* boolean 理论上占1/8字节,实际上按照1byte处理。
* Java采用的是Unicode编码,每一个字节占8位,一个字节由8个二进制位组成。
*
* @param clazz
* @return
*/
public static Long size(Class clazz) {
// 最后一个Filed的内存偏移量
long maxOffset = 0;
Class lastFiledClass = null;
Unsafe unsafe = UnsafeFactory.getUnsafe();
do {
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
long tmpOffset = unsafe.objectFieldOffset(field);
if (tmpOffset > maxOffset) {
maxOffset = tmpOffset;
lastFiledClass = field.getType();
}
}
}
} while ((clazz = clazz.getSuperclass()) != null);
// 最后一个Field本身的大小
int lastFiledSize = (boolean.class.equals(lastFiledClass) || byte.class.equals(lastFiledClass)) ? 1 :
(short.class.equals(lastFiledClass) || char.class.equals(lastFiledClass)) ? 2 :
(int.class.equals(lastFiledClass) || float.class.equals(lastFiledClass)) ? 4 : 8 ;
return maxOffset + lastFiledSize;
}
/**
* 申请一块固定大小的内存空间
*
* @Description
* 通过Unsafe的public native long allocateMemory(long var1);申请一块内存空间。
*
* @param bytes 需要申请的内存大小
* @return
*/
public static Long allocateMemory(long bytes) {
return UnsafeFactory.getUnsafe().allocateMemory(bytes);
}
/**
* 从原对象内存地址srcAddr复制大小位size的内存到destAddr地址处
*
* @param srcAddr 源地址
* @param destAddr 目标地址
* @param size 复制内存大小
*/
public static void copyMemory(long srcAddr, long destAddr, long size) {
UnsafeFactory.getUnsafe().copyMemory(srcAddr, destAddr, size);
}
/**
* Unsafe未提供直接读取内存转为Java对象的方法,但是可以通过新建一个包含T类型属性的对象将申请的内存地址赋值给T属性
*
* @param addr
* @param <T>
* @return
*/
public static <T> T addressConvertObject(long addr) {
Object[] objects = new Object[] {null};
Unsafe unsafe = UnsafeFactory.getUnsafe();
int baseOffset = unsafe.arrayBaseOffset(Object.class);
unsafe.putLong(objects, baseOffset, addr);
return (T)objects[0];
}
/**
* 实现对象的浅克隆
*
* @Description
* 数组的无法通过此方法实现克隆,数组类是在JVM运行时动态生成的
*
* @param t
* @param <T>
* @return
*/
public static <T> T shallowClone(T t) {
Class<?> clazz = t.getClass();
if (clazz.isArray()) {
Object[] objects = (Object[]) t;
return (T) Arrays.copyOf(objects, objects.length);
}
Long srcAddr = getAddress(t);
Long size = size(clazz);
Long destAddr = allocateMemory(size);
copyMemory(srcAddr, destAddr, size);
return addressConvertObject(destAddr);
}
}
测试
/**
* <p>
* 浅克隆测试
* </p>
*
* @Author: Liziba
* @Date: 2021/5/24 23:11
*/
public class ShallowCloneTest {
public static void main(String[] args){
SimpleInfo simpleInfo = new SimpleInfo();
simpleInfo.setId(1);
System.out.println(simpleInfo.hashCode());
SimpleInfo clone = ShallowCloneUtil.shallowClone(simpleInfo);
System.out.println(clone.hashCode());
}
}