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

作者:jingxian 时间:2023-05-24 07:06:52 

1. 背景介绍

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

  • 当索引越界时, 会自动抛出ArrayIndexOutOfBoundsException, 避免一错再错

  • 另一个很重要的方法是获取数组长度

这两个功能都不是通过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的封装,因而能获得更丰富的信息,做更多语言层面的检查。

java 数组越界问题

Java中数组初始化和OC其实是一样的,分为动态初始化和静态初始化,

  • 动态初始化:指定长度,由系统给出初始化值

  • 静态初始化:给出初始化值,由系统给出长度

在我们使用数组时最容易出现的就是数组越界问题,好了,下面来演示一下


int [][] array = {{1,2,3},{1,4}};
       System.out.println(array[1][2]);

这是一个二维数组,很明显,数组越界了,控制台中会打印如下信息:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2

at demo.Array.main(Array.java:31)

很准确的定位到31行

来源:https://blog.csdn.net/weixin_45244678/article/details/109034133

标签:java,数组越界,数组长度
0
投稿

猜你喜欢

  • Android实现图片文字轮播特效

    2021-07-25 18:44:11
  • C# 装箱和拆箱的知识回顾

    2022-07-19 09:27:58
  • Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)

    2022-07-31 14:39:23
  • Android Studio设置主题与字体大小图文教程

    2023-05-25 15:54:10
  • Android Handler runWithScissors 梳理流程解析

    2023-01-29 11:51:27
  • SpringBoot2.6.x升级后循环依赖及Swagger无法使用问题

    2021-12-02 09:49:43
  • springboot2.0如何通过fastdfs实现文件分布式上传

    2022-03-20 16:49:24
  • Java开发必备的三大修饰符

    2021-10-19 10:11:01
  • SpringBoot集成支付宝沙箱支付(支付、退款)

    2022-02-15 16:50:52
  • Android实现EventBus登录界面与传值(粘性事件)

    2023-11-08 00:34:52
  • SpringBoot 使用 FTP 操作文件的过程(删除、上传、下载文件)

    2021-07-26 10:40:05
  • Android本地存储SharedPreferences详解

    2023-06-24 17:01:51
  • Android View 事件防抖的两种方案

    2022-02-04 15:46:52
  • Android模拟实现支付宝蚂蚁森林效果

    2023-03-15 05:07:38
  • SpringBoot使用token简单鉴权的具体实现方法

    2022-07-10 14:23:42
  • Java多线程事务管理的实现

    2023-07-26 22:58:20
  • java基础--自己动手实现一个LRU

    2023-06-25 18:21:04
  • 简单工厂模式_动力节点Java学院整理

    2022-07-22 16:42:08
  • C#实现动态执行字符串脚本(优化版)的示例代码

    2022-06-10 19:13:54
  • mybatis中resultMap 标签的使用教程

    2022-01-15 11:19:42
  • asp之家 软件编程 m.aspxhome.com