平时在用Java编码过程中,总是会用到数据类型,那么到底怎么理解数据类型呢?以下为个人愚见:
我们的数据都是要保存在内存中的,比如0x99,0x100这样的,但是人类更善于记住有意义的东西而不是数字,于是我们就给这块内存区域起个有意义的名字->变量名来帮助我们操作这块内存地址。
但是我们编程中使用的数据占据的内存区域可能有大有小,如果有一个占据了四个字节的数据保存于0x99-0x102这四个字节的连续内存区域上,这时就需要一个数据类型来决定从内存中取出这个数据时要取几个字节了,比如我们要读取一个int num,那么我们就要从这个变量所在内存的起始地址0x99开始,到0x102的结尾结束(偏移量),而不是到0x100或者0x108结束。
由此展开一些的话,比如我们编程常用,并且是很多容器类如ArrayList、HashMap等的基础数据结构的数组,由于数组是存放于连续内存空间,并且数组元素的数据类型是相同的,所以就有了随机访问的特性。如果数据类型不同,那么即使数组是存放于连续内存空间的,我们也没办法根据数组的起始地址和偏移量来快速访问到指定下标的元素,也就不可能有随机访问这个特性了,那些基于数组随机访问特性来实现O(1)查找速度的一些容器如ArrayList、HashMap(可能因哈希冲突转成链表和红黑树,暂不考虑)也就无从实现。
加点更直观的来帮助记忆,如定义一个数组int[] nums = new int[10];假设数组起始地址是0x1000,那么数组中元素的访问定位可以表示为:(TypeSize即为数据类型大小)
如果数组中数据类型(TypeSize)不一致,那么也就无法使用这个公式快速定位数组中元素了。
写了一个小例子来验证下,主要使用到了Unsafe魔法类提供的API,详细信息请看代码注释和控制台输出:
package com.ebao.concurrent;
import java.lang.reflect.Field;
import org.junit.Test;
import sun.misc.Unsafe;
/**
* @title: TestRandomAccess
* @projectName demo1
* @description: 使用Unsafe类查看数组元素大小,并通过数组起始地址+数组元素大小获取数组元素
*/
public class TestRandomAccess {
private static Unsafe unsafe;
String[] names = {"paul", "wade", "james", "yao"};
static {
try {
/**
* Unsafe类限制只能启动类加载器才能使用,比如rt.jar中的类
* 有两种方式可以获取:1是把当前类加到启动类的classpath下。2是使用反射
* 本例中使用第二种方式
*/
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
@Test
public void test() {
//Unsafe操作数组
int baseOffset = unsafe.arrayBaseOffset(String[].class);
System.out.println("数组内存起始地址" + baseOffset);
int arrayIndexScale = unsafe.arrayIndexScale(String[].class);
System.out.println("数组每个元素数值大小范围" + arrayIndexScale);
//根据数组起始地址+3个数组元素大小偏移量获取数组第四个元素
System.out.println(unsafe.getObject(names, baseOffset + 3 * arrayIndexScale));
}
}
跑下测试方法,控制台输出为: