java中实现数组越界判断和获取数组长度的实现

1. 背景介绍

java中的数组比c语言中的数组, 多了两个很重要的功能

  1. 当索引越界时, 会自动抛出ArrayIndexOutOfBoundsException, 避免一错再错
  2. 另一个很重要的方法是获取数组长度

这两个功能都不是通过java代码层面实现的, 而是在jvm中通过c++来实现的. 本文就针对这连个点来一探究竟

2. 原始java代码

public class TestArrayIndexOutOfBoundsException {
    public static void main(String[] args) {
        int[] is = new int[2];
        int x = is[5];
        System.out.println(x);
        int len = is.length;
        System.out.println(len);
    }
}

3. java代码对应的反编译字节码

 0 iconst_2
 1 newarray 10 (int) // 创建长度为2的int型数组
 3 astore_1 // 将数组is存储到local stack的slot1中
 4 aload_1 // 将数组is压入操作数栈
 5 iconst_5 // 将常量5压入操作数栈
 6 iaload // 这个指令就是通过数组索引获取元素, is[5]
 7 istore_2
 8 getstatic #2 <java/lang/System.out>
11 iload_2
12 invokevirtual #3 <java/io/PrintStream.println>
15 aload_1 // 将数组is压入操作数栈
16 arraylength // 获取is数组的长度
17 istore_3
18 getstatic #2 <java/lang/System.out>
21 iload_3
22 invokevirtual #3 <java/io/PrintStream.println>
25 return

4. jvm实现分析

4.1 获取数组长度arraylength指令核心代码分析

// hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
void
BytecodeInterpreter::run(interpreterState istate) {
// 省略无关代码
	CASE(_arraylength):
	{
		// java中的对象实例, 对应的c++实例就是arrayOopDesc
	    arrayOop ary = (arrayOop) STACK_OBJECT(-1);
	    CHECK_NULL(ary);
	    SET_STACK_INT(ary->length(), -1); // 就是通过ary->length()这个方法来获取数组长度
	    UPDATE_PC_AND_CONTINUE(1);
	}
}
// 省略无关代码

4.2 获取数组元素iaload指令分析

// hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
void
BytecodeInterpreter::run(interpreterState istate) {
// 省略无关代码
	#define ARRAY_INTRO(arrayOff)                                                  \
	      arrayOop arrObj = (arrayOop)STACK_OBJECT(arrayOff);                      \ // 从局部变量表中获取数组对象is
	      jint     index  = STACK_INT(arrayOff + 1);                               \ // 从局部变量表中获取索引5
	      char message[jintAsStringSize];                                          \
	      CHECK_NULL(arrObj);                                                      \
	      if ((uint32_t)index >= (uint32_t)arrObj->length()) {                     \ // 判断索引是否大于或等于数组长度
	          sprintf(message, "%d", index);                                       \
	          VM_JAVA_ERROR(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), \ // 如果越界, 则抛出ArrayIndexOutOfBoundsException异常
	                        message);                                              \
	      }
// 省略无关代码
	#define ARRAY_LOADTO32(T, T2, format, stackRes, extra)                                \
	      {                                                                               \
	          ARRAY_INTRO(-2);                                                            \ // 获取数组所在的地址
	          (void)extra;                                                                \
	          SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), \ // 根据数组所在地址,加上索引计算的偏移地址, 获得数组指定元素
	                           -2);                                                       \
	          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);                                      \
	      }
// 省略无关代码
	CASE(_iaload): // 此处对应的就是iaload指令的具体实现
          ARRAY_LOADTO32(T_INT, jint,   "%d",   STACK_INT, 0);
}
// 省略无关代码

通过上面代码的分析, 可以jvm是通过(uint32_t)index >= (uint32_t)arrObj->length()来判断数组越界

5. 总结

java中的数组和c语言数组差异很大, c语言中的数组更原始, 直接和内存对应. 而java中的数组类型其实是经过了arrayOopDesc的封装, 因而能获得更丰富的信息, 做更多语言层面的检查.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值